feat: add MessageService for sending SMS and integrate into ContactModal
All checks were successful
/ mirror (push) Successful in 5s
/ build (push) Successful in 10m40s
/ build-stealth (push) Successful in 10m34s

This commit is contained in:
AlexisDanlos 2025-05-11 12:11:32 +02:00
parent afa0c5b5a4
commit 4ecf7c546b
2 changed files with 43 additions and 32 deletions

View File

@ -0,0 +1,18 @@
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:url_launcher/url_launcher.dart';
/// A service to handle sending SMS messages using the url_launcher package.
class MessageService {
/// Launches the SMS dialer for the given [phoneNumber].
Future<void> sendSms(String phoneNumber) async {
// Sanitize number (keep only digits and plus sign)
final sanitized = phoneNumber.replaceAll(RegExp(r'[^0-9+]'), '');
Uri uri;
uri = Uri(scheme: 'sms', path: sanitized);
await launchUrl(uri);
}
}

View File

@ -7,6 +7,7 @@ import '../../../../domain/services/obfuscate_service.dart';
import '../../../../domain/services/block_service.dart'; import '../../../../domain/services/block_service.dart';
import '../../../../domain/services/contact_service.dart'; import '../../../../domain/services/contact_service.dart';
import '../../../../domain/services/call_service.dart'; import '../../../../domain/services/call_service.dart';
import '../../../../domain/services/message_service.dart';
class ContactModal extends StatefulWidget { class ContactModal extends StatefulWidget {
final Contact contact; final Contact contact;
@ -32,6 +33,7 @@ class _ContactModalState extends State<ContactModal> {
final ObfuscateService _obfuscateService = ObfuscateService(); final ObfuscateService _obfuscateService = ObfuscateService();
final CallService _callService = CallService(); final CallService _callService = CallService();
final ContactService _contactService = ContactService(); final ContactService _contactService = ContactService();
final MessageService _messageService = MessageService();
@override @override
void initState() { void initState() {
@ -60,7 +62,7 @@ class _ContactModalState extends State<ContactModal> {
); );
return; return;
} }
if (isBlocked) { if (isBlocked) {
await BlockService().unblockNumber(phoneNumber); await BlockService().unblockNumber(phoneNumber);
if (mounted) { if (mounted) {
@ -80,30 +82,12 @@ class _ContactModalState extends State<ContactModal> {
if (phoneNumber != 'No phone number' && mounted) { if (phoneNumber != 'No phone number' && mounted) {
_checkIfBlocked(); _checkIfBlocked();
} }
if (mounted) { if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
} }
void _launchPhoneDialer(String phoneNumber) async {
final uri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
debugPrint('Could not launch $phoneNumber');
}
}
void _launchSms(String phoneNumber) async {
final uri = Uri(scheme: 'sms', path: phoneNumber);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
debugPrint('Could not launch SMS to $phoneNumber');
}
}
void _launchEmail(String email) async { void _launchEmail(String email) async {
final uri = Uri(scheme: 'mailto', path: email); final uri = Uri(scheme: 'mailto', path: email);
if (await canLaunchUrl(uri)) { if (await canLaunchUrl(uri)) {
@ -246,7 +230,8 @@ class _ContactModalState extends State<ContactModal> {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
children: [ children: [
widget.contact.thumbnail != null && widget.contact.thumbnail!.isNotEmpty widget.contact.thumbnail != null &&
widget.contact.thumbnail!.isNotEmpty
? ClipOval( ? ClipOval(
child: Image.memory( child: Image.memory(
widget.contact.thumbnail!, widget.contact.thumbnail!,
@ -260,7 +245,8 @@ class _ContactModalState extends State<ContactModal> {
radius: 50, radius: 50,
child: Text( child: Text(
widget.contact.displayName.isNotEmpty widget.contact.displayName.isNotEmpty
? widget.contact.displayName[0].toUpperCase() ? widget.contact.displayName[0]
.toUpperCase()
: '?', : '?',
style: TextStyle( style: TextStyle(
color: darken(avatarColor), color: darken(avatarColor),
@ -287,7 +273,8 @@ class _ContactModalState extends State<ContactModal> {
child: Column( child: Column(
children: [ children: [
ListTile( ListTile(
leading: const Icon(Icons.phone, color: Colors.green), leading:
const Icon(Icons.phone, color: Colors.green),
title: Text( title: Text(
_obfuscateService.obfuscateData(phoneNumber), _obfuscateService.obfuscateData(phoneNumber),
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
@ -300,19 +287,21 @@ class _ContactModalState extends State<ContactModal> {
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.message, color: Colors.blue), leading:
const Icon(Icons.message, color: Colors.blue),
title: Text( title: Text(
_obfuscateService.obfuscateData(phoneNumber), _obfuscateService.obfuscateData(phoneNumber),
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
onTap: () { onTap: () {
if (widget.contact.phones.isNotEmpty) { if (widget.contact.phones.isNotEmpty) {
_launchSms(phoneNumber); _messageService.sendSms(phoneNumber);
} }
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.email, color: Colors.orange), leading:
const Icon(Icons.email, color: Colors.orange),
title: Text( title: Text(
email, email,
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
@ -326,7 +315,8 @@ class _ContactModalState extends State<ContactModal> {
const Divider(color: Colors.grey), const Divider(color: Colors.grey),
// Favorite, Edit, and Block/Unblock Buttons // Favorite, Edit, and Block/Unblock Buttons
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding:
const EdgeInsets.symmetric(horizontal: 16.0),
child: Column( child: Column(
children: [ children: [
// Favorite button // Favorite button
@ -342,8 +332,9 @@ class _ContactModalState extends State<ContactModal> {
icon: Icon(widget.isFavorite icon: Icon(widget.isFavorite
? Icons.star ? Icons.star
: Icons.star_border), : Icons.star_border),
label: Text( label: Text(widget.isFavorite
widget.isFavorite ? 'Unfavorite' : 'Favorite'), ? 'Unfavorite'
: 'Favorite'),
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@ -362,9 +353,11 @@ class _ContactModalState extends State<ContactModal> {
width: double.infinity, width: double.infinity,
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: _toggleBlockState, onPressed: _toggleBlockState,
icon: Icon( icon: Icon(isBlocked
isBlocked ? Icons.block : Icons.block_flipped), ? Icons.block
label: Text(isBlocked ? 'Unblock' : 'Block'), : Icons.block_flipped),
label:
Text(isBlocked ? 'Unblock' : 'Block'),
), ),
), ),
], ],