Fix uploaded assets getting deleted and fix new audio messages loading wrong file

This commit is contained in:
Nutcake 2023-05-28 16:38:59 +02:00
parent c368fb6fe5
commit 9ab4774f34
5 changed files with 80 additions and 37 deletions

View file

@ -141,6 +141,7 @@ class RecordApi {
filename: filename, filename: filename,
thumbnailUri: imageDigest.dbUri, thumbnailUri: imageDigest.dbUri,
digests: digests, digests: digests,
extraTags: ["image"],
); );
progressCallback?.call(.1); progressCallback?.call(.1);
final status = await tryPreprocessRecord(client, record: record); final status = await tryPreprocessRecord(client, record: record);
@ -151,6 +152,7 @@ class RecordApi {
client, client,
assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(), assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(),
progressCallback: (progress) => progressCallback?.call(.2 + progress * .6)); progressCallback: (progress) => progressCallback?.call(.2 + progress * .6));
await upsertRecord(client, record: record);
progressCallback?.call(1); progressCallback?.call(1);
return record; return record;
} }
@ -170,6 +172,7 @@ class RecordApi {
filename: filename, filename: filename,
thumbnailUri: "", thumbnailUri: "",
digests: digests, digests: digests,
extraTags: ["voice", "message"],
); );
progressCallback?.call(.1); progressCallback?.call(.1);
final status = await tryPreprocessRecord(client, record: record); final status = await tryPreprocessRecord(client, record: record);
@ -180,6 +183,7 @@ class RecordApi {
client, client,
assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(), assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(),
progressCallback: (progress) => progressCallback?.call(.2 + progress * .6)); progressCallback: (progress) => progressCallback?.call(.2 + progress * .6));
await upsertRecord(client, record: record);
progressCallback?.call(1); progressCallback?.call(1);
return record; return record;
} }
@ -203,6 +207,7 @@ class RecordApi {
filename: fileDigest.name, filename: fileDigest.name,
thumbnailUri: JsonTemplate.thumbUrl, thumbnailUri: JsonTemplate.thumbUrl,
digests: digests, digests: digests,
extraTags: ["document"],
); );
progressCallback?.call(.1); progressCallback?.call(.1);
final status = await tryPreprocessRecord(client, record: record); final status = await tryPreprocessRecord(client, record: record);
@ -213,6 +218,7 @@ class RecordApi {
client, client,
assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(), assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(),
progressCallback: (progress) => progressCallback?.call(.2 + progress * .6)); progressCallback: (progress) => progressCallback?.call(.2 + progress * .6));
await upsertRecord(client, record: record);
progressCallback?.call(1); progressCallback?.call(1);
return record; return record;
} }

View file

@ -111,6 +111,7 @@ class Record {
required String filename, required String filename,
required String thumbnailUri, required String thumbnailUri,
required List<AssetDigest> digests, required List<AssetDigest> digests,
List<String>? extraTags,
}) { }) {
final combinedRecordId = RecordId(id: Record.generateId(), ownerId: userId, isValid: true); final combinedRecordId = RecordId(id: Record.generateId(), ownerId: userId, isValid: true);
return Record( return Record(
@ -118,11 +119,12 @@ class Record {
combinedRecordId: combinedRecordId, combinedRecordId: combinedRecordId,
assetUri: assetUri, assetUri: assetUri,
name: filename, name: filename,
tags: [ tags: ([
filename, filename,
"message_item", "message_item",
"message_id:${Message.generateId()}" "message_id:${Message.generateId()}",
], "contacts-plus-plus"
] + (extraTags ?? [])).unique(),
recordType: recordType, recordType: recordType,
thumbnailUri: thumbnailUri, thumbnailUri: thumbnailUri,
isPublic: false, isPublic: false,

View file

@ -20,39 +20,46 @@ class MessageAsset extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final content = jsonDecode(message.content); final content = jsonDecode(message.content);
PhotoAsset? photoAsset;
try {
photoAsset = PhotoAsset.fromTags((content["tags"] as List).map((e) => "$e").toList());
} catch (_) {}
final formattedName = FormatNode.fromText(content["name"]); final formattedName = FormatNode.fromText(content["name"]);
return Container( return Container(
constraints: const BoxConstraints(maxWidth: 300), constraints: const BoxConstraints(maxWidth: 300),
child: Column( child: Column(
children: [ children: [
CachedNetworkImage( SizedBox(
imageUrl: Aux.neosDbToHttp(content["thumbnailUri"]), height: 256,
imageBuilder: (context, image) { width: double.infinity,
return InkWell( child: CachedNetworkImage(
onTap: () async { imageUrl: Aux.neosDbToHttp(content["thumbnailUri"]),
await Navigator.push( imageBuilder: (context, image) {
context, MaterialPageRoute(builder: (context) => return InkWell(
PhotoView( onTap: () async {
minScale: PhotoViewComputedScale.contained, PhotoAsset? photoAsset;
imageProvider: photoAsset == null try {
? image photoAsset = PhotoAsset.fromTags((content["tags"] as List).map((e) => "$e").toList());
: CachedNetworkImageProvider(Aux.neosDbToHttp(photoAsset.imageUri)), } catch (_) {}
heroAttributes: PhotoViewHeroAttributes(tag: message.id), await Navigator.push(
), context, MaterialPageRoute(builder: (context) =>
),); PhotoView(
}, minScale: PhotoViewComputedScale.contained,
child: Hero( imageProvider: photoAsset == null
tag: message.id, ? image
child: ClipRRect(borderRadius: BorderRadius.circular(16), child: Image(image: image,)), : CachedNetworkImageProvider(Aux.neosDbToHttp(photoAsset.imageUri)),
), heroAttributes: PhotoViewHeroAttributes(tag: message.id),
); ),
}, ),);
errorWidget: (context, url, error) => const Icon(Icons.image_not_supported, size: 128,), },
placeholder: (context, uri) => const CircularProgressIndicator(), child: Hero(
tag: message.id,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image(image: image, fit: BoxFit.cover,),
),
),
);
},
errorWidget: (context, url, error) => const Icon(Icons.broken_image, size: 64,),
placeholder: (context, uri) => const Center(child: CircularProgressIndicator()),
),
), ),
const SizedBox(height: 8,), const SizedBox(height: 8,),
Row( Row(

View file

@ -45,6 +45,14 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> with WidgetsBin
.then((value) => _audioPlayer.setFilePath(value.path)).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off)); .then((value) => _audioPlayer.setFilePath(value.path)).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
} }
@override
void didUpdateWidget(covariant MessageAudioPlayer oldWidget) {
super.didUpdateWidget(oldWidget);
final audioCache = Provider.of<AudioCacheClient>(context);
_audioFileFuture = audioCache.cachedNetworkAudioFile(AudioClipContent.fromMap(jsonDecode(widget.message.content)))
.then((value) => _audioPlayer.setFilePath(value.path)).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
}
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);

View file

@ -47,6 +47,13 @@ class _MessageInputBarState extends State<MessageInputBar> {
set _isRecording(value) => _recordingStartTime = value ? DateTime.now() : null; set _isRecording(value) => _recordingStartTime = value ? DateTime.now() : null;
bool _recordingCancelled = false; bool _recordingCancelled = false;
@override
void dispose() {
_recorder.dispose();
_messageTextController.dispose();
super.dispose();
}
Future<void> sendTextMessage(ApiClient client, MessagingClient mClient, String content) async { Future<void> sendTextMessage(ApiClient client, MessagingClient mClient, String content) async {
if (content.isEmpty) return; if (content.isEmpty) return;
final message = Message( final message = Message(
@ -160,7 +167,6 @@ class _MessageInputBarState extends State<MessageInputBar> {
if (_isRecording) { if (_isRecording) {
if (_recordingCancelled) { if (_recordingCancelled) {
setState(() { setState(() {
_recordingCancelled = false;
_isRecording = false; _isRecording = false;
}); });
final recording = await _recorder.stop(); final recording = await _recorder.stop();
@ -319,7 +325,9 @@ class _MessageInputBarState extends State<MessageInputBar> {
), ),
child: switch((_attachmentPickerOpen, _isRecording)) { child: switch((_attachmentPickerOpen, _isRecording)) {
(_, true) => IconButton( (_, true) => IconButton(
onPressed: () {}, onPressed: () {
},
icon: Icon(Icons.delete, color: _recordingCancelled ? Theme.of(context).colorScheme.error : null,), icon: Icon(Icons.delete, color: _recordingCancelled ? Theme.of(context).colorScheme.error : null,),
), ),
(false, _) => IconButton( (false, _) => IconButton(
@ -513,13 +521,25 @@ class _MessageInputBarState extends State<MessageInputBar> {
}, },
icon: const Icon(Icons.send), icon: const Icon(Icons.send),
) : GestureDetector( ) : GestureDetector(
onTapUp: (_) {
_recordingCancelled = true;
},
onTapDown: widget.disabled ? null : (_) async { onTapDown: widget.disabled ? null : (_) async {
HapticFeedback.vibrate(); HapticFeedback.vibrate();
if (!await _recorder.hasPermission()) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("No permission to record audio."),
));
}
return;
}
final dir = await getTemporaryDirectory(); final dir = await getTemporaryDirectory();
await _recorder.start( await _recorder.start(
path: "${dir.path}/A-${const Uuid().v4()}.ogg", path: "${dir.path}/A-${const Uuid().v4()}.wav",
encoder: AudioEncoder.opus, encoder: AudioEncoder.wav,
samplingRate: 44100, samplingRate: 44100
); );
setState(() { setState(() {
_isRecording = true; _isRecording = true;
@ -527,7 +547,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
}, },
child: IconButton( child: IconButton(
icon: const Icon(Icons.mic_outlined), icon: const Icon(Icons.mic_outlined),
onPressed: () { onPressed: _isSending ? null : () {
// Empty onPressed for that sweet sweet ripple effect // Empty onPressed for that sweet sweet ripple effect
}, },
), ),