Compare commits

..

No commits in common. "ab73916795d509cdc3f8b7de1f4c3fac8b33ec59" and "302691f557c5a3a3ce82bdc7c3074d48568c1d50" have entirely different histories.

13 changed files with 62 additions and 365 deletions

View File

@ -30,7 +30,6 @@
"create": "Create",
"preview": "Preview",
"loading": "Loading...",
"delete": "Delete",
"fieldUsername": "Username",
"fieldNickname": "Nickname",
"fieldEmail": "Email address",

View File

@ -30,7 +30,6 @@
"apply": "应用",
"create": "创建",
"preview": "预览",
"delete": "删除",
"fieldUsername": "用户名",
"fieldNickname": "显示名",
"fieldEmail": "电子邮箱地址",

View File

@ -84,12 +84,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
distance: 75,
type: ExpandableFabType.up,
childrenAnimation: ExpandableFabAnimation.none,
overlayStyle: ExpandableFabOverlayStyle(
color: Theme.of(context)
.colorScheme
.surface
.withAlpha((255 * 0.5).round()),
),
overlayStyle: ExpandableFabOverlayStyle(blur: 10),
openButtonBuilder: RotateFloatingActionButtonBuilder(
child: const Icon(Symbols.add, size: 28),
fabSize: ExpandableFabSize.regular,

View File

@ -4,19 +4,15 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/types/post.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/post/post_media_pending_list.dart';
import 'package:surface/widgets/post/post_meta_editor.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:provider/provider.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/post/post_meta_editor.dart';
class PostEditorScreen extends StatefulWidget {
final String mode;
@ -37,9 +33,6 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
SnPublisher? _publisher;
List<SnPublisher>? _publishers;
final List<XFile> _selectedMedia = List.empty(growable: true);
final List<SnAttachment> _attachments = List.empty(growable: true);
void _fetchPublishers() async {
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/co/publishers');
@ -56,82 +49,22 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
final TextEditingController _contentController = TextEditingController();
double? _progress;
static const kAttachmentProgressWeight = 0.9;
static const kPostingProgressWeight = 0.1;
void _performAction() async {
if (_isBusy || _publisher == null) return;
final sn = context.read<SnNetworkProvider>();
final attach = context.read<SnAttachmentProvider>();
setState(() {
_progress = 0;
_isBusy = true;
});
setState(() => _isBusy = true);
// Uploading attachments
try {
for (int i = 0; i < _selectedMedia.length; i++) {
final media = _selectedMedia[i];
final place = await attach.chunkedUploadInitialize(
await media.length(),
media.name,
'interactive',
null,
);
final item = await attach.chunkedUploadParts(
media,
place.$1,
place.$2,
onProgress: (progress) {
// Calculate overall progress for attachments
setState(() {
_progress = ((i + progress) / _selectedMedia.length) *
kAttachmentProgressWeight;
});
},
);
_attachments.add(item);
}
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
setState(() => _isBusy = false);
return;
}
setState(() => _progress = kAttachmentProgressWeight);
// Posting the content
try {
final baseProgressVal = _progress!;
await sn.client.post('/cgi/co/${widget.mode}', data: {
'publisher': _publisher!.id,
'content': _contentController.value.text,
'title': _title,
'description': _description,
'attachments': _attachments.map((e) => e.rid).toList(),
}, onSendProgress: (count, total) {
setState(() {
_progress =
baseProgressVal + (count / total) * (kPostingProgressWeight / 2);
});
}, onReceiveProgress: (count, total) {
setState(() {
_progress = baseProgressVal +
(kPostingProgressWeight / 2) +
(count / total) * (kPostingProgressWeight / 2);
});
});
if (!mounted) return;
Navigator.pop(context, true);
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
} finally {
setState(() => _isBusy = false);
@ -155,15 +88,6 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
});
}
final _imagePicker = ImagePicker();
void _selectMedia() async {
final result = await _imagePicker.pickMultipleMedia();
if (result.isEmpty) return;
_selectedMedia.addAll(result);
setState(() {});
}
@override
void dispose() {
_contentController.dispose();
@ -324,29 +248,15 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
),
),
),
if (_selectedMedia.isNotEmpty)
PostMediaPendingList(
data: _selectedMedia,
onRemove: (idx) {
setState(() {
_selectedMedia.removeAt(idx);
});
},
).padding(bottom: 8),
Material(
elevation: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_isBusy && _progress != null)
TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: 1),
duration: Duration(milliseconds: 300),
builder: (context, value, _) =>
LinearProgressIndicator(value: value, minHeight: 2),
)
else if (_isBusy)
const LinearProgressIndicator(value: null, minHeight: 2),
if (_isBusy)
const LinearProgressIndicator(
minHeight: 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -358,7 +268,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
child: Row(
children: [
IconButton(
onPressed: _isBusy ? null : _selectMedia,
onPressed: () {},
icon: Icon(
Symbols.add_photo_alternate,
color: Theme.of(context).colorScheme.primary,

View File

@ -21,6 +21,7 @@ class SnAttachment with _$SnAttachment {
required int refCount,
required dynamic fileChunks,
required dynamic cleanedAt,
required Map<String, dynamic> metadata,
required bool isMature,
required bool isAnalyzed,
required bool isUploaded,
@ -30,7 +31,6 @@ class SnAttachment with _$SnAttachment {
required SnAttachmentPool? pool,
required int poolId,
required int accountId,
@Default({}) Map<String, dynamic> metadata,
}) = _SnAttachment;
factory SnAttachment.fromJson(Map<String, Object?> json) =>

View File

@ -35,6 +35,7 @@ mixin _$SnAttachment {
int get refCount => throw _privateConstructorUsedError;
dynamic get fileChunks => throw _privateConstructorUsedError;
dynamic get cleanedAt => throw _privateConstructorUsedError;
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
bool get isMature => throw _privateConstructorUsedError;
bool get isAnalyzed => throw _privateConstructorUsedError;
bool get isUploaded => throw _privateConstructorUsedError;
@ -44,7 +45,6 @@ mixin _$SnAttachment {
SnAttachmentPool? get pool => throw _privateConstructorUsedError;
int get poolId => throw _privateConstructorUsedError;
int get accountId => throw _privateConstructorUsedError;
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
/// Serializes this SnAttachment to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -78,6 +78,7 @@ abstract class $SnAttachmentCopyWith<$Res> {
int refCount,
dynamic fileChunks,
dynamic cleanedAt,
Map<String, dynamic> metadata,
bool isMature,
bool isAnalyzed,
bool isUploaded,
@ -86,8 +87,7 @@ abstract class $SnAttachmentCopyWith<$Res> {
dynamic refId,
SnAttachmentPool? pool,
int poolId,
int accountId,
Map<String, dynamic> metadata});
int accountId});
$SnAttachmentPoolCopyWith<$Res>? get pool;
}
@ -122,6 +122,7 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
Object? refCount = null,
Object? fileChunks = freezed,
Object? cleanedAt = freezed,
Object? metadata = null,
Object? isMature = null,
Object? isAnalyzed = null,
Object? isUploaded = null,
@ -131,7 +132,6 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
Object? pool = freezed,
Object? poolId = null,
Object? accountId = null,
Object? metadata = null,
}) {
return _then(_value.copyWith(
id: null == id
@ -194,6 +194,10 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
? _value.cleanedAt
: cleanedAt // ignore: cast_nullable_to_non_nullable
as dynamic,
metadata: null == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
isMature: null == isMature
? _value.isMature
: isMature // ignore: cast_nullable_to_non_nullable
@ -230,10 +234,6 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
metadata: null == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
) as $Val);
}
@ -276,6 +276,7 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
int refCount,
dynamic fileChunks,
dynamic cleanedAt,
Map<String, dynamic> metadata,
bool isMature,
bool isAnalyzed,
bool isUploaded,
@ -284,8 +285,7 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
dynamic refId,
SnAttachmentPool? pool,
int poolId,
int accountId,
Map<String, dynamic> metadata});
int accountId});
@override
$SnAttachmentPoolCopyWith<$Res>? get pool;
@ -319,6 +319,7 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
Object? refCount = null,
Object? fileChunks = freezed,
Object? cleanedAt = freezed,
Object? metadata = null,
Object? isMature = null,
Object? isAnalyzed = null,
Object? isUploaded = null,
@ -328,7 +329,6 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
Object? pool = freezed,
Object? poolId = null,
Object? accountId = null,
Object? metadata = null,
}) {
return _then(_$SnAttachmentImpl(
id: null == id
@ -391,6 +391,10 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
? _value.cleanedAt
: cleanedAt // ignore: cast_nullable_to_non_nullable
as dynamic,
metadata: null == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
isMature: null == isMature
? _value.isMature
: isMature // ignore: cast_nullable_to_non_nullable
@ -427,10 +431,6 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
metadata: null == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
@ -454,6 +454,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
required this.refCount,
required this.fileChunks,
required this.cleanedAt,
required final Map<String, dynamic> metadata,
required this.isMature,
required this.isAnalyzed,
required this.isUploaded,
@ -462,8 +463,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
required this.refId,
required this.pool,
required this.poolId,
required this.accountId,
final Map<String, dynamic> metadata = const {}})
required this.accountId})
: _metadata = metadata;
factory _$SnAttachmentImpl.fromJson(Map<String, dynamic> json) =>
@ -499,6 +499,14 @@ class _$SnAttachmentImpl implements _SnAttachment {
final dynamic fileChunks;
@override
final dynamic cleanedAt;
final Map<String, dynamic> _metadata;
@override
Map<String, dynamic> get metadata {
if (_metadata is EqualUnmodifiableMapView) return _metadata;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_metadata);
}
@override
final bool isMature;
@override
@ -517,18 +525,10 @@ class _$SnAttachmentImpl implements _SnAttachment {
final int poolId;
@override
final int accountId;
final Map<String, dynamic> _metadata;
@override
@JsonKey()
Map<String, dynamic> get metadata {
if (_metadata is EqualUnmodifiableMapView) return _metadata;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_metadata);
}
@override
String toString() {
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, fileChunks: $fileChunks, cleanedAt: $cleanedAt, isMature: $isMature, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, metadata: $metadata)';
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, fileChunks: $fileChunks, cleanedAt: $cleanedAt, metadata: $metadata, isMature: $isMature, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId)';
}
@override
@ -557,6 +557,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
const DeepCollectionEquality()
.equals(other.fileChunks, fileChunks) &&
const DeepCollectionEquality().equals(other.cleanedAt, cleanedAt) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
(identical(other.isMature, isMature) ||
other.isMature == isMature) &&
(identical(other.isAnalyzed, isAnalyzed) ||
@ -570,8 +571,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
(identical(other.pool, pool) || other.pool == pool) &&
(identical(other.poolId, poolId) || other.poolId == poolId) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId) &&
const DeepCollectionEquality().equals(other._metadata, _metadata));
other.accountId == accountId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@ -593,6 +593,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
refCount,
const DeepCollectionEquality().hash(fileChunks),
const DeepCollectionEquality().hash(cleanedAt),
const DeepCollectionEquality().hash(_metadata),
isMature,
isAnalyzed,
isUploaded,
@ -601,8 +602,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
const DeepCollectionEquality().hash(refId),
pool,
poolId,
accountId,
const DeepCollectionEquality().hash(_metadata)
accountId
]);
/// Create a copy of SnAttachment
@ -638,6 +638,7 @@ abstract class _SnAttachment implements SnAttachment {
required final int refCount,
required final dynamic fileChunks,
required final dynamic cleanedAt,
required final Map<String, dynamic> metadata,
required final bool isMature,
required final bool isAnalyzed,
required final bool isUploaded,
@ -646,8 +647,7 @@ abstract class _SnAttachment implements SnAttachment {
required final dynamic refId,
required final SnAttachmentPool? pool,
required final int poolId,
required final int accountId,
final Map<String, dynamic> metadata}) = _$SnAttachmentImpl;
required final int accountId}) = _$SnAttachmentImpl;
factory _SnAttachment.fromJson(Map<String, dynamic> json) =
_$SnAttachmentImpl.fromJson;
@ -683,6 +683,8 @@ abstract class _SnAttachment implements SnAttachment {
@override
dynamic get cleanedAt;
@override
Map<String, dynamic> get metadata;
@override
bool get isMature;
@override
bool get isAnalyzed;
@ -700,8 +702,6 @@ abstract class _SnAttachment implements SnAttachment {
int get poolId;
@override
int get accountId;
@override
Map<String, dynamic> get metadata;
/// Create a copy of SnAttachment
/// with the given fields replaced by the non-null parameter values.

View File

@ -23,6 +23,7 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
refCount: (json['ref_count'] as num).toInt(),
fileChunks: json['file_chunks'],
cleanedAt: json['cleaned_at'],
metadata: json['metadata'] as Map<String, dynamic>,
isMature: json['is_mature'] as bool,
isAnalyzed: json['is_analyzed'] as bool,
isUploaded: json['is_uploaded'] as bool,
@ -34,7 +35,6 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
poolId: (json['pool_id'] as num).toInt(),
accountId: (json['account_id'] as num).toInt(),
metadata: json['metadata'] as Map<String, dynamic>? ?? const {},
);
Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
@ -54,6 +54,7 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
'ref_count': instance.refCount,
'file_chunks': instance.fileChunks,
'cleaned_at': instance.cleanedAt,
'metadata': instance.metadata,
'is_mature': instance.isMature,
'is_analyzed': instance.isAnalyzed,
'is_uploaded': instance.isUploaded,
@ -63,7 +64,6 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
'pool': instance.pool?.toJson(),
'pool_id': instance.poolId,
'account_id': instance.accountId,
'metadata': instance.metadata,
};
_$SnAttachmentPoolImpl _$$SnAttachmentPoolImplFromJson(

View File

@ -1,37 +0,0 @@
import 'package:dismissible_page/dismissible_page.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/universal_image.dart';
import 'package:uuid/uuid.dart';
class AttachmentDetailPopup extends StatelessWidget {
final SnAttachment data;
final String? heroTag;
const AttachmentDetailPopup({super.key, required this.data, this.heroTag});
@override
Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>();
final uuid = Uuid();
return DismissiblePage(
onDismissed: () {
Navigator.of(context).pop();
},
direction: DismissiblePageDismissDirection.down,
backgroundColor: Colors.transparent,
isFullScreen: true,
child: Hero(
tag: 'attachment-${data.rid}-${heroTag ?? uuid.v4()}',
child: PhotoView(
imageProvider: UniversalImage.provider(
sn.getAttachmentUrl(data.rid),
),
),
),
);
}
}

View File

@ -1,55 +1,25 @@
import 'package:dismissible_page/dismissible_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/attachment/attachment_detail.dart';
import 'package:surface/widgets/universal_image.dart';
import 'package:uuid/uuid.dart';
class AttachmentItem extends StatelessWidget {
final SnAttachment data;
final bool isExpandable;
const AttachmentItem({
super.key,
required this.data,
this.isExpandable = false,
});
const AttachmentItem({super.key, required this.data});
Widget _buildContent(BuildContext context, String heroTag) {
@override
Widget build(BuildContext context) {
final tp = data.mimetype.split('/').firstOrNull;
final sn = context.read<SnNetworkProvider>();
switch (tp) {
case 'image':
return Hero(
tag: 'attachment-${data.rid}-$heroTag',
child: UniversalImage(
sn.getAttachmentUrl(data.rid),
fit: BoxFit.cover,
),
return AspectRatio(
aspectRatio: data.metadata['ratio']?.toDouble(),
child: UniversalImage(sn.getAttachmentUrl(data.rid)),
);
default:
return const Placeholder();
}
}
@override
Widget build(BuildContext context) {
final uuid = Uuid();
final heroTag = uuid.v4();
if (isExpandable) {
return GestureDetector(
child: _buildContent(context, heroTag),
onTap: () {
context.pushTransparentRoute(
AttachmentDetailPopup(data: data, heroTag: heroTag),
rootNavigator: true,
);
},
);
}
return _buildContent(context, heroTag);
}
}

View File

@ -1,9 +1,6 @@
import 'dart:math' as math;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/attachment/attachment_item.dart';
@ -11,16 +8,8 @@ class AttachmentList extends StatelessWidget {
final List<SnAttachment> data;
final bool? bordered;
final double? maxListHeight;
const AttachmentList({
super.key,
required this.data,
this.bordered,
this.maxListHeight,
});
static const double kMaxListItemWidth = 520;
static const BorderRadius kDefaultRadius =
BorderRadius.all(Radius.circular(8));
const AttachmentList(
{super.key, required this.data, this.bordered, this.maxListHeight});
@override
Widget build(BuildContext context) {
@ -30,36 +19,11 @@ class AttachmentList extends StatelessWidget {
if (data.isEmpty) return const SizedBox.shrink();
if (data.length == 1) {
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
return Container(
constraints: BoxConstraints(
maxWidth: math.min(
MediaQuery.of(context).size.width - 20,
kMaxListItemWidth,
),
),
decoration: BoxDecoration(
border: Border(top: borderSide, bottom: borderSide),
borderRadius: kDefaultRadius,
),
child: AspectRatio(
aspectRatio: data[0].metadata['ratio']?.toDouble() ?? 1,
child: ClipRRect(
borderRadius: kDefaultRadius,
child: AttachmentItem(data: data[0], isExpandable: true),
),
),
);
}
return Container(
decoration: BoxDecoration(
border: Border(top: borderSide, bottom: borderSide),
),
child: AspectRatio(
aspectRatio: data[0].metadata['ratio']?.toDouble() ?? 1,
child: AttachmentItem(data: data[0], isExpandable: true),
),
child: AttachmentItem(data: data[0]),
);
}
@ -71,23 +35,15 @@ class AttachmentList extends StatelessWidget {
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, idx) {
const radius = BorderRadius.all(Radius.circular(8));
return Container(
constraints: BoxConstraints(
maxWidth: math.min(
MediaQuery.of(context).size.width - 20,
kMaxListItemWidth,
),
),
decoration: BoxDecoration(
border: Border(top: borderSide, bottom: borderSide),
borderRadius: kDefaultRadius,
borderRadius: radius,
),
child: AspectRatio(
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
child: ClipRRect(
borderRadius: kDefaultRadius,
child: AttachmentItem(data: data[idx], isExpandable: true),
),
borderRadius: radius,
child: AttachmentItem(data: data[idx]),
),
);
},

View File

@ -1,67 +0,0 @@
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart';
class PostMediaPendingList extends StatelessWidget {
final List<XFile> data;
final Function(int idx)? onRemove;
const PostMediaPendingList({
super.key,
required this.data,
this.onRemove,
});
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(maxHeight: 120),
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8),
separatorBuilder: (context, index) => const Gap(8),
itemCount: data.length,
itemBuilder: (context, idx) {
final file = data[idx];
return ContextMenuRegion(
contextMenu: ContextMenu(
entries: [
if (onRemove != null)
MenuItem(
label: 'delete'.tr(),
icon: Symbols.delete,
onSelected: () {
onRemove!(idx);
},
),
],
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1,
),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AspectRatio(
aspectRatio: 1,
child: kIsWeb
? Image.network(file.path, fit: BoxFit.cover)
: Image.file(File(file.path), fit: BoxFit.cover),
),
),
),
);
},
),
);
}
}

View File

@ -334,14 +334,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
dismissible_page:
dependency: "direct main"
description:
name: dismissible_page
sha256: "5b2316f770fe83583f770df1f6505cb19102081c5971979806e77f2e507a9958"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
dropdown_button2:
dependency: "direct main"
description:
@ -475,14 +467,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_context_menu:
dependency: "direct main"
description:
name: flutter_context_menu
sha256: "4bc1dc30ae5aa705ed99ebbeb875898c6341a6d092397a566fecd5184b392380"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
flutter_expandable_fab:
dependency: "direct main"
description:
@ -1042,14 +1026,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.2"
photo_view:
dependency: "direct main"
description:
name: photo_view
sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
platform:
dependency: transitive
description:
@ -1432,7 +1408,7 @@ packages:
source: hosted
version: "3.1.3"
uuid:
dependency: "direct main"
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff

View File

@ -66,10 +66,6 @@ dependencies:
croppy: ^1.3.1
flutter_expandable_fab: ^2.3.0
dropdown_button2: ^2.3.9
flutter_context_menu: ^0.2.0
dismissible_page: ^1.0.2
uuid: ^4.5.1
photo_view: ^0.15.0
dev_dependencies:
flutter_test: