Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
cc1e0599aa | |||
221b97901f | |||
498bb0e5fb | |||
aa94dfcfe0 | |||
65d9253876 |
@ -1,5 +1,30 @@
|
|||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
|
||||||
|
class AttachmentPlaceholder {
|
||||||
|
int chunkCount;
|
||||||
|
int chunkSize;
|
||||||
|
Attachment meta;
|
||||||
|
|
||||||
|
AttachmentPlaceholder({
|
||||||
|
required this.chunkCount,
|
||||||
|
required this.chunkSize,
|
||||||
|
required this.meta,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory AttachmentPlaceholder.fromJson(Map<String, dynamic> json) =>
|
||||||
|
AttachmentPlaceholder(
|
||||||
|
chunkCount: json['chunk_count'],
|
||||||
|
chunkSize: json['chunk_size'],
|
||||||
|
meta: Attachment.fromJson(json['meta']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'chunk_count': chunkCount,
|
||||||
|
'chunk_size': chunkSize,
|
||||||
|
'meta': meta.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class Attachment {
|
class Attachment {
|
||||||
int id;
|
int id;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -14,7 +39,9 @@ class Attachment {
|
|||||||
String hash;
|
String hash;
|
||||||
int destination;
|
int destination;
|
||||||
bool isAnalyzed;
|
bool isAnalyzed;
|
||||||
|
bool isUploaded;
|
||||||
Map<String, dynamic>? metadata;
|
Map<String, dynamic>? metadata;
|
||||||
|
Map<String, dynamic>? fileChunks;
|
||||||
bool isMature;
|
bool isMature;
|
||||||
Account? account;
|
Account? account;
|
||||||
int? accountId;
|
int? accountId;
|
||||||
@ -33,7 +60,9 @@ class Attachment {
|
|||||||
required this.hash,
|
required this.hash,
|
||||||
required this.destination,
|
required this.destination,
|
||||||
required this.isAnalyzed,
|
required this.isAnalyzed,
|
||||||
|
required this.isUploaded,
|
||||||
required this.metadata,
|
required this.metadata,
|
||||||
|
required this.fileChunks,
|
||||||
required this.isMature,
|
required this.isMature,
|
||||||
required this.account,
|
required this.account,
|
||||||
required this.accountId,
|
required this.accountId,
|
||||||
@ -55,7 +84,9 @@ class Attachment {
|
|||||||
hash: json['hash'],
|
hash: json['hash'],
|
||||||
destination: json['destination'],
|
destination: json['destination'],
|
||||||
isAnalyzed: json['is_analyzed'],
|
isAnalyzed: json['is_analyzed'],
|
||||||
|
isUploaded: json['is_uploaded'],
|
||||||
metadata: json['metadata'],
|
metadata: json['metadata'],
|
||||||
|
fileChunks: json['file_chunks'],
|
||||||
isMature: json['is_mature'],
|
isMature: json['is_mature'],
|
||||||
account:
|
account:
|
||||||
json['account'] != null ? Account.fromJson(json['account']) : null,
|
json['account'] != null ? Account.fromJson(json['account']) : null,
|
||||||
@ -76,7 +107,9 @@ class Attachment {
|
|||||||
'hash': hash,
|
'hash': hash,
|
||||||
'destination': destination,
|
'destination': destination,
|
||||||
'is_analyzed': isAnalyzed,
|
'is_analyzed': isAnalyzed,
|
||||||
|
'is_uploaded': isUploaded,
|
||||||
'metadata': metadata,
|
'metadata': metadata,
|
||||||
|
'file_chunks': fileChunks,
|
||||||
'is_mature': isMature,
|
'is_mature': isMature,
|
||||||
'account': account?.toJson(),
|
'account': account?.toJson(),
|
||||||
'account_id': accountId,
|
'account_id': accountId,
|
||||||
|
@ -1,24 +1,27 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cross_file/cross_file.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:path/path.dart' show basename;
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/providers/content/attachment.dart';
|
import 'package:solian/providers/content/attachment.dart';
|
||||||
|
|
||||||
class AttachmentUploadTask {
|
class AttachmentUploadTask {
|
||||||
File file;
|
XFile file;
|
||||||
String usage;
|
String pool;
|
||||||
Map<String, dynamic>? metadata;
|
Map<String, dynamic>? metadata;
|
||||||
|
Map<String, int>? chunkFiles;
|
||||||
|
|
||||||
double progress = 0;
|
double? progress;
|
||||||
bool isUploading = false;
|
bool isUploading = false;
|
||||||
bool isCompleted = false;
|
bool isCompleted = false;
|
||||||
dynamic error;
|
dynamic error;
|
||||||
|
|
||||||
AttachmentUploadTask({
|
AttachmentUploadTask({
|
||||||
required this.file,
|
required this.file,
|
||||||
required this.usage,
|
required this.pool,
|
||||||
this.metadata,
|
this.metadata,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -73,32 +76,36 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
|
|
||||||
_startProgressSyncTimer();
|
_startProgressSyncTimer();
|
||||||
queueOfUpload[queueIndex].isUploading = true;
|
queueOfUpload[queueIndex].isUploading = true;
|
||||||
|
queueOfUpload[queueIndex].progress = 0;
|
||||||
|
|
||||||
final task = queueOfUpload[queueIndex];
|
final task = queueOfUpload[queueIndex];
|
||||||
final result = await _rawUploadAttachment(
|
try {
|
||||||
await task.file.readAsBytes(),
|
final result = await _chunkedUploadAttachment(
|
||||||
task.file.path,
|
task.file,
|
||||||
task.usage,
|
task.pool,
|
||||||
null,
|
null,
|
||||||
onProgress: (value) {
|
onData: (_) {},
|
||||||
queueOfUpload[queueIndex].progress = value;
|
onProgress: (progress) {
|
||||||
_progressOfUpload = value;
|
queueOfUpload[queueIndex].progress = progress;
|
||||||
},
|
_progressOfUpload = progress;
|
||||||
onError: (err) {
|
},
|
||||||
queueOfUpload[queueIndex].error = err;
|
);
|
||||||
queueOfUpload[queueIndex].isUploading = false;
|
return result;
|
||||||
},
|
} catch (err) {
|
||||||
);
|
queueOfUpload[queueIndex].error = err;
|
||||||
|
queueOfUpload[queueIndex].isUploading = false;
|
||||||
|
} finally {
|
||||||
|
_progressOfUpload = 1;
|
||||||
|
if (queueOfUpload[queueIndex].error == null) {
|
||||||
|
queueOfUpload.removeAt(queueIndex);
|
||||||
|
}
|
||||||
|
_stopProgressSyncTimer();
|
||||||
|
_syncProgress();
|
||||||
|
|
||||||
if (queueOfUpload[queueIndex].error == null) {
|
isUploading.value = false;
|
||||||
queueOfUpload.removeAt(queueIndex);
|
|
||||||
}
|
}
|
||||||
_stopProgressSyncTimer();
|
|
||||||
_syncProgress();
|
|
||||||
|
|
||||||
isUploading.value = false;
|
return null;
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> performUploadQueue({
|
Future<void> performUploadQueue({
|
||||||
@ -115,24 +122,26 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queueOfUpload[idx].isUploading = true;
|
queueOfUpload[idx].isUploading = true;
|
||||||
|
queueOfUpload[idx].progress = 0;
|
||||||
|
|
||||||
final task = queueOfUpload[idx];
|
final task = queueOfUpload[idx];
|
||||||
final result = await _rawUploadAttachment(
|
try {
|
||||||
await task.file.readAsBytes(),
|
final result = await _chunkedUploadAttachment(
|
||||||
task.file.path,
|
task.file,
|
||||||
task.usage,
|
task.pool,
|
||||||
null,
|
null,
|
||||||
onProgress: (value) {
|
onData: (_) {},
|
||||||
queueOfUpload[idx].progress = value;
|
onProgress: (progress) {
|
||||||
_progressOfUpload = (idx + value) / queueOfUpload.length;
|
queueOfUpload[idx].progress = progress;
|
||||||
},
|
},
|
||||||
onError: (err) {
|
);
|
||||||
queueOfUpload[idx].error = err;
|
if (result != null) onData(result);
|
||||||
queueOfUpload[idx].isUploading = false;
|
} catch (err) {
|
||||||
},
|
queueOfUpload[idx].error = err;
|
||||||
);
|
queueOfUpload[idx].isUploading = false;
|
||||||
_progressOfUpload = (idx + 1) / queueOfUpload.length;
|
} finally {
|
||||||
if (result != null) onData(result);
|
_progressOfUpload = (idx + 1) / queueOfUpload.length;
|
||||||
|
}
|
||||||
|
|
||||||
queueOfUpload[idx].isUploading = false;
|
queueOfUpload[idx].isUploading = false;
|
||||||
queueOfUpload[idx].isCompleted = true;
|
queueOfUpload[idx].isCompleted = true;
|
||||||
@ -145,69 +154,94 @@ class AttachmentUploaderController extends GetxController {
|
|||||||
isUploading.value = false;
|
isUploading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadAttachmentWithCallback(
|
Future<Attachment?> uploadAttachmentFromData(
|
||||||
Uint8List data,
|
|
||||||
String path,
|
|
||||||
String pool,
|
|
||||||
Map<String, dynamic>? metadata,
|
|
||||||
Function(Attachment?) callback,
|
|
||||||
) async {
|
|
||||||
if (isUploading.value) throw Exception('uploading blocked');
|
|
||||||
|
|
||||||
isUploading.value = true;
|
|
||||||
final result = await _rawUploadAttachment(
|
|
||||||
data,
|
|
||||||
path,
|
|
||||||
pool,
|
|
||||||
metadata,
|
|
||||||
onProgress: (progress) {
|
|
||||||
progressOfUpload.value = progress;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
isUploading.value = false;
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Attachment?> uploadAttachment(
|
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
String path,
|
String path,
|
||||||
String pool,
|
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');
|
||||||
|
|
||||||
isUploading.value = true;
|
isUploading.value = true;
|
||||||
final result = await _rawUploadAttachment(
|
|
||||||
data,
|
|
||||||
path,
|
|
||||||
pool,
|
|
||||||
metadata,
|
|
||||||
onProgress: (progress) {
|
|
||||||
progressOfUpload.value = progress;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
isUploading.value = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Attachment?> _rawUploadAttachment(
|
final AttachmentProvider attach = Get.find();
|
||||||
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
|
|
||||||
{Function(double)? onProgress, Function(dynamic err)? onError}) async {
|
|
||||||
final AttachmentProvider provider = Get.find();
|
|
||||||
try {
|
try {
|
||||||
final result = await provider.createAttachment(
|
final result = await attach.createAttachmentDirectly(
|
||||||
data,
|
data,
|
||||||
path,
|
path,
|
||||||
pool,
|
pool,
|
||||||
metadata,
|
metadata,
|
||||||
onProgress: onProgress,
|
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (_) {
|
||||||
if (onError != null) {
|
|
||||||
onError(err);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
|
} finally {
|
||||||
|
isUploading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Attachment?> _chunkedUploadAttachment(
|
||||||
|
XFile file,
|
||||||
|
String pool,
|
||||||
|
Map<String, dynamic>? metadata, {
|
||||||
|
required Function(AttachmentPlaceholder) onData,
|
||||||
|
required Function(double) onProgress,
|
||||||
|
}) async {
|
||||||
|
final AttachmentProvider attach = Get.find();
|
||||||
|
|
||||||
|
final holder = await attach.createAttachmentMultipartPlaceholder(
|
||||||
|
await file.length(),
|
||||||
|
file.path,
|
||||||
|
pool,
|
||||||
|
metadata,
|
||||||
|
);
|
||||||
|
onData(holder);
|
||||||
|
|
||||||
|
onProgress(0);
|
||||||
|
|
||||||
|
final filename = basename(file.path);
|
||||||
|
final chunks = holder.meta.fileChunks ?? {};
|
||||||
|
var currentTask = 0;
|
||||||
|
|
||||||
|
final queue = Queue<Future<void>>();
|
||||||
|
final activeTasks = <Future<void>>[];
|
||||||
|
|
||||||
|
for (final entry in chunks.entries) {
|
||||||
|
queue.add(() async {
|
||||||
|
final beginCursor = entry.value * holder.chunkSize;
|
||||||
|
final endCursor = (entry.value + 1) * holder.chunkSize;
|
||||||
|
final data = Uint8List.fromList(await file
|
||||||
|
.openRead(beginCursor, endCursor)
|
||||||
|
.expand((chunk) => chunk)
|
||||||
|
.toList());
|
||||||
|
|
||||||
|
final out = await attach.uploadAttachmentMultipartChunk(
|
||||||
|
data,
|
||||||
|
filename,
|
||||||
|
holder.meta.rid,
|
||||||
|
entry.key,
|
||||||
|
);
|
||||||
|
holder.meta = out;
|
||||||
|
|
||||||
|
currentTask++;
|
||||||
|
onProgress(currentTask / chunks.length);
|
||||||
|
onData(holder);
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (queue.isNotEmpty || activeTasks.isNotEmpty) {
|
||||||
|
while (activeTasks.length < 3 && queue.isNotEmpty) {
|
||||||
|
final task = queue.removeFirst();
|
||||||
|
activeTasks.add(task);
|
||||||
|
|
||||||
|
task.then((_) => activeTasks.remove(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTasks.isNotEmpty) {
|
||||||
|
await Future.any(activeTasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return holder.meta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import 'package:solian/models/attachment.dart';
|
|||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:dio/dio.dart' as dio;
|
|
||||||
|
|
||||||
class AttachmentProvider extends GetConnect {
|
class AttachmentProvider extends GetConnect {
|
||||||
static Map<String, String> mimetypeOverrides = {
|
static Map<String, String> mimetypeOverrides = {
|
||||||
@ -83,16 +82,21 @@ class AttachmentProvider extends GetConnect {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Attachment> createAttachment(
|
Future<Attachment> createAttachmentDirectly(
|
||||||
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
|
Uint8List data,
|
||||||
{Function(double)? onProgress}) async {
|
String path,
|
||||||
|
String pool,
|
||||||
|
Map<String, dynamic>? metadata,
|
||||||
|
) 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');
|
||||||
|
|
||||||
await auth.ensureCredentials();
|
final client = auth.configureClient(
|
||||||
|
'uc',
|
||||||
|
timeout: const Duration(minutes: 3),
|
||||||
|
);
|
||||||
|
|
||||||
final filePayload =
|
final filePayload = MultipartFile(data, filename: basename(path));
|
||||||
dio.MultipartFile.fromBytes(data, filename: basename(path));
|
|
||||||
final fileAlt = basename(path).contains('.')
|
final fileAlt = basename(path).contains('.')
|
||||||
? basename(path).substring(0, basename(path).lastIndexOf('.'))
|
? basename(path).substring(0, basename(path).lastIndexOf('.'))
|
||||||
: basename(path);
|
: basename(path);
|
||||||
@ -105,30 +109,82 @@ class AttachmentProvider extends GetConnect {
|
|||||||
if (mimetypeOverrides.keys.contains(fileExt)) {
|
if (mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
mimetypeOverride = mimetypeOverrides[fileExt];
|
mimetypeOverride = mimetypeOverrides[fileExt];
|
||||||
}
|
}
|
||||||
final payload = dio.FormData.fromMap({
|
final payload = FormData({
|
||||||
'alt': fileAlt,
|
'alt': fileAlt,
|
||||||
'file': filePayload,
|
'file': filePayload,
|
||||||
'pool': pool,
|
'pool': pool,
|
||||||
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
'metadata': jsonEncode(metadata),
|
'metadata': jsonEncode(metadata),
|
||||||
});
|
});
|
||||||
final resp = await dio.Dio(
|
final resp = await client.post('/attachments', payload);
|
||||||
dio.BaseOptions(
|
|
||||||
baseUrl: ServiceFinder.buildUrl('files', null),
|
|
||||||
headers: {'Authorization': 'Bearer ${auth.credentials!.accessToken}'},
|
|
||||||
),
|
|
||||||
).post(
|
|
||||||
'/attachments',
|
|
||||||
data: payload,
|
|
||||||
onSendProgress: (count, total) {
|
|
||||||
if (onProgress != null) onProgress(count / total);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (resp.statusCode != 200) {
|
if (resp.statusCode != 200) {
|
||||||
throw Exception(resp.data);
|
throw Exception(resp.bodyString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Attachment.fromJson(resp.data);
|
return Attachment.fromJson(resp.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AttachmentPlaceholder> createAttachmentMultipartPlaceholder(
|
||||||
|
int size,
|
||||||
|
String path,
|
||||||
|
String pool,
|
||||||
|
Map<String, dynamic>? metadata,
|
||||||
|
) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) throw Exception('unauthorized');
|
||||||
|
|
||||||
|
final client = auth.configureClient('uc');
|
||||||
|
|
||||||
|
final fileAlt = basename(path).contains('.')
|
||||||
|
? basename(path).substring(0, basename(path).lastIndexOf('.'))
|
||||||
|
: basename(path);
|
||||||
|
final fileExt = basename(path)
|
||||||
|
.substring(basename(path).lastIndexOf('.') + 1)
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
// Override for some files cannot be detected mimetype by server-side
|
||||||
|
String? mimetypeOverride;
|
||||||
|
if (mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
|
mimetypeOverride = mimetypeOverrides[fileExt];
|
||||||
|
}
|
||||||
|
final resp = await client.post('/attachments/multipart', {
|
||||||
|
'alt': fileAlt,
|
||||||
|
'name': basename(path),
|
||||||
|
'size': size,
|
||||||
|
'pool': pool,
|
||||||
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
|
'metadata': metadata,
|
||||||
|
});
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw Exception(resp.bodyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AttachmentPlaceholder.fromJson(resp.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Attachment> uploadAttachmentMultipartChunk(
|
||||||
|
Uint8List data,
|
||||||
|
String name,
|
||||||
|
String rid,
|
||||||
|
String cid,
|
||||||
|
) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (auth.isAuthorized.isFalse) throw Exception('unauthorized');
|
||||||
|
|
||||||
|
final client = auth.configureClient(
|
||||||
|
'uc',
|
||||||
|
timeout: const Duration(minutes: 3),
|
||||||
|
);
|
||||||
|
|
||||||
|
final payload = FormData({
|
||||||
|
'file': MultipartFile(data, filename: name),
|
||||||
|
});
|
||||||
|
final resp = await client.post('/attachments/multipart/$rid/$cid', payload);
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw Exception(resp.bodyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Attachment.fromJson(resp.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response> updateAttachment(
|
Future<Response> updateAttachment(
|
||||||
|
@ -113,7 +113,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
|||||||
|
|
||||||
Attachment? attachResult;
|
Attachment? attachResult;
|
||||||
try {
|
try {
|
||||||
attachResult = await provider.createAttachment(
|
attachResult = await provider.createAttachmentDirectly(
|
||||||
await file.readAsBytes(),
|
await file.readAsBytes(),
|
||||||
file.path,
|
file.path,
|
||||||
'avatar',
|
'avatar',
|
||||||
|
@ -174,7 +174,7 @@ const i18nEnglish = {
|
|||||||
'attachmentAttached': 'Exists Files',
|
'attachmentAttached': 'Exists Files',
|
||||||
'attachmentUploadBlocked':
|
'attachmentUploadBlocked':
|
||||||
'Upload blocked, there is currently a task in progress...',
|
'Upload blocked, there is currently a task in progress...',
|
||||||
'attachmentAdd': 'Attach attachments',
|
'attachmentAdd': 'Attach file',
|
||||||
'attachmentAddGalleryPhoto': 'Gallery photo',
|
'attachmentAddGalleryPhoto': 'Gallery photo',
|
||||||
'attachmentAddGalleryVideo': 'Gallery video',
|
'attachmentAddGalleryVideo': 'Gallery video',
|
||||||
'attachmentAddCameraPhoto': 'Capture photo',
|
'attachmentAddCameraPhoto': 'Capture photo',
|
||||||
|
@ -64,7 +64,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse) return;
|
if (auth.isAuthorized.isFalse) return;
|
||||||
|
|
||||||
if (widget.singleMode) {
|
if (!widget.singleMode) {
|
||||||
final medias = await _imagePicker.pickMultiImage(
|
final medias = await _imagePicker.pickMultiImage(
|
||||||
maxWidth: widget.imageMaxWidth,
|
maxWidth: widget.imageMaxWidth,
|
||||||
maxHeight: widget.imageMaxHeight,
|
maxHeight: widget.imageMaxHeight,
|
||||||
@ -72,8 +72,8 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
if (medias.isEmpty) return;
|
if (medias.isEmpty) return;
|
||||||
|
|
||||||
_enqueueTaskBatch(medias.map((x) {
|
_enqueueTaskBatch(medias.map((x) {
|
||||||
final file = File(x.path);
|
final file = XFile(x.path);
|
||||||
return AttachmentUploadTask(file: file, usage: widget.pool);
|
return AttachmentUploadTask(file: file, pool: 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.pool),
|
AttachmentUploadTask(file: XFile(media.path), pool: widget.pool),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,9 +95,8 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
final media = await _imagePicker.pickVideo(source: ImageSource.gallery);
|
final media = await _imagePicker.pickVideo(source: ImageSource.gallery);
|
||||||
if (media == null) return;
|
if (media == null) return;
|
||||||
|
|
||||||
final file = File(media.path);
|
|
||||||
_enqueueTask(
|
_enqueueTask(
|
||||||
AttachmentUploadTask(file: file, usage: widget.pool),
|
AttachmentUploadTask(file: XFile(media.path), pool: widget.pool),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +112,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.pool);
|
return AttachmentUploadTask(file: XFile(x.path), pool: widget.pool);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,9 +128,8 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
}
|
}
|
||||||
if (media == null) return;
|
if (media == null) return;
|
||||||
|
|
||||||
final file = File(media.path);
|
|
||||||
_enqueueTask(
|
_enqueueTask(
|
||||||
AttachmentUploadTask(file: file, usage: widget.pool),
|
AttachmentUploadTask(file: XFile(media.path), pool: widget.pool),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,20 +195,16 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
|
|
||||||
if (_uploadController.isUploading.value) return;
|
if (_uploadController.isUploading.value) return;
|
||||||
|
|
||||||
_uploadController.uploadAttachmentWithCallback(
|
_uploadController
|
||||||
data,
|
.uploadAttachmentFromData(data, 'Pasted Image', widget.pool, null)
|
||||||
'Pasted Image',
|
.then((item) {
|
||||||
widget.pool,
|
if (item == null) return;
|
||||||
null,
|
widget.onAdd(item.rid);
|
||||||
(item) {
|
if (mounted) {
|
||||||
if (item == null) return;
|
setState(() => _attachments.add(item));
|
||||||
widget.onAdd(item.rid);
|
if (widget.singleMode) Navigator.pop(context);
|
||||||
if (mounted) {
|
}
|
||||||
setState(() => _attachments.add(item));
|
});
|
||||||
if (widget.singleMode) Navigator.pop(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatBytes(int bytes, {int decimals = 2}) {
|
String _formatBytes(int bytes, {int decimals = 2}) {
|
||||||
@ -304,7 +298,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (croppedFile == null) return;
|
if (croppedFile == null) return;
|
||||||
_uploadController.queueOfUpload[queueIndex].file = File(croppedFile.path);
|
_uploadController.queueOfUpload[queueIndex].file = XFile(croppedFile.path);
|
||||||
_uploadController.queueOfUpload.refresh();
|
_uploadController.queueOfUpload.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,9 +341,25 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Row(
|
||||||
'In queue #${index + 1}',
|
children: [
|
||||||
style: const TextStyle(fontSize: 12),
|
FutureBuilder(
|
||||||
|
future: element.file.length(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (!snapshot.hasData) return const SizedBox();
|
||||||
|
return Text(
|
||||||
|
_formatBytes(snapshot.data!),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
if (element.progress != null)
|
||||||
|
Text(
|
||||||
|
'${(element.progress! * 100).toStringAsFixed(2)}%',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -581,8 +591,8 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
onDragDone: (detail) async {
|
onDragDone: (detail) async {
|
||||||
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 = XFile(x.path);
|
||||||
return AttachmentUploadTask(file: file, usage: widget.pool);
|
return AttachmentUploadTask(file: file, pool: widget.pool);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -596,15 +606,13 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Text(
|
||||||
child: Text(
|
'attachmentAdd'.tr,
|
||||||
'attachmentAdd'.tr,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
style:
|
maxLines: 1,
|
||||||
Theme.of(context).textTheme.headlineSmall,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:solian/platform.dart';
|
import 'package:solian/platform.dart';
|
||||||
import 'package:solian/providers/link_expander.dart';
|
import 'package:solian/providers/link_expander.dart';
|
||||||
@ -12,6 +13,9 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
const LinkExpansion({super.key, required this.content});
|
const LinkExpansion({super.key, required this.content});
|
||||||
|
|
||||||
Widget _buildImage(String url, {double? width, double? height}) {
|
Widget _buildImage(String url, {double? width, double? height}) {
|
||||||
|
if (url.endsWith('svg')) {
|
||||||
|
return SvgPicture.network(url, width: width, height: height);
|
||||||
|
}
|
||||||
return PlatformInfo.canCacheImage
|
return PlatformInfo.canCacheImage
|
||||||
? CachedNetworkImage(imageUrl: url, width: width, height: height)
|
? CachedNetworkImage(imageUrl: url, width: width, height: height)
|
||||||
: Image.network(url, width: width, height: height);
|
: Image.network(url, width: width, height: height);
|
||||||
@ -20,10 +24,7 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final linkRegex = RegExp(
|
final linkRegex = RegExp(
|
||||||
r'(?:(?:https?|ftp):\/\/|www\.)'
|
r'(?<!\()(?:(?:https?):\/\/|www\.)(?:[-_a-z0-9]+\.)*(?:[-a-z0-9]+\.[-a-z0-9]+)[^\s<]*[^\s<?!.,:*_~]',
|
||||||
r'(?:[-_a-z0-9]+\.)*(?:[-a-z0-9]+\.[-a-z0-9]+)'
|
|
||||||
r'[^\s<]*'
|
|
||||||
r'[^\s<?!.,:*_~]',
|
|
||||||
);
|
);
|
||||||
final matches = linkRegex.allMatches(content);
|
final matches = linkRegex.allMatches(content);
|
||||||
if (matches.isEmpty) {
|
if (matches.isEmpty) {
|
||||||
@ -46,7 +47,7 @@ class LinkExpansion extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final isRichDescription = [
|
final isRichDescription = [
|
||||||
"solsynth.dev",
|
'solsynth.dev',
|
||||||
].contains(Uri.parse(snapshot.data!.url).host);
|
].contains(Uri.parse(snapshot.data!.url).host);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
@ -163,6 +163,12 @@ PODS:
|
|||||||
- WebRTC-SDK (= 125.6422.04)
|
- WebRTC-SDK (= 125.6422.04)
|
||||||
- macos_window_utils (1.0.0):
|
- macos_window_utils (1.0.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- media_kit_libs_macos_video (1.0.4):
|
||||||
|
- FlutterMacOS
|
||||||
|
- media_kit_native_event_loop (1.0.0):
|
||||||
|
- FlutterMacOS
|
||||||
|
- media_kit_video (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- nanopb (2.30910.0):
|
- nanopb (2.30910.0):
|
||||||
- nanopb/decode (= 2.30910.0)
|
- nanopb/decode (= 2.30910.0)
|
||||||
- nanopb/encode (= 2.30910.0)
|
- nanopb/encode (= 2.30910.0)
|
||||||
@ -180,6 +186,8 @@ PODS:
|
|||||||
- PromisesObjC (= 2.4.0)
|
- PromisesObjC (= 2.4.0)
|
||||||
- protocol_handler_macos (0.0.1):
|
- protocol_handler_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- screen_brightness_macos (0.1.0):
|
||||||
|
- FlutterMacOS
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
@ -190,9 +198,6 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- video_player_avfoundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- WebRTC-SDK (125.6422.04)
|
- WebRTC-SDK (125.6422.04)
|
||||||
@ -212,15 +217,18 @@ DEPENDENCIES:
|
|||||||
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
||||||
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
||||||
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
|
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
|
||||||
|
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
||||||
|
- media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`)
|
||||||
|
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
|
||||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- protocol_handler_macos (from `Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos`)
|
- protocol_handler_macos (from `Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos`)
|
||||||
|
- screen_brightness_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos`)
|
||||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
|
|
||||||
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@ -272,6 +280,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
||||||
macos_window_utils:
|
macos_window_utils:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
|
||||||
|
media_kit_libs_macos_video:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
|
||||||
|
media_kit_native_event_loop:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos
|
||||||
|
media_kit_video:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
pasteboard:
|
pasteboard:
|
||||||
@ -280,6 +294,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||||
protocol_handler_macos:
|
protocol_handler_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos
|
||||||
|
screen_brightness_macos:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@ -288,8 +304,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
video_player_avfoundation:
|
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
|
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
||||||
|
|
||||||
@ -321,6 +335,9 @@ SPEC CHECKSUMS:
|
|||||||
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
||||||
livekit_client: 95f3b71e6545845aa658a6df0a3a62dcc3471d7c
|
livekit_client: 95f3b71e6545845aa658a6df0a3a62dcc3471d7c
|
||||||
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
|
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
|
||||||
|
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
|
||||||
|
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
|
||||||
|
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5
|
||||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||||
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
||||||
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
||||||
@ -328,11 +345,11 @@ SPEC CHECKSUMS:
|
|||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa
|
protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa
|
||||||
|
screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda
|
||||||
share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf
|
share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
|
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
|
||||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
|
||||||
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
|
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
|
||||||
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3
|
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3
|
||||||
|
|
||||||
|
42
pubspec.lock
42
pubspec.lock
@ -263,7 +263,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
cross_file:
|
cross_file:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||||
@ -787,6 +787,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.7.0"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.10+1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -1293,6 +1301,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1978,6 +1994,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.2"
|
version: "4.4.2"
|
||||||
|
vector_graphics:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics
|
||||||
|
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.11+1"
|
||||||
|
vector_graphics_codec:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_codec
|
||||||
|
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.11+1"
|
||||||
|
vector_graphics_compiler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_graphics_compiler
|
||||||
|
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.11+1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -2,7 +2,7 @@ name: solian
|
|||||||
description: "The Solar Network App"
|
description: "The Solar Network App"
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.2.1+19
|
version: 1.2.1+20
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.4 <4.0.0"
|
sdk: ">=3.3.4 <4.0.0"
|
||||||
@ -71,6 +71,8 @@ dependencies:
|
|||||||
media_kit: ^1.1.10+1
|
media_kit: ^1.1.10+1
|
||||||
media_kit_video: ^1.2.4
|
media_kit_video: ^1.2.4
|
||||||
media_kit_libs_video: ^1.0.4
|
media_kit_libs_video: ^1.0.4
|
||||||
|
flutter_svg: ^2.0.10+1
|
||||||
|
cross_file: ^0.3.4+2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user