mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
## Summary - add iOS and Android voice-note recording and preview directly in the mobile composer - add waveform playback, scrubbing, speed controls, haptics, and one-shot playback in chat - package recordings in a canonical H.264/AAC MP4 envelope on both platforms so existing relays accept them - preserve the shared composer interaction and attachment-card treatment across mobile platforms Mobile counterpart to #6978. ## Testing - `just ci` - `just mobile-check` - `just mobile-test` (2,026 tests) - Android debug build compiled, installed, launched, and Voice note verified in the attachment menu on Pixel 10 - signed iOS device build installed on iPhone --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
98 lines
2.8 KiB
Dart
98 lines
2.8 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:buzz/features/channels/message_media.dart';
|
|
|
|
void main() {
|
|
group('classifyMediaUrl', () {
|
|
test('treats only mp4 URLs as video fallback', () {
|
|
expect(
|
|
classifyMediaUrl('https://example.com/media/clip.mp4'),
|
|
MessageMediaKind.video,
|
|
);
|
|
expect(classifyMediaUrl('https://example.com/media/clip.mov'), isNull);
|
|
expect(classifyMediaUrl('https://example.com/media/clip.webm'), isNull);
|
|
});
|
|
|
|
test('uses explicit video mimetypes for the video UI', () {
|
|
expect(
|
|
classifyMediaUrl(
|
|
'https://example.com/media/clip.mov',
|
|
imeta: const ImetaEntry(
|
|
url: 'https://example.com/media/clip.mov',
|
|
mimeType: 'video/quicktime',
|
|
),
|
|
),
|
|
MessageMediaKind.video,
|
|
);
|
|
expect(
|
|
classifyMediaUrl(
|
|
'https://example.com/media/clip.mp4',
|
|
imeta: const ImetaEntry(
|
|
url: 'https://example.com/media/clip.mp4',
|
|
mimeType: 'video/mp4',
|
|
),
|
|
),
|
|
MessageMediaKind.video,
|
|
);
|
|
});
|
|
|
|
test('classifies voice notes from imeta or an audio extension', () {
|
|
expect(
|
|
classifyMediaUrl(
|
|
'https://example.com/media/blob',
|
|
imeta: const ImetaEntry(
|
|
url: 'https://example.com/media/blob',
|
|
mimeType: 'audio/mp4',
|
|
),
|
|
),
|
|
MessageMediaKind.audio,
|
|
);
|
|
expect(
|
|
classifyMediaUrl('https://example.com/media/voice-note.m4a'),
|
|
MessageMediaKind.audio,
|
|
);
|
|
expect(
|
|
classifyMediaUrl(
|
|
'https://example.com/media/blob.mp4',
|
|
imeta: const ImetaEntry(
|
|
url: 'https://example.com/media/blob.mp4',
|
|
mimeType: 'video/mp4',
|
|
filename: 'voice-note-test.mp4',
|
|
),
|
|
),
|
|
MessageMediaKind.audio,
|
|
);
|
|
});
|
|
|
|
test('rejects non-finite and negative durations', () {
|
|
for (final duration in ['NaN', 'Infinity', '-1']) {
|
|
final entry = parseImetaTags([
|
|
[
|
|
'imeta',
|
|
'url https://example.com/media/$duration',
|
|
'duration $duration',
|
|
],
|
|
]).values.single;
|
|
expect(entry.duration, isNull);
|
|
}
|
|
});
|
|
|
|
test('parses voice note duration and filename metadata', () {
|
|
final entry = parseImetaTags(const [
|
|
[
|
|
'imeta',
|
|
'url https://example.com/media/voice-note.m4a',
|
|
'm audio/mp4',
|
|
'duration 3.25',
|
|
'filename voice-note.m4a',
|
|
'size 42',
|
|
],
|
|
]).values.single;
|
|
|
|
expect(entry.duration, 3.25);
|
|
expect(entry.filename, 'voice-note.m4a');
|
|
expect(entry.size, 42);
|
|
expect(entry.isAudio, isTrue);
|
|
});
|
|
});
|
|
}
|