⬆️ Support new attachments system

This commit is contained in:
LittleSheep 2024-08-18 22:51:52 +08:00
parent 98749f42c0
commit 49f73f5f04
19 changed files with 154 additions and 151 deletions

View File

@ -31,9 +31,9 @@ class PostEditorController extends GetxController {
Rx<Realm?> realmZone = Rx(null); Rx<Realm?> realmZone = Rx(null);
Rx<DateTime?> publishedAt = Rx(null); Rx<DateTime?> publishedAt = Rx(null);
Rx<DateTime?> publishedUntil = Rx(null); Rx<DateTime?> publishedUntil = Rx(null);
RxList<int> attachments = RxList<int>.empty(growable: true); RxList<String> attachments = RxList<String>.empty(growable: true);
RxList<String> tags = RxList<String>.empty(growable: true); RxList<String> tags = RxList<String>.empty(growable: true);
Rx<int?> thumbnail = Rx(null); Rx<String?> thumbnail = Rx(null);
RxList<int> visibleUsers = RxList.empty(growable: true); RxList<int> visibleUsers = RxList.empty(growable: true);
RxList<int> invisibleUsers = RxList.empty(growable: true); RxList<int> invisibleUsers = RxList.empty(growable: true);
@ -116,12 +116,12 @@ class PostEditorController extends GetxController {
return showModalBottomSheet( return showModalBottomSheet(
context: context, context: context,
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment', pool: 'interactive',
initialAttachments: attachments, initialAttachments: attachments,
onAdd: (int value) { onAdd: (String value) {
attachments.add(value); attachments.add(value);
}, },
onRemove: (int value) { onRemove: (String value) {
attachments.remove(value); attachments.remove(value);
}, },
), ),
@ -169,6 +169,7 @@ class PostEditorController extends GetxController {
} }
void currentClear() { void currentClear() {
aliasController.clear();
titleController.clear(); titleController.clear();
descriptionController.clear(); descriptionController.clear();
contentController.clear(); contentController.clear();

View File

@ -5,11 +5,11 @@ class Attachment {
DateTime createdAt; DateTime createdAt;
DateTime updatedAt; DateTime updatedAt;
DateTime? deletedAt; DateTime? deletedAt;
String rid;
String uuid; String uuid;
int size; int size;
String name; String name;
String alt; String alt;
String usage;
String mimetype; String mimetype;
String hash; String hash;
int destination; int destination;
@ -24,11 +24,11 @@ class Attachment {
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.deletedAt, required this.deletedAt,
required this.rid,
required this.uuid, required this.uuid,
required this.size, required this.size,
required this.name, required this.name,
required this.alt, required this.alt,
required this.usage,
required this.mimetype, required this.mimetype,
required this.hash, required this.hash,
required this.destination, required this.destination,
@ -40,42 +40,45 @@ class Attachment {
}); });
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment( factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
id: json['id'], id: json['id'],
createdAt: DateTime.parse(json['created_at']), createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']), updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null, deletedAt: json['deleted_at'] != null
uuid: json['uuid'], ? DateTime.parse(json['deleted_at'])
size: json['size'], : null,
name: json['name'], rid: json['rid'],
alt: json['alt'], uuid: json['uuid'],
usage: json['usage'], size: json['size'],
mimetype: json['mimetype'], name: json['name'],
hash: json['hash'], alt: json['alt'],
destination: json['destination'], mimetype: json['mimetype'],
isAnalyzed: json['is_analyzed'], hash: json['hash'],
metadata: json['metadata'], destination: json['destination'],
isMature: json['is_mature'], isAnalyzed: json['is_analyzed'],
account: json['account'] != null ? Account.fromJson(json['account']) : null, metadata: json['metadata'],
accountId: json['account_id'], isMature: json['is_mature'],
); account:
json['account'] != null ? Account.fromJson(json['account']) : null,
accountId: json['account_id'],
);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'created_at': createdAt.toIso8601String(), 'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(), 'deleted_at': deletedAt?.toIso8601String(),
'uuid': uuid, 'rid': rid,
'size': size, 'uuid': uuid,
'name': name, 'size': size,
'alt': alt, 'name': name,
'usage': usage, 'alt': alt,
'mimetype': mimetype, 'mimetype': mimetype,
'hash': hash, 'hash': hash,
'destination': destination, 'destination': destination,
'is_analyzed': isAnalyzed, 'is_analyzed': isAnalyzed,
'metadata': metadata, 'metadata': metadata,
'is_mature': isMature, 'is_mature': isMature,
'account': account?.toJson(), 'account': account?.toJson(),
'account_id': accountId, 'account_id': accountId,
}; };
} }

View File

@ -148,7 +148,7 @@ class AttachmentUploaderController extends GetxController {
Future<void> uploadAttachmentWithCallback( Future<void> uploadAttachmentWithCallback(
Uint8List data, Uint8List data,
String path, String path,
String usage, String pool,
Map<String, dynamic>? metadata, Map<String, dynamic>? metadata,
Function(Attachment?) callback, Function(Attachment?) callback,
) async { ) async {
@ -158,7 +158,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
data, data,
path, path,
usage, pool,
metadata, metadata,
onProgress: (progress) { onProgress: (progress) {
progressOfUpload.value = progress; progressOfUpload.value = progress;
@ -171,7 +171,7 @@ class AttachmentUploaderController extends GetxController {
Future<Attachment?> uploadAttachment( Future<Attachment?> uploadAttachment(
Uint8List data, Uint8List data,
String path, String path,
String usage, String pool,
Map<String, dynamic>? metadata, Map<String, dynamic>? metadata,
) async { ) async {
if (isUploading.value) throw Exception('uploading blocked'); if (isUploading.value) throw Exception('uploading blocked');
@ -180,7 +180,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
data, data,
path, path,
usage, pool,
metadata, metadata,
onProgress: (progress) { onProgress: (progress) {
progressOfUpload.value = progress; progressOfUpload.value = progress;
@ -191,14 +191,14 @@ class AttachmentUploaderController extends GetxController {
} }
Future<Attachment?> _rawUploadAttachment( Future<Attachment?> _rawUploadAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata, Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress, Function(dynamic err)? onError}) async { {Function(double)? onProgress, Function(dynamic err)? onError}) async {
final AttachmentProvider provider = Get.find(); final AttachmentProvider provider = Get.find();
try { try {
final result = await provider.createAttachment( final result = await provider.createAttachment(
data, data,
path, path,
usage, pool,
metadata, metadata,
onProgress: onProgress, onProgress: onProgress,
); );

View File

@ -88,7 +88,30 @@ class ChatCallProvider extends GetxController {
void initRoom() { void initRoom() {
initHardware(); initHardware();
room = Room(); room = Room(
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
);
listener = room.createListener(); listener = room.createListener();
WakelockPlus.enable(); WakelockPlus.enable();
} }
@ -104,28 +127,6 @@ class ChatCallProvider extends GetxController {
await room.connect( await room.connect(
url, url,
token, token,
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
fastConnectOptions: FastConnectOptions( fastConnectOptions: FastConnectOptions(
microphone: TrackOption(track: audioTrack.value), microphone: TrackOption(track: audioTrack.value),
camera: TrackOption(track: videoTrack.value), camera: TrackOption(track: videoTrack.value),
@ -152,7 +153,7 @@ class ChatCallProvider extends GetxController {
void onRoomDidUpdate() => sortParticipants(); void onRoomDidUpdate() => sortParticipants();
void setupRoom() { void setupRoom() {
if(isInitialized.value) return; if (isInitialized.value) return;
sortParticipants(); sortParticipants();
room.addListener(onRoomDidUpdate); room.addListener(onRoomDidUpdate);

View File

@ -20,22 +20,22 @@ class AttachmentProvider extends GetConnect {
httpClient.baseUrl = ServiceFinder.buildUrl('files', null); httpClient.baseUrl = ServiceFinder.buildUrl('files', null);
} }
final Map<int, Attachment> _cachedResponses = {}; final Map<String, Attachment> _cachedResponses = {};
Future<List<Attachment?>> listMetadata( Future<List<Attachment?>> listMetadata(
List<int> id, { List<String> rid, {
noCache = false, noCache = false,
}) async { }) async {
if (id.isEmpty) return List.empty(); if (rid.isEmpty) return List.empty();
List<Attachment?> result = List.filled(id.length, null); List<Attachment?> result = List.filled(rid.length, null);
List<int> pendingQuery = List.empty(growable: true); List<String> pendingQuery = List.empty(growable: true);
if (!noCache) { if (!noCache) {
for (var idx = 0; idx < id.length; idx++) { for (var idx = 0; idx < rid.length; idx++) {
if (_cachedResponses.containsKey(id[idx])) { if (_cachedResponses.containsKey(rid[idx])) {
result[idx] = _cachedResponses[id[idx]]; result[idx] = _cachedResponses[rid[idx]];
} else { } else {
pendingQuery.add(id[idx]); pendingQuery.add(rid[idx]);
} }
} }
} }
@ -52,12 +52,12 @@ class AttachmentProvider extends GetConnect {
rawOut.data!.map((x) => Attachment.fromJson(x)).toList(); rawOut.data!.map((x) => Attachment.fromJson(x)).toList();
for (final item in out) { for (final item in out) {
if (item.destination != 0 && item.isAnalyzed) { if (item.destination != 0 && item.isAnalyzed) {
_cachedResponses[item.id] = item; _cachedResponses[item.rid] = item;
} }
} }
for (var i = 0; i < out.length; i++) { for (var i = 0; i < out.length; i++) {
for (var j = 0; j < id.length; j++) { for (var j = 0; j < rid.length; j++) {
if (out[i].id == id[j]) { if (out[i].rid == rid[j]) {
result[j] = out[i]; result[j] = out[i];
} }
} }
@ -66,16 +66,16 @@ class AttachmentProvider extends GetConnect {
return result; return result;
} }
Future<Attachment?> getMetadata(int id, {noCache = false}) async { Future<Attachment?> getMetadata(String rid, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(id)) { if (!noCache && _cachedResponses.containsKey(rid)) {
return _cachedResponses[id]!; return _cachedResponses[rid]!;
} }
final resp = await get('/attachments/$id/meta'); final resp = await get('/attachments/$rid/meta');
if (resp.statusCode == 200) { if (resp.statusCode == 200) {
final result = Attachment.fromJson(resp.body); final result = Attachment.fromJson(resp.body);
if (result.destination != 0 && result.isAnalyzed) { if (result.destination != 0 && result.isAnalyzed) {
_cachedResponses[id] = result; _cachedResponses[rid] = result;
} }
return result; return result;
} }
@ -84,7 +84,7 @@ class AttachmentProvider extends GetConnect {
} }
Future<Attachment> createAttachment( Future<Attachment> createAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata, Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress}) async { {Function(double)? onProgress}) async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw Exception('unauthorized'); if (auth.isAuthorized.isFalse) throw Exception('unauthorized');
@ -108,7 +108,7 @@ class AttachmentProvider extends GetConnect {
final payload = dio.FormData.fromMap({ final payload = dio.FormData.fromMap({
'alt': fileAlt, 'alt': fileAlt,
'file': filePayload, 'file': filePayload,
'usage': usage, 'pool': pool,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride, if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode(metadata), 'metadata': jsonEncode(metadata),
}); });
@ -133,8 +133,7 @@ class AttachmentProvider extends GetConnect {
Future<Response> updateAttachment( Future<Response> updateAttachment(
int id, int id,
String alt, String alt, {
String usage, {
bool isMature = false, bool isMature = false,
}) async { }) async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
@ -144,7 +143,6 @@ class AttachmentProvider extends GetConnect {
var resp = await client.put('/attachments/$id', { var resp = await client.put('/attachments/$id', {
'alt': alt, 'alt': alt,
'usage': usage,
'is_mature': isMature, 'is_mature': isMature,
}); });

View File

@ -116,7 +116,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
attachResult = await provider.createAttachment( attachResult = await provider.createAttachment(
await file.readAsBytes(), await file.readAsBytes(),
file.path, file.path,
'p.$position', 'avatar',
null, null,
); );
} catch (e) { } catch (e) {

View File

@ -65,7 +65,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
final AttachmentUploaderController uploader = Get.find(); final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any( if (uploader.queueOfUpload.any(
((x) => x.usage == 'i.attachment' && x.isUploading), ((x) => x.isUploading),
)) { )) {
context.showErrorDialog('attachmentUploadInProgress'.tr); context.showErrorDialog('attachmentUploadInProgress'.tr);
return; return;
@ -90,8 +90,8 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
if (resp.statusCode != 200) { if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString); context.showErrorDialog(resp.bodyString);
} else { } else {
_editorController.localClear();
_editorController.currentClear(); _editorController.currentClear();
_editorController.localClear();
AppRouter.instance.pop(resp.body); AppRouter.instance.pop(resp.body);
} }

View File

@ -24,7 +24,6 @@ class AccountAvatar extends StatelessWidget {
if (content is String) { if (content is String) {
direct = content.startsWith('http'); direct = content.startsWith('http');
if (!isEmpty) isEmpty = content.isEmpty; if (!isEmpty) isEmpty = content.isEmpty;
if (!isEmpty) isEmpty = content.endsWith('/attachments/0');
} }
final url = direct final url = direct

View File

@ -16,10 +16,12 @@ class AttachmentAttrEditorDialog extends StatefulWidget {
}); });
@override @override
State<AttachmentAttrEditorDialog> createState() => _AttachmentAttrEditorDialogState(); State<AttachmentAttrEditorDialog> createState() =>
_AttachmentAttrEditorDialogState();
} }
class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog> { class _AttachmentAttrEditorDialogState
extends State<AttachmentAttrEditorDialog> {
final _altController = TextEditingController(); final _altController = TextEditingController();
bool _isBusy = false; bool _isBusy = false;
@ -33,7 +35,6 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
final resp = await provider.updateAttachment( final resp = await provider.updateAttachment(
widget.item.id, widget.item.id,
_altController.value.text, _altController.value.text,
widget.item.usage,
isMature: _isMature, isMature: _isMature,
); );
@ -109,7 +110,7 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: foregroundColor:
Theme.of(context).colorScheme.onSurfaceVariant), Theme.of(context).colorScheme.onSurfaceVariant),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: Text('cancel'.tr), child: Text('cancel'.tr),
), ),

View File

@ -22,19 +22,19 @@ import 'package:solian/widgets/attachments/attachment_attr_editor.dart';
import 'package:solian/widgets/attachments/attachment_fullscreen.dart'; import 'package:solian/widgets/attachments/attachment_fullscreen.dart';
class AttachmentEditorPopup extends StatefulWidget { class AttachmentEditorPopup extends StatefulWidget {
final String usage; final String pool;
final bool singleMode; final bool singleMode;
final bool imageOnly; final bool imageOnly;
final bool autoUpload; final bool autoUpload;
final double? imageMaxWidth; final double? imageMaxWidth;
final double? imageMaxHeight; final double? imageMaxHeight;
final List<int>? initialAttachments; final List<String>? initialAttachments;
final void Function(int) onAdd; final void Function(String) onAdd;
final void Function(int) onRemove; final void Function(String) onRemove;
const AttachmentEditorPopup({ const AttachmentEditorPopup({
super.key, super.key,
required this.usage, required this.pool,
required this.onAdd, required this.onAdd,
required this.onRemove, required this.onRemove,
this.singleMode = false, this.singleMode = false,
@ -73,7 +73,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_enqueueTaskBatch(medias.map((x) { _enqueueTaskBatch(medias.map((x) {
final file = File(x.path); final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage); return AttachmentUploadTask(file: file, usage: widget.pool);
})); }));
} else { } else {
final media = await _imagePicker.pickMedia( final media = await _imagePicker.pickMedia(
@ -83,7 +83,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (media == null) return; if (media == null) return;
_enqueueTask( _enqueueTask(
AttachmentUploadTask(file: File(media.path), usage: widget.usage), AttachmentUploadTask(file: File(media.path), usage: widget.pool),
); );
} }
} }
@ -97,7 +97,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path); final file = File(media.path);
_enqueueTask( _enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage), AttachmentUploadTask(file: file, usage: widget.pool),
); );
} }
@ -113,7 +113,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
List<File> files = result.paths.map((path) => File(path!)).toList(); List<File> files = result.paths.map((path) => File(path!)).toList();
_enqueueTaskBatch(files.map((x) { _enqueueTaskBatch(files.map((x) {
return AttachmentUploadTask(file: x, usage: widget.usage); return AttachmentUploadTask(file: x, usage: widget.pool);
})); }));
} }
@ -131,7 +131,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path); final file = File(media.path);
_enqueueTask( _enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage), AttachmentUploadTask(file: file, usage: widget.pool),
); );
} }
@ -181,13 +181,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
WidgetsBinding.instance.addPostFrameCallback((_) => controller.dispose()); WidgetsBinding.instance.addPostFrameCallback((_) => controller.dispose());
if (input == null || input.isEmpty) return; if (input == null || input.isEmpty) return;
final value = int.tryParse(input);
if (value == null) return;
final AttachmentProvider attach = Get.find(); final AttachmentProvider attach = Get.find();
final result = await attach.getMetadata(value); final result = await attach.getMetadata(input);
if (result != null) { if (result != null) {
widget.onAdd(result.id); widget.onAdd(result.rid);
setState(() => _attachments.add(result)); setState(() => _attachments.add(result));
if (widget.singleMode) Navigator.pop(context); if (widget.singleMode) Navigator.pop(context);
} }
@ -202,11 +200,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_uploadController.uploadAttachmentWithCallback( _uploadController.uploadAttachmentWithCallback(
data, data,
'Pasted Image', 'Pasted Image',
widget.usage, widget.pool,
null, null,
(item) { (item) {
if (item == null) return; if (item == null) return;
widget.onAdd(item.id); widget.onAdd(item.rid);
if (mounted) { if (mounted) {
setState(() => _attachments.add(item)); setState(() => _attachments.add(item));
if (widget.singleMode) Navigator.pop(context); if (widget.singleMode) Navigator.pop(context);
@ -413,11 +411,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
: () { : () {
_uploadController _uploadController
.performSingleTask(index) .performSingleTask(index)
.then((r) { .then((out) {
if (r == null) return; if (out == null) return;
widget.onAdd(r.id); widget.onAdd(out.rid);
if (mounted) { if (mounted) {
setState(() => _attachments.add(r)); setState(() => _attachments.add(out));
if (widget.singleMode) { if (widget.singleMode) {
Navigator.pop(context); Navigator.pop(context);
} }
@ -515,7 +513,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
), ),
onTap: () { onTap: () {
_deleteAttachment(element).then((_) { _deleteAttachment(element).then((_) {
widget.onRemove(element.id); widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index)); setState(() => _attachments.removeAt(index));
}); });
}, },
@ -529,7 +527,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
), ),
), ),
onTap: () { onTap: () {
widget.onRemove(element.id); widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index)); setState(() => _attachments.removeAt(index));
}, },
), ),
@ -560,7 +558,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
void _startUploading() { void _startUploading() {
_uploadController.performUploadQueue(onData: (r) { _uploadController.performUploadQueue(onData: (r) {
widget.onAdd(r.id); widget.onAdd(r.rid);
if (mounted) { if (mounted) {
setState(() => _attachments.add(r)); setState(() => _attachments.add(r));
if (widget.singleMode) Navigator.pop(context); if (widget.singleMode) Navigator.pop(context);
@ -584,7 +582,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (_uploadController.isUploading.value) return; if (_uploadController.isUploading.value) return;
_enqueueTaskBatch(detail.files.map((x) { _enqueueTaskBatch(detail.files.map((x) {
final file = File(x.path); final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage); return AttachmentUploadTask(file: file, usage: widget.pool);
})); }));
}, },
child: Column( child: Column(

View File

@ -67,9 +67,10 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
Future<void> _saveToAlbum() async { Future<void> _saveToAlbum() async {
final url = ServiceFinder.buildUrl( final url = ServiceFinder.buildUrl(
'files', 'files',
'/attachments/${widget.item.id}', '/attachments/${widget.item.rid}',
); );
print(url);
if (PlatformInfo.isWeb || PlatformInfo.isDesktop) { if (PlatformInfo.isWeb || PlatformInfo.isDesktop) {
await launchUrlString(url); await launchUrlString(url);
return; return;
@ -258,7 +259,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
spacing: 6, spacing: 6,
children: [ children: [
Text( Text(
'#${widget.item.id}', '#${widget.item.rid}',
style: metaTextStyle, style: metaTextStyle,
), ),
if (widget.item.metadata?['width'] != null && if (widget.item.metadata?['width'] != null &&

View File

@ -91,7 +91,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
launchUrlString( launchUrlString(
ServiceFinder.buildUrl( ServiceFinder.buildUrl(
'files', 'files',
'/attachments/${widget.item.id}', '/attachments/${widget.item.rid}',
), ),
); );
}, },
@ -135,7 +135,7 @@ class _AttachmentItemImage extends StatelessWidget {
fit: fit, fit: fit,
imageUrl: ServiceFinder.buildUrl( imageUrl: ServiceFinder.buildUrl(
'files', 'files',
'/attachments/${item.id}', '/attachments/${item.rid}',
), ),
progressIndicatorBuilder: (context, url, downloadProgress) { progressIndicatorBuilder: (context, url, downloadProgress) {
return Center( return Center(
@ -240,7 +240,7 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
final ratio = widget.item.metadata?['ratio'] ?? 16 / 9; final ratio = widget.item.metadata?['ratio'] ?? 16 / 9;
_playerController = VideoPlayerController.networkUrl( _playerController = VideoPlayerController.networkUrl(
Uri.parse( Uri.parse(
ServiceFinder.buildUrl('files', '/attachments/${widget.item.id}'), ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}'),
), ),
); );
_playerController!.initialize(); _playerController!.initialize();

View File

@ -14,7 +14,7 @@ import 'package:solian/widgets/sized_container.dart';
class AttachmentList extends StatefulWidget { class AttachmentList extends StatefulWidget {
final String parentId; final String parentId;
final List<int> attachmentsId; final List<String> attachmentsId;
final bool isGrid; final bool isGrid;
final bool isForceGrid; final bool isForceGrid;
final bool autoload; final bool autoload;
@ -334,13 +334,13 @@ class AttachmentListEntry extends StatelessWidget {
} }
class AttachmentSelfContainedEntry extends StatefulWidget { class AttachmentSelfContainedEntry extends StatefulWidget {
final int id; final String rid;
final String parentId; final String parentId;
final bool isDense; final bool isDense;
const AttachmentSelfContainedEntry({ const AttachmentSelfContainedEntry({
super.key, super.key,
required this.id, required this.rid,
required this.parentId, required this.parentId,
this.isDense = false, this.isDense = false,
}); });
@ -359,10 +359,12 @@ class _AttachmentSelfContainedEntryState
final AttachmentProvider attachments = Get.find(); final AttachmentProvider attachments = Get.find();
return FutureBuilder( return FutureBuilder(
future: attachments.getMetadata(widget.id), future: attachments.getMetadata(widget.rid),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const Text('Loading...'); return const Center(
child: CircularProgressIndicator(),
);
} }
return AttachmentListEntry( return AttachmentListEntry(

View File

@ -39,8 +39,8 @@ class ChatEvent extends StatelessWidget {
Widget _buildAttachment(BuildContext context, {bool isMinimal = false}) { Widget _buildAttachment(BuildContext context, {bool isMinimal = false}) {
final attachments = item.body['attachments'] != null final attachments = item.body['attachments'] != null
? List<int>.from(item.body['attachments'].map((x) => x)) ? List<String>.from(item.body['attachments'].map((x) => x))
: List<int>.empty(); : List<String>.empty();
if (attachments.isEmpty) return const SizedBox(); if (attachments.isEmpty) return const SizedBox();

View File

@ -59,7 +59,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final TextEditingController _textController = TextEditingController(); final TextEditingController _textController = TextEditingController();
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
final List<int> _attachments = List.empty(growable: true); final List<String> _attachments = List.empty(growable: true);
Event? _editTo; Event? _editTo;
Event? _replyTo; Event? _replyTo;
@ -68,7 +68,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'm.attachment', pool: 'messaging',
initialAttachments: _attachments, initialAttachments: _attachments,
onAdd: (value) { onAdd: (value) {
setState(() { setState(() {
@ -103,7 +103,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final AttachmentUploaderController uploader = Get.find(); final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any( if (uploader.queueOfUpload.any(
((x) => x.usage == 'm.attachment' && x.isUploading), ((x) => x.isUploading),
)) { )) {
context.showErrorDialog('attachmentUploadInProgress'.tr); context.showErrorDialog('attachmentUploadInProgress'.tr);
return; return;

View File

@ -131,7 +131,7 @@ class MarkdownTextContent extends StatelessWidget {
child: AttachmentSelfContainedEntry( child: AttachmentSelfContainedEntry(
isDense: true, isDense: true,
parentId: parentId, parentId: parentId,
id: int.parse(segments[1]), rid: segments[1],
), ),
), ),
).paddingSymmetric(vertical: 4); ).paddingSymmetric(vertical: 4);

View File

@ -20,7 +20,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment', pool: 'interactive',
singleMode: true, singleMode: true,
imageOnly: true, imageOnly: true,
autoUpload: true, autoUpload: true,
@ -84,8 +84,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
widget.controller.thumbnail.value = widget.controller.thumbnail.value = _attachmentController.text;
int.tryParse(_attachmentController.text);
Navigator.pop(context); Navigator.pop(context);
}, },
child: Text('confirm'.tr), child: Text('confirm'.tr),

View File

@ -87,7 +87,7 @@ class _PostItemState extends State<PostItem> {
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: AttachmentSelfContainedEntry( child: AttachmentSelfContainedEntry(
id: widget.item.body['thumbnail'], rid: widget.item.body['thumbnail'],
parentId: 'p${item.id}-thumbnail', parentId: 'p${item.id}-thumbnail',
), ),
), ),
@ -292,8 +292,8 @@ class _PostItemState extends State<PostItem> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<int> attachments = item.body['attachments'] is List final List<String> attachments = item.body['attachments'] is List
? item.body['attachments']?.cast<int>() ? item.body['attachments']?.cast<String>()
: List.empty(); : List.empty();
final hasAttachment = attachments.isNotEmpty; final hasAttachment = attachments.isNotEmpty;

View File

@ -29,7 +29,7 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'sticker', pool: 'sticker',
singleMode: true, singleMode: true,
imageOnly: true, imageOnly: true,
autoUpload: true, autoUpload: true,