Sync update: 2026-05-18 21:13:35
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -152,11 +153,13 @@ class MessageBubble extends StatelessWidget {
|
|||||||
Widget _buildAckIcon(int ack) {
|
Widget _buildAckIcon(int ack) {
|
||||||
switch (ack) {
|
switch (ack) {
|
||||||
case 1: // Pending/Queued
|
case 1: // Pending/Queued
|
||||||
return const Icon(Icons.access_time, size: 13, color: AppTheme.textSecondary);
|
return const Icon(Icons.access_time,
|
||||||
|
size: 13, color: AppTheme.textSecondary);
|
||||||
case 2: // Sent (single grey tick)
|
case 2: // Sent (single grey tick)
|
||||||
return const Icon(Icons.done, size: 15, color: AppTheme.textSecondary);
|
return const Icon(Icons.done, size: 15, color: AppTheme.textSecondary);
|
||||||
case 3: // Delivered (double grey tick)
|
case 3: // Delivered (double grey tick)
|
||||||
return const Icon(Icons.done_all, size: 15, color: AppTheme.textSecondary);
|
return const Icon(Icons.done_all,
|
||||||
|
size: 15, color: AppTheme.textSecondary);
|
||||||
case 4: // Read (double blue tick)
|
case 4: // Read (double blue tick)
|
||||||
return const Icon(Icons.done_all, size: 15, color: Colors.blue);
|
return const Icon(Icons.done_all, size: 15, color: Colors.blue);
|
||||||
case 5: // Played audio/video (double blue with wave icon)
|
case 5: // Played audio/video (double blue with wave icon)
|
||||||
@@ -250,7 +253,8 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
await _player.pause();
|
await _player.pause();
|
||||||
} else {
|
} else {
|
||||||
final bytes = base64Decode(base64Data);
|
final bytes = base64Decode(base64Data);
|
||||||
final safeId = widget.message.id.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '');
|
final safeId =
|
||||||
|
widget.message.id.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '');
|
||||||
final tempDir = Directory.systemTemp;
|
final tempDir = Directory.systemTemp;
|
||||||
final tempFile = File('${tempDir.path}/voice_$safeId.mp3');
|
final tempFile = File('${tempDir.path}/voice_$safeId.mp3');
|
||||||
|
|
||||||
@@ -293,7 +297,8 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
child: const SizedBox(
|
child: const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
child: CircularProgressIndicator(strokeWidth: 2, color: AppTheme.primary),
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2, color: AppTheme.primary),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -318,12 +323,16 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_getLabel(),
|
_getLabel(),
|
||||||
style: const TextStyle(color: AppTheme.textPrimary, fontWeight: FontWeight.w500, fontSize: 13),
|
style: const TextStyle(
|
||||||
|
color: AppTheme.textPrimary,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 13),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
const Text(
|
const Text(
|
||||||
'Tap to download',
|
'Tap to download',
|
||||||
style: TextStyle(color: AppTheme.textSecondary, fontSize: 10),
|
style:
|
||||||
|
TextStyle(color: AppTheme.textSecondary, fontSize: 10),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -397,7 +406,9 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
|
_isPlaying
|
||||||
|
? Icons.pause_circle_filled
|
||||||
|
: Icons.play_circle_filled,
|
||||||
color: AppTheme.primary,
|
color: AppTheme.primary,
|
||||||
size: 36,
|
size: 36,
|
||||||
),
|
),
|
||||||
@@ -413,8 +424,10 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
SliderTheme(
|
SliderTheme(
|
||||||
data: SliderTheme.of(context).copyWith(
|
data: SliderTheme.of(context).copyWith(
|
||||||
trackHeight: 2.5,
|
trackHeight: 2.5,
|
||||||
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 5),
|
thumbShape:
|
||||||
overlayShape: const RoundSliderOverlayShape(overlayRadius: 10),
|
const RoundSliderThumbShape(enabledThumbRadius: 5),
|
||||||
|
overlayShape:
|
||||||
|
const RoundSliderOverlayShape(overlayRadius: 10),
|
||||||
activeTrackColor: AppTheme.primary,
|
activeTrackColor: AppTheme.primary,
|
||||||
inactiveTrackColor: AppTheme.surfaceLight,
|
inactiveTrackColor: AppTheme.surfaceLight,
|
||||||
thumbColor: AppTheme.primary,
|
thumbColor: AppTheme.primary,
|
||||||
@@ -423,7 +436,8 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
child: Slider(
|
child: Slider(
|
||||||
value: _audioProgress.clamp(0.0, 1.0),
|
value: _audioProgress.clamp(0.0, 1.0),
|
||||||
onChanged: (v) async {
|
onChanged: (v) async {
|
||||||
final targetMs = (v * _audioDurationSeconds * 1000).toInt();
|
final targetMs =
|
||||||
|
(v * _audioDurationSeconds * 1000).toInt();
|
||||||
await _player.seek(Duration(milliseconds: targetMs));
|
await _player.seek(Duration(milliseconds: targetMs));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -457,11 +471,13 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.check_circle_outline, color: AppTheme.primary, size: 32),
|
const Icon(Icons.check_circle_outline,
|
||||||
|
color: AppTheme.primary, size: 32),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(
|
Text(
|
||||||
_getLabel(),
|
_getLabel(),
|
||||||
style: const TextStyle(color: AppTheme.textPrimary, fontWeight: FontWeight.w500),
|
style: const TextStyle(
|
||||||
|
color: AppTheme.textPrimary, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -470,21 +486,31 @@ class _InteractiveMediaWidgetState extends State<InteractiveMediaWidget> {
|
|||||||
|
|
||||||
IconData _getIcon() {
|
IconData _getIcon() {
|
||||||
switch (widget.message.type) {
|
switch (widget.message.type) {
|
||||||
case "image": return Icons.photo_outlined;
|
case "image":
|
||||||
case "video": return Icons.videocam_outlined;
|
return Icons.photo_outlined;
|
||||||
case "audio": return Icons.audiotrack_outlined;
|
case "video":
|
||||||
case "sticker": return Icons.emoji_emotions_outlined;
|
return Icons.videocam_outlined;
|
||||||
default: return Icons.insert_drive_file_outlined;
|
case "audio":
|
||||||
|
return Icons.audiotrack_outlined;
|
||||||
|
case "sticker":
|
||||||
|
return Icons.emoji_emotions_outlined;
|
||||||
|
default:
|
||||||
|
return Icons.insert_drive_file_outlined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getLabel() {
|
String _getLabel() {
|
||||||
switch (widget.message.type) {
|
switch (widget.message.type) {
|
||||||
case "image": return "Image Attachment";
|
case "image":
|
||||||
case "video": return "Video Attachment";
|
return "Image Attachment";
|
||||||
case "audio": return "Audio / Voice Note";
|
case "video":
|
||||||
case "sticker": return "Sticker Attachment";
|
return "Video Attachment";
|
||||||
default: return "File Attachment";
|
case "audio":
|
||||||
|
return "Audio / Voice Note";
|
||||||
|
case "sticker":
|
||||||
|
return "Sticker Attachment";
|
||||||
|
default:
|
||||||
|
return "File Attachment";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user