update
@@ -1,4 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application
|
||||
android:label="nexuschat"
|
||||
android:name="${applicationName}"
|
||||
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 24 KiB |
@@ -428,7 +428,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@@ -485,7 +485,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 262 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 641 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 20 KiB |
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_giphy_picker/giphy_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
@@ -19,7 +20,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
List<Map<String, String>> messages = [];
|
||||
List<Map<String, dynamic>> messages = [];
|
||||
String? expediteur;
|
||||
late String destinataire;
|
||||
int idConversation = 0;
|
||||
@@ -133,6 +134,45 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _chiffrerMessage(String message) async {
|
||||
final uri =
|
||||
Uri.parse('https://nexuschat.derickexm.be/messages/crypt_message/')
|
||||
.replace(queryParameters: {'message': message});
|
||||
final response = await http.post(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
print("Chiffrement → encrypted_message: ${data['encrypted_message']}");
|
||||
print("Chiffrement → key: ${data['key']}");
|
||||
return {
|
||||
'encrypted_message': data['encrypted_message'] ?? message,
|
||||
'key': data['key'] ?? ''
|
||||
};
|
||||
} else {
|
||||
print("Erreur chiffrement: ${response.body}");
|
||||
return {'encrypted_message': message, 'key': ''};
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _dechiffrerMessage(String encryptedMessage, String key) async {
|
||||
final uri =
|
||||
Uri.parse('https://nexuschat.derickexm.be/messages/uncrypt_message/');
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
'encrypted_message': encryptedMessage,
|
||||
'key': key,
|
||||
}),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
return data['decrypted_message'] ?? encryptedMessage;
|
||||
} else {
|
||||
print("Erreur déchiffrement: ${response.body}");
|
||||
return encryptedMessage;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchMessages() async {
|
||||
if (idConversation <= 0) return;
|
||||
try {
|
||||
@@ -144,15 +184,24 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
final jsonResponse = jsonDecode(response.body);
|
||||
if (jsonResponse.containsKey('messages')) {
|
||||
final List<dynamic> messagesList = jsonResponse['messages'];
|
||||
setState(() {
|
||||
messages = messagesList.map((msg) {
|
||||
bool isMe = msg['expediteur'].toString() == expediteur;
|
||||
final decryptedMessages =
|
||||
await Future.wait(messagesList.map((msg) async {
|
||||
final isMe = msg['expediteur'].toString() == expediteur;
|
||||
|
||||
final encrypted = msg['messages'].toString();
|
||||
final cle = msg['key']?.toString() ?? '';
|
||||
final texte = await _dechiffrerMessage(encrypted, cle);
|
||||
return {
|
||||
'sender': msg['expediteur'].toString(),
|
||||
'text': msg['messages'].toString(),
|
||||
'text': texte,
|
||||
'encrypted': msg['messages'].toString(),
|
||||
'timestamp': msg['sent_at'].toString(),
|
||||
'key': msg['key']?.toString() ?? '',
|
||||
'type': msg['type'] ?? 'text'
|
||||
};
|
||||
}).toList();
|
||||
}));
|
||||
setState(() {
|
||||
messages = decryptedMessages;
|
||||
_isInitialLoading = false;
|
||||
});
|
||||
_scrollToBottom();
|
||||
@@ -163,6 +212,31 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _supprimerMessage(Map<String, dynamic> message) async {
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
'https://nexuschat.derickexm.be/messages/messages/delete_message/');
|
||||
final response = await http.post(
|
||||
uri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
'expediteur': expediteur,
|
||||
'id_conversation': idConversation,
|
||||
'message': message['encrypted'],
|
||||
'key': message['key'],
|
||||
}),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
print("✅ Message supprimé");
|
||||
_fetchMessages(); // refresh
|
||||
} else {
|
||||
print("❌ Erreur suppression: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print("❌ Erreur _supprimerMessage: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadExpediteur() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
String? expediteurEmail =
|
||||
@@ -178,6 +252,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
setState(() {
|
||||
expediteur = jsonResponse['username'];
|
||||
});
|
||||
print("📦 Chargement expediteur : $expediteur");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -194,21 +269,41 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
}
|
||||
|
||||
Future<void> sendMessage(String message) async {
|
||||
print("🧪 Appel à sendMessage()...");
|
||||
print("🔍 expediteur: $expediteur");
|
||||
if (expediteur == null || message.trim().isEmpty) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
final cryptoData = await _chiffrerMessage(message.trim());
|
||||
print("Résultat chiffrement : $cryptoData");
|
||||
final encryptedMessage = cryptoData['encrypted_message'] ?? '';
|
||||
final key = cryptoData['key'] ?? '';
|
||||
// Ajout du texte en clair
|
||||
final plainText = _controller.text.trim();
|
||||
if (key == null || key.isEmpty) {
|
||||
print('❌ Clé de chiffrement manquante. Message non envoyé.');
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
messages.add({
|
||||
'sender': expediteur!,
|
||||
'text': message.trim(),
|
||||
messages = List<Map<String, dynamic>>.from(messages)
|
||||
..add({
|
||||
'sender': expediteur ?? '',
|
||||
'text': plainText,
|
||||
'encrypted': encryptedMessage,
|
||||
'timestamp': now.toIso8601String(),
|
||||
'key': key,
|
||||
'type': 'text'
|
||||
});
|
||||
});
|
||||
_controller.clear();
|
||||
_updateButtonState();
|
||||
_scrollToBottom();
|
||||
|
||||
print("✉️ Envoi du message chiffré : $encryptedMessage");
|
||||
print("🔑 Clé : $key");
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('https://nexuschat.derickexm.be/messages/send_message/'),
|
||||
@@ -216,9 +311,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
body: jsonEncode({
|
||||
'expediteur': expediteur,
|
||||
'destinataire': destinataire,
|
||||
'message': message.trim(),
|
||||
'sent_at': now.toIso8601String(),
|
||||
'message': encryptedMessage,
|
||||
// Correction : horodatage en heure locale (Belgique)
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'id_conversation': idConversation,
|
||||
// Correction : forcer 'key' non vide
|
||||
'key': key.isNotEmpty ? key : 'test'
|
||||
}),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
@@ -239,6 +337,66 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _sendGif(String gifUrl) async {
|
||||
if (expediteur == null || gifUrl.isEmpty) return;
|
||||
print('📤 Préparation à l’envoi du GIF :');
|
||||
print(' ↪️ URL : $gifUrl');
|
||||
|
||||
final nowUtcIso = DateTime.now().toIso8601String();
|
||||
// Désactivation du chiffrement pour les GIFs
|
||||
final encryptedMessage = gifUrl;
|
||||
final key = 'test';
|
||||
print(' 🔐 Encrypted : $encryptedMessage');
|
||||
print(' 🔑 Key : $key');
|
||||
print(' 💬 ID conversation : $idConversation');
|
||||
setState(() {
|
||||
messages = List<Map<String, dynamic>>.from(messages)
|
||||
..add({
|
||||
'sender': expediteur ?? '',
|
||||
'text': gifUrl,
|
||||
'encrypted': encryptedMessage,
|
||||
'timestamp': nowUtcIso,
|
||||
'key': key,
|
||||
'type': 'gif'
|
||||
});
|
||||
});
|
||||
_scrollToBottom();
|
||||
|
||||
final body = jsonEncode({
|
||||
'expediteur': expediteur,
|
||||
'destinataire': destinataire,
|
||||
'message': encryptedMessage,
|
||||
'timestamp': nowUtcIso,
|
||||
'id_conversation': idConversation,
|
||||
'key': key,
|
||||
'type': 'gif'
|
||||
});
|
||||
print("📦 Corps envoyé à l’API :");
|
||||
print(" expediteur : ${expediteur}");
|
||||
print(" destinataire : ${destinataire}");
|
||||
print(" message : $encryptedMessage");
|
||||
print(" key : $key");
|
||||
print(" timestamp : $nowUtcIso");
|
||||
print(" id_conversation: $idConversation");
|
||||
print(" type : gif");
|
||||
print("🔒 Longueur message : ${encryptedMessage.length}");
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('https://nexuschat.derickexm.be/messages/send_message/'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: body,
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
print('Erreur envoi GIF : ${response.body}');
|
||||
} else {
|
||||
print('✅ GIF envoyé avec succès.');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Erreur réseau envoi GIF : $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _scrollToBottom() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_scrollController.hasClients) {
|
||||
@@ -255,8 +413,20 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
print('Sélectionner une photo');
|
||||
}
|
||||
|
||||
void _selectGif() {
|
||||
print('Sélectionner un GIF');
|
||||
Future<void> _selectGif() async {
|
||||
GiphyLocale? fr;
|
||||
fr ??= GiphyLocale.fromContext(context);
|
||||
|
||||
final config = GiphyUIConfig(
|
||||
apiKey: 'qG62ngUKbr66l2jVPcDGulJW1RbZy5xI',
|
||||
);
|
||||
final result =
|
||||
await showGiphyPicker(context, config, locale: GiphyLocale.fr);
|
||||
|
||||
if (result != null) {
|
||||
print("GIF sélectionné : ${result.url}");
|
||||
_sendGif(result.url);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -308,20 +478,65 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: GestureDetector(
|
||||
onLongPress: isMe
|
||||
? () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
"Supprimer le message ?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text("Annuler"),
|
||||
onPressed: () =>
|
||||
Navigator.of(context)
|
||||
.pop(),
|
||||
),
|
||||
TextButton(
|
||||
child: Text("Supprimer"),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
_supprimerMessage(
|
||||
message);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 2),
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: isMe ? Colors.orange : Colors.grey[300],
|
||||
color: isMe
|
||||
? Colors.orange
|
||||
: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
child: message['type'] == 'gif'
|
||||
? Image.network(message['text'] ?? '')
|
||||
: Text(
|
||||
message['text'] ?? '',
|
||||
style: TextStyle(
|
||||
color: isMe ? Colors.white : Colors.black,
|
||||
color: isMe
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (formattedTime.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
@@ -339,7 +554,10 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
@@ -371,6 +589,19 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade100, // plus doux
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.gif_box,
|
||||
color: Colors.deepOrange, size: 28),
|
||||
onPressed: _selectGif,
|
||||
tooltip: 'GIF',
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange,
|
||||
@@ -379,13 +610,26 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.send, color: Colors.black),
|
||||
onPressed: _isButtonEnabled
|
||||
? () => sendMessage(_controller.text)
|
||||
? () {
|
||||
sendMessage(_controller.text);
|
||||
_controller.clear();
|
||||
_updateButtonState();
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4, top: 2),
|
||||
child: Text(
|
||||
"🔒 Messages chiffrés de bout en bout",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -14,10 +14,12 @@ class Contacts extends StatefulWidget {
|
||||
class _ContactsState extends State<Contacts>
|
||||
with SingleTickerProviderStateMixin {
|
||||
List<dynamic> _contacts = [];
|
||||
List<dynamic> _filteredContacts = [];
|
||||
List<dynamic> _demandes = [];
|
||||
List<dynamic> _users = [];
|
||||
List<dynamic> _filteredUsers = [];
|
||||
TextEditingController _searchController = TextEditingController();
|
||||
TextEditingController _contactSearchController = TextEditingController();
|
||||
|
||||
String? _userEmail;
|
||||
String? currentUser;
|
||||
@@ -28,6 +30,7 @@ class _ContactsState extends State<Contacts>
|
||||
super.initState();
|
||||
_tabController = TabController(length: 3, vsync: this);
|
||||
_initUser();
|
||||
_contactSearchController.addListener(_filterContacts);
|
||||
}
|
||||
|
||||
Future<void> _initUser() async {
|
||||
@@ -66,7 +69,10 @@ class _ContactsState extends State<Contacts>
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
setState(() => _contacts = data);
|
||||
setState(() {
|
||||
_contacts = data;
|
||||
_filteredContacts = data;
|
||||
});
|
||||
} else {
|
||||
print("Erreur chargement contacts: ${response.statusCode}");
|
||||
}
|
||||
@@ -119,6 +125,15 @@ class _ContactsState extends State<Contacts>
|
||||
});
|
||||
}
|
||||
|
||||
void _filterContacts() {
|
||||
final query = _contactSearchController.text.toLowerCase();
|
||||
setState(() {
|
||||
_filteredContacts = _contacts
|
||||
.where((contact) => contact['contacts'].toLowerCase().contains(query))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _envoyerDemandeContact(String destinataire) async {
|
||||
if (destinataire == currentUser) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -202,6 +217,47 @@ class _ContactsState extends State<Contacts>
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _supprimerContact(String destinataire) async {
|
||||
final confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text("Confirmer la suppression"),
|
||||
content: Text(
|
||||
"Voulez-vous vraiment supprimer $destinataire de vos contacts ?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: Text("Annuler"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: Text("Supprimer"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm != true) return;
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('https://nexuschat.derickexm.be/contacts/supprimer_contact'),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: jsonEncode({"owner": currentUser, "contact": destinataire}),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
await _loadContacts();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Contact $destinataire supprimé")),
|
||||
);
|
||||
print("Status: ${response.statusCode}");
|
||||
print("Body: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Erreur : $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getEtatRelation(String target) async {
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
@@ -234,28 +290,54 @@ class _ContactsState extends State<Contacts>
|
||||
children: [
|
||||
_contacts.isEmpty
|
||||
? Center(child: Text("Aucun contact"))
|
||||
: ListView.builder(
|
||||
itemCount: _contacts.length,
|
||||
: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: TextField(
|
||||
controller: _contactSearchController,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Rechercher dans mes contacts...",
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: _filteredContacts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final user = _contacts[index]['contacts'];
|
||||
final user = _filteredContacts[index]['contacts'];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
title: Text(user),
|
||||
leading: Icon(Icons.person),
|
||||
trailing: ElevatedButton(
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ChatScreen(usernameExpediteur: user),
|
||||
builder: (context) => ChatScreen(
|
||||
usernameExpediteur: user),
|
||||
),
|
||||
),
|
||||
child: Text("Chat"),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () => _supprimerContact(user),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_demandes.isEmpty
|
||||
? Center(child: Text("Aucune demande"))
|
||||
: ListView.builder(
|
||||
|
||||
@@ -42,6 +42,11 @@ class Inscription extends StatelessWidget {
|
||||
Future<void> _API_inscription(BuildContext context) async {
|
||||
const String apiUrl = "https://nexuschat.derickexm.be/users/create_user";
|
||||
|
||||
if (username.text.isEmpty || email.text.isEmpty || passwd.text.isEmpty) {
|
||||
_showSnackBar(context, "Veuillez remplir tous les champs.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(apiUrl),
|
||||
|
||||
@@ -271,6 +271,12 @@ class Listechatstate extends State<Listechat> {
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
// trailing: IconButton(
|
||||
// icon: Icon(Icons.delete),
|
||||
// color: Colors.red,
|
||||
// onPressed: () =>
|
||||
// _supprimerConversation(destinataire),
|
||||
// ),
|
||||
//
|
||||
// subtitle: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -78,7 +78,6 @@ class Login extends StatelessWidget {
|
||||
if (response.statusCode == 200) {
|
||||
print("Connexion réussie");
|
||||
|
||||
// Stocker l'email de l'utilisateur
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('user_email', email);
|
||||
|
||||
@@ -99,8 +98,7 @@ class Login extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
width:
|
||||
400, // Définition d'une largeur fixe pour un meilleur affichage
|
||||
width: 400,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.black, width: 2),
|
||||
@@ -159,6 +157,13 @@ class Login extends StatelessWidget {
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
if (email.text.isEmpty || passwd.text.isEmpty) {
|
||||
_showErrorDialog(
|
||||
context, "Veuillez remplir tous les champs.");
|
||||
return;
|
||||
}
|
||||
|
||||
print("Email saisi : ${email.text}");
|
||||
print("Mot de passe : ${passwd.text}");
|
||||
checkCredentials(email.text, passwd.text);
|
||||
|
||||
@@ -93,28 +93,6 @@ class _MenuState extends State<Menu> {
|
||||
padding: const EdgeInsets.fromLTRB(15, 10, 10, 10),
|
||||
child: Column(
|
||||
children: [
|
||||
if (showEmailWarning)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber[200],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
Icon(Icons.warning_amber_rounded, color: Colors.black),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
" Les emails de vérification sont momentanément indisponibles. Veuillez nous en excuser !",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600, color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: _widgetOptions.elementAt(_selectedIndex),
|
||||
|
||||
@@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -41,6 +49,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -110,14 +134,27 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
flutter_giphy_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||
name: flutter_giphy_picker
|
||||
sha256: "249841f086ab5b7200a2316eb2a0a773a813f696d822a856e7803f17df759e35"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "1.0.11"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.0"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -126,6 +163,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.26"
|
||||
flutter_staggered_grid_view:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_staggered_grid_view
|
||||
sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -136,14 +181,22 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
focus_detector_v2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: focus_detector_v2
|
||||
sha256: d4abc4c755ba894238ab92f42f6eee7ade78aa285199e112f45926c7053f90c6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
|
||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.6"
|
||||
version: "1.4.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -168,6 +221,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -192,14 +253,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -469,6 +522,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
visibility_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: visibility_detector
|
||||
sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0+2"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -509,6 +570,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.6.0 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
||||
@@ -31,10 +31,13 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.8
|
||||
http: ^0.13.6
|
||||
|
||||
flutter_giphy_picker: ^1.0.5
|
||||
http: ^1.4.0
|
||||
adaptive_theme: ^3.6.0
|
||||
url_launcher: ^6.3.0
|
||||
intl: ^0.19.0
|
||||
@@ -47,13 +50,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_launcher_icons: ^0.11.0
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
@@ -97,3 +97,8 @@ flutter:
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/to/font-from-package
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
ios: true
|
||||
image_path: "assets/logo.png"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
<meta name="description" content="Un chat qui respecte votre intimité.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -30,7 +30,7 @@
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="assets/assets/logo.png" />
|
||||
|
||||
<title>nexuschat</title>
|
||||
<title>Nexuschat</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
|
||||
|
||||