✨ Post with attachment
This commit is contained in:
parent
302691f557
commit
ef51948fad
@ -30,6 +30,7 @@
|
|||||||
"create": "Create",
|
"create": "Create",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
|
"delete": "Delete",
|
||||||
"fieldUsername": "Username",
|
"fieldUsername": "Username",
|
||||||
"fieldNickname": "Nickname",
|
"fieldNickname": "Nickname",
|
||||||
"fieldEmail": "Email address",
|
"fieldEmail": "Email address",
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"apply": "应用",
|
"apply": "应用",
|
||||||
"create": "创建",
|
"create": "创建",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
|
"delete": "删除",
|
||||||
"fieldUsername": "用户名",
|
"fieldUsername": "用户名",
|
||||||
"fieldNickname": "显示名",
|
"fieldNickname": "显示名",
|
||||||
"fieldEmail": "电子邮箱地址",
|
"fieldEmail": "电子邮箱地址",
|
||||||
|
@ -84,7 +84,12 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
distance: 75,
|
distance: 75,
|
||||||
type: ExpandableFabType.up,
|
type: ExpandableFabType.up,
|
||||||
childrenAnimation: ExpandableFabAnimation.none,
|
childrenAnimation: ExpandableFabAnimation.none,
|
||||||
overlayStyle: ExpandableFabOverlayStyle(blur: 10),
|
overlayStyle: ExpandableFabOverlayStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surface
|
||||||
|
.withAlpha((255 * 0.5).round()),
|
||||||
|
),
|
||||||
openButtonBuilder: RotateFloatingActionButtonBuilder(
|
openButtonBuilder: RotateFloatingActionButtonBuilder(
|
||||||
child: const Icon(Symbols.add, size: 28),
|
child: const Icon(Symbols.add, size: 28),
|
||||||
fabSize: ExpandableFabSize.regular,
|
fabSize: ExpandableFabSize.regular,
|
||||||
|
@ -4,15 +4,19 @@ import 'package:flutter/gestures.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:go_router/go_router.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.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_network.dart';
|
import 'package:surface/providers/sn_network.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/dialog.dart';
|
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.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/post/post_meta_editor.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class PostEditorScreen extends StatefulWidget {
|
class PostEditorScreen extends StatefulWidget {
|
||||||
final String mode;
|
final String mode;
|
||||||
@ -33,6 +37,9 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
SnPublisher? _publisher;
|
SnPublisher? _publisher;
|
||||||
List<SnPublisher>? _publishers;
|
List<SnPublisher>? _publishers;
|
||||||
|
|
||||||
|
final List<XFile> _selectedMedia = List.empty(growable: true);
|
||||||
|
final List<SnAttachment> _attachments = List.empty(growable: true);
|
||||||
|
|
||||||
void _fetchPublishers() async {
|
void _fetchPublishers() async {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/publishers');
|
final resp = await sn.client.get('/cgi/co/publishers');
|
||||||
@ -49,19 +56,41 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
|
|
||||||
final TextEditingController _contentController = TextEditingController();
|
final TextEditingController _contentController = TextEditingController();
|
||||||
|
|
||||||
|
double? _progress;
|
||||||
|
|
||||||
void _performAction() async {
|
void _performAction() async {
|
||||||
if (_isBusy || _publisher == null) return;
|
if (_isBusy || _publisher == null) return;
|
||||||
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
// Uploading attachments
|
||||||
|
try {
|
||||||
|
for (final media in _selectedMedia) {
|
||||||
|
final place = await attach.chunkedUploadInitialize(
|
||||||
|
await media.length(),
|
||||||
|
media.name,
|
||||||
|
'interactive',
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
final item = await attach.chunkedUploadParts(media, place.$1, place.$2);
|
||||||
|
_attachments.add(item);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finishing up
|
||||||
try {
|
try {
|
||||||
await sn.client.post('/cgi/co/${widget.mode}', data: {
|
await sn.client.post('/cgi/co/${widget.mode}', data: {
|
||||||
'publisher': _publisher!.id,
|
'publisher': _publisher!.id,
|
||||||
'content': _contentController.value.text,
|
'content': _contentController.value.text,
|
||||||
'title': _title,
|
'title': _title,
|
||||||
'description': _description,
|
'description': _description,
|
||||||
|
'attachments': _attachments.map((e) => e.rid).toList(),
|
||||||
});
|
});
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -88,6 +117,15 @@ 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
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_contentController.dispose();
|
_contentController.dispose();
|
||||||
@ -248,13 +286,23 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_selectedMedia.isNotEmpty)
|
||||||
|
PostMediaPendingList(
|
||||||
|
data: _selectedMedia,
|
||||||
|
onRemove: (idx) {
|
||||||
|
setState(() {
|
||||||
|
_selectedMedia.removeAt(idx);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
).padding(bottom: 8),
|
||||||
Material(
|
Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_isBusy)
|
if (_isBusy)
|
||||||
const LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
value: _progress,
|
||||||
minHeight: 2,
|
minHeight: 2,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -268,7 +316,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {},
|
onPressed: _isBusy ? null : _selectMedia,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Symbols.add_photo_alternate,
|
Symbols.add_photo_alternate,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
@ -21,7 +21,6 @@ class SnAttachment with _$SnAttachment {
|
|||||||
required int refCount,
|
required int refCount,
|
||||||
required dynamic fileChunks,
|
required dynamic fileChunks,
|
||||||
required dynamic cleanedAt,
|
required dynamic cleanedAt,
|
||||||
required Map<String, dynamic> metadata,
|
|
||||||
required bool isMature,
|
required bool isMature,
|
||||||
required bool isAnalyzed,
|
required bool isAnalyzed,
|
||||||
required bool isUploaded,
|
required bool isUploaded,
|
||||||
@ -31,6 +30,7 @@ class SnAttachment with _$SnAttachment {
|
|||||||
required SnAttachmentPool? pool,
|
required SnAttachmentPool? pool,
|
||||||
required int poolId,
|
required int poolId,
|
||||||
required int accountId,
|
required int accountId,
|
||||||
|
@Default({}) Map<String, dynamic> metadata,
|
||||||
}) = _SnAttachment;
|
}) = _SnAttachment;
|
||||||
|
|
||||||
factory SnAttachment.fromJson(Map<String, Object?> json) =>
|
factory SnAttachment.fromJson(Map<String, Object?> json) =>
|
||||||
|
@ -35,7 +35,6 @@ mixin _$SnAttachment {
|
|||||||
int get refCount => throw _privateConstructorUsedError;
|
int get refCount => throw _privateConstructorUsedError;
|
||||||
dynamic get fileChunks => throw _privateConstructorUsedError;
|
dynamic get fileChunks => throw _privateConstructorUsedError;
|
||||||
dynamic get cleanedAt => throw _privateConstructorUsedError;
|
dynamic get cleanedAt => throw _privateConstructorUsedError;
|
||||||
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
|
||||||
bool get isMature => throw _privateConstructorUsedError;
|
bool get isMature => throw _privateConstructorUsedError;
|
||||||
bool get isAnalyzed => throw _privateConstructorUsedError;
|
bool get isAnalyzed => throw _privateConstructorUsedError;
|
||||||
bool get isUploaded => throw _privateConstructorUsedError;
|
bool get isUploaded => throw _privateConstructorUsedError;
|
||||||
@ -45,6 +44,7 @@ mixin _$SnAttachment {
|
|||||||
SnAttachmentPool? get pool => throw _privateConstructorUsedError;
|
SnAttachmentPool? get pool => throw _privateConstructorUsedError;
|
||||||
int get poolId => throw _privateConstructorUsedError;
|
int get poolId => throw _privateConstructorUsedError;
|
||||||
int get accountId => throw _privateConstructorUsedError;
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this SnAttachment to a JSON map.
|
/// Serializes this SnAttachment to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
@ -78,7 +78,6 @@ abstract class $SnAttachmentCopyWith<$Res> {
|
|||||||
int refCount,
|
int refCount,
|
||||||
dynamic fileChunks,
|
dynamic fileChunks,
|
||||||
dynamic cleanedAt,
|
dynamic cleanedAt,
|
||||||
Map<String, dynamic> metadata,
|
|
||||||
bool isMature,
|
bool isMature,
|
||||||
bool isAnalyzed,
|
bool isAnalyzed,
|
||||||
bool isUploaded,
|
bool isUploaded,
|
||||||
@ -87,7 +86,8 @@ abstract class $SnAttachmentCopyWith<$Res> {
|
|||||||
dynamic refId,
|
dynamic refId,
|
||||||
SnAttachmentPool? pool,
|
SnAttachmentPool? pool,
|
||||||
int poolId,
|
int poolId,
|
||||||
int accountId});
|
int accountId,
|
||||||
|
Map<String, dynamic> metadata});
|
||||||
|
|
||||||
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
||||||
}
|
}
|
||||||
@ -122,7 +122,6 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
|||||||
Object? refCount = null,
|
Object? refCount = null,
|
||||||
Object? fileChunks = freezed,
|
Object? fileChunks = freezed,
|
||||||
Object? cleanedAt = freezed,
|
Object? cleanedAt = freezed,
|
||||||
Object? metadata = null,
|
|
||||||
Object? isMature = null,
|
Object? isMature = null,
|
||||||
Object? isAnalyzed = null,
|
Object? isAnalyzed = null,
|
||||||
Object? isUploaded = null,
|
Object? isUploaded = null,
|
||||||
@ -132,6 +131,7 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
|||||||
Object? pool = freezed,
|
Object? pool = freezed,
|
||||||
Object? poolId = null,
|
Object? poolId = null,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
|
Object? metadata = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
id: null == id
|
id: null == id
|
||||||
@ -194,10 +194,6 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
|||||||
? _value.cleanedAt
|
? _value.cleanedAt
|
||||||
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as dynamic,
|
as dynamic,
|
||||||
metadata: null == metadata
|
|
||||||
? _value.metadata
|
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>,
|
|
||||||
isMature: null == isMature
|
isMature: null == isMature
|
||||||
? _value.isMature
|
? _value.isMature
|
||||||
: isMature // ignore: cast_nullable_to_non_nullable
|
: isMature // ignore: cast_nullable_to_non_nullable
|
||||||
@ -234,6 +230,10 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
|||||||
? _value.accountId
|
? _value.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
metadata: null == metadata
|
||||||
|
? _value.metadata
|
||||||
|
: metadata // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +276,6 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
|
|||||||
int refCount,
|
int refCount,
|
||||||
dynamic fileChunks,
|
dynamic fileChunks,
|
||||||
dynamic cleanedAt,
|
dynamic cleanedAt,
|
||||||
Map<String, dynamic> metadata,
|
|
||||||
bool isMature,
|
bool isMature,
|
||||||
bool isAnalyzed,
|
bool isAnalyzed,
|
||||||
bool isUploaded,
|
bool isUploaded,
|
||||||
@ -285,7 +284,8 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
|
|||||||
dynamic refId,
|
dynamic refId,
|
||||||
SnAttachmentPool? pool,
|
SnAttachmentPool? pool,
|
||||||
int poolId,
|
int poolId,
|
||||||
int accountId});
|
int accountId,
|
||||||
|
Map<String, dynamic> metadata});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
||||||
@ -319,7 +319,6 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
|||||||
Object? refCount = null,
|
Object? refCount = null,
|
||||||
Object? fileChunks = freezed,
|
Object? fileChunks = freezed,
|
||||||
Object? cleanedAt = freezed,
|
Object? cleanedAt = freezed,
|
||||||
Object? metadata = null,
|
|
||||||
Object? isMature = null,
|
Object? isMature = null,
|
||||||
Object? isAnalyzed = null,
|
Object? isAnalyzed = null,
|
||||||
Object? isUploaded = null,
|
Object? isUploaded = null,
|
||||||
@ -329,6 +328,7 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
|||||||
Object? pool = freezed,
|
Object? pool = freezed,
|
||||||
Object? poolId = null,
|
Object? poolId = null,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
|
Object? metadata = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$SnAttachmentImpl(
|
return _then(_$SnAttachmentImpl(
|
||||||
id: null == id
|
id: null == id
|
||||||
@ -391,10 +391,6 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
|||||||
? _value.cleanedAt
|
? _value.cleanedAt
|
||||||
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as dynamic,
|
as dynamic,
|
||||||
metadata: null == metadata
|
|
||||||
? _value._metadata
|
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>,
|
|
||||||
isMature: null == isMature
|
isMature: null == isMature
|
||||||
? _value.isMature
|
? _value.isMature
|
||||||
: isMature // ignore: cast_nullable_to_non_nullable
|
: isMature // ignore: cast_nullable_to_non_nullable
|
||||||
@ -431,6 +427,10 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
|||||||
? _value.accountId
|
? _value.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
metadata: null == metadata
|
||||||
|
? _value._metadata
|
||||||
|
: metadata // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,7 +454,6 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
required this.refCount,
|
required this.refCount,
|
||||||
required this.fileChunks,
|
required this.fileChunks,
|
||||||
required this.cleanedAt,
|
required this.cleanedAt,
|
||||||
required final Map<String, dynamic> metadata,
|
|
||||||
required this.isMature,
|
required this.isMature,
|
||||||
required this.isAnalyzed,
|
required this.isAnalyzed,
|
||||||
required this.isUploaded,
|
required this.isUploaded,
|
||||||
@ -463,7 +462,8 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
required this.refId,
|
required this.refId,
|
||||||
required this.pool,
|
required this.pool,
|
||||||
required this.poolId,
|
required this.poolId,
|
||||||
required this.accountId})
|
required this.accountId,
|
||||||
|
final Map<String, dynamic> metadata = const {}})
|
||||||
: _metadata = metadata;
|
: _metadata = metadata;
|
||||||
|
|
||||||
factory _$SnAttachmentImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$SnAttachmentImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
@ -499,14 +499,6 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
final dynamic fileChunks;
|
final dynamic fileChunks;
|
||||||
@override
|
@override
|
||||||
final dynamic cleanedAt;
|
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
|
@override
|
||||||
final bool isMature;
|
final bool isMature;
|
||||||
@override
|
@override
|
||||||
@ -525,10 +517,18 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
final int poolId;
|
final int poolId;
|
||||||
@override
|
@override
|
||||||
final int accountId;
|
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
|
@override
|
||||||
String toString() {
|
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, metadata: $metadata, isMature: $isMature, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId)';
|
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)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -557,7 +557,6 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
const DeepCollectionEquality()
|
const DeepCollectionEquality()
|
||||||
.equals(other.fileChunks, fileChunks) &&
|
.equals(other.fileChunks, fileChunks) &&
|
||||||
const DeepCollectionEquality().equals(other.cleanedAt, cleanedAt) &&
|
const DeepCollectionEquality().equals(other.cleanedAt, cleanedAt) &&
|
||||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
|
||||||
(identical(other.isMature, isMature) ||
|
(identical(other.isMature, isMature) ||
|
||||||
other.isMature == isMature) &&
|
other.isMature == isMature) &&
|
||||||
(identical(other.isAnalyzed, isAnalyzed) ||
|
(identical(other.isAnalyzed, isAnalyzed) ||
|
||||||
@ -571,7 +570,8 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
(identical(other.pool, pool) || other.pool == pool) &&
|
(identical(other.pool, pool) || other.pool == pool) &&
|
||||||
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
||||||
(identical(other.accountId, accountId) ||
|
(identical(other.accountId, accountId) ||
|
||||||
other.accountId == accountId));
|
other.accountId == accountId) &&
|
||||||
|
const DeepCollectionEquality().equals(other._metadata, _metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@ -593,7 +593,6 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
refCount,
|
refCount,
|
||||||
const DeepCollectionEquality().hash(fileChunks),
|
const DeepCollectionEquality().hash(fileChunks),
|
||||||
const DeepCollectionEquality().hash(cleanedAt),
|
const DeepCollectionEquality().hash(cleanedAt),
|
||||||
const DeepCollectionEquality().hash(_metadata),
|
|
||||||
isMature,
|
isMature,
|
||||||
isAnalyzed,
|
isAnalyzed,
|
||||||
isUploaded,
|
isUploaded,
|
||||||
@ -602,7 +601,8 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
|||||||
const DeepCollectionEquality().hash(refId),
|
const DeepCollectionEquality().hash(refId),
|
||||||
pool,
|
pool,
|
||||||
poolId,
|
poolId,
|
||||||
accountId
|
accountId,
|
||||||
|
const DeepCollectionEquality().hash(_metadata)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Create a copy of SnAttachment
|
/// Create a copy of SnAttachment
|
||||||
@ -638,7 +638,6 @@ abstract class _SnAttachment implements SnAttachment {
|
|||||||
required final int refCount,
|
required final int refCount,
|
||||||
required final dynamic fileChunks,
|
required final dynamic fileChunks,
|
||||||
required final dynamic cleanedAt,
|
required final dynamic cleanedAt,
|
||||||
required final Map<String, dynamic> metadata,
|
|
||||||
required final bool isMature,
|
required final bool isMature,
|
||||||
required final bool isAnalyzed,
|
required final bool isAnalyzed,
|
||||||
required final bool isUploaded,
|
required final bool isUploaded,
|
||||||
@ -647,7 +646,8 @@ abstract class _SnAttachment implements SnAttachment {
|
|||||||
required final dynamic refId,
|
required final dynamic refId,
|
||||||
required final SnAttachmentPool? pool,
|
required final SnAttachmentPool? pool,
|
||||||
required final int poolId,
|
required final int poolId,
|
||||||
required final int accountId}) = _$SnAttachmentImpl;
|
required final int accountId,
|
||||||
|
final Map<String, dynamic> metadata}) = _$SnAttachmentImpl;
|
||||||
|
|
||||||
factory _SnAttachment.fromJson(Map<String, dynamic> json) =
|
factory _SnAttachment.fromJson(Map<String, dynamic> json) =
|
||||||
_$SnAttachmentImpl.fromJson;
|
_$SnAttachmentImpl.fromJson;
|
||||||
@ -683,8 +683,6 @@ abstract class _SnAttachment implements SnAttachment {
|
|||||||
@override
|
@override
|
||||||
dynamic get cleanedAt;
|
dynamic get cleanedAt;
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> get metadata;
|
|
||||||
@override
|
|
||||||
bool get isMature;
|
bool get isMature;
|
||||||
@override
|
@override
|
||||||
bool get isAnalyzed;
|
bool get isAnalyzed;
|
||||||
@ -702,6 +700,8 @@ abstract class _SnAttachment implements SnAttachment {
|
|||||||
int get poolId;
|
int get poolId;
|
||||||
@override
|
@override
|
||||||
int get accountId;
|
int get accountId;
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> get metadata;
|
||||||
|
|
||||||
/// Create a copy of SnAttachment
|
/// Create a copy of SnAttachment
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@ -23,7 +23,6 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
|
|||||||
refCount: (json['ref_count'] as num).toInt(),
|
refCount: (json['ref_count'] as num).toInt(),
|
||||||
fileChunks: json['file_chunks'],
|
fileChunks: json['file_chunks'],
|
||||||
cleanedAt: json['cleaned_at'],
|
cleanedAt: json['cleaned_at'],
|
||||||
metadata: json['metadata'] as Map<String, dynamic>,
|
|
||||||
isMature: json['is_mature'] as bool,
|
isMature: json['is_mature'] as bool,
|
||||||
isAnalyzed: json['is_analyzed'] as bool,
|
isAnalyzed: json['is_analyzed'] as bool,
|
||||||
isUploaded: json['is_uploaded'] as bool,
|
isUploaded: json['is_uploaded'] as bool,
|
||||||
@ -35,6 +34,7 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
|
|||||||
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
|
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
|
||||||
poolId: (json['pool_id'] as num).toInt(),
|
poolId: (json['pool_id'] as num).toInt(),
|
||||||
accountId: (json['account_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) =>
|
Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
|
||||||
@ -54,7 +54,6 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
|
|||||||
'ref_count': instance.refCount,
|
'ref_count': instance.refCount,
|
||||||
'file_chunks': instance.fileChunks,
|
'file_chunks': instance.fileChunks,
|
||||||
'cleaned_at': instance.cleanedAt,
|
'cleaned_at': instance.cleanedAt,
|
||||||
'metadata': instance.metadata,
|
|
||||||
'is_mature': instance.isMature,
|
'is_mature': instance.isMature,
|
||||||
'is_analyzed': instance.isAnalyzed,
|
'is_analyzed': instance.isAnalyzed,
|
||||||
'is_uploaded': instance.isUploaded,
|
'is_uploaded': instance.isUploaded,
|
||||||
@ -64,6 +63,7 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
|
|||||||
'pool': instance.pool?.toJson(),
|
'pool': instance.pool?.toJson(),
|
||||||
'pool_id': instance.poolId,
|
'pool_id': instance.poolId,
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
|
'metadata': instance.metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$SnAttachmentPoolImpl _$$SnAttachmentPoolImplFromJson(
|
_$SnAttachmentPoolImpl _$$SnAttachmentPoolImplFromJson(
|
||||||
|
@ -14,9 +14,9 @@ class AttachmentItem extends StatelessWidget {
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
switch (tp) {
|
switch (tp) {
|
||||||
case 'image':
|
case 'image':
|
||||||
return AspectRatio(
|
return UniversalImage(
|
||||||
aspectRatio: data.metadata['ratio']?.toDouble(),
|
sn.getAttachmentUrl(data.rid),
|
||||||
child: UniversalImage(sn.getAttachmentUrl(data.rid)),
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return const Placeholder();
|
return const Placeholder();
|
||||||
|
@ -8,8 +8,12 @@ class AttachmentList extends StatelessWidget {
|
|||||||
final List<SnAttachment> data;
|
final List<SnAttachment> data;
|
||||||
final bool? bordered;
|
final bool? bordered;
|
||||||
final double? maxListHeight;
|
final double? maxListHeight;
|
||||||
const AttachmentList(
|
const AttachmentList({
|
||||||
{super.key, required this.data, this.bordered, this.maxListHeight});
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
this.bordered,
|
||||||
|
this.maxListHeight,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -37,13 +41,19 @@ class AttachmentList extends StatelessWidget {
|
|||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
const radius = BorderRadius.all(Radius.circular(8));
|
const radius = BorderRadius.all(Radius.circular(8));
|
||||||
return Container(
|
return Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width - 20,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
child: AspectRatio(
|
||||||
borderRadius: radius,
|
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
|
||||||
child: AttachmentItem(data: data[idx]),
|
child: ClipRRect(
|
||||||
|
borderRadius: radius,
|
||||||
|
child: AttachmentItem(data: data[idx]),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
67
lib/widgets/post/post_media_pending_list.dart
Normal file
67
lib/widgets/post/post_media_pending_list.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -467,6 +467,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
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:
|
flutter_expandable_fab:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -66,6 +66,7 @@ dependencies:
|
|||||||
croppy: ^1.3.1
|
croppy: ^1.3.1
|
||||||
flutter_expandable_fab: ^2.3.0
|
flutter_expandable_fab: ^2.3.0
|
||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
|
flutter_context_menu: ^0.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user