This commit is contained in:
exmKrd
2025-04-22 18:42:24 +02:00
parent 91db846958
commit e727ac641c
11 changed files with 584 additions and 115 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,7 +1,6 @@
// ✅ chat.dart (corrigé sans ChatApp)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
@@ -17,6 +16,9 @@ class ChatScreen extends StatefulWidget {
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _controller = TextEditingController();
final ScrollController _scrollController = ScrollController();
final FocusNode _focusNode = FocusNode();
List<Map<String, String>> messages = [];
String? expediteur;
late String destinataire;
@@ -45,6 +47,8 @@ class _ChatScreenState extends State<ChatScreen> {
_pollingTimer.cancel();
_controller.removeListener(_updateButtonState);
_controller.dispose();
_focusNode.dispose();
_scrollController.dispose();
super.dispose();
}
@@ -144,12 +148,14 @@ class _ChatScreenState extends State<ChatScreen> {
messages = messagesList.map((msg) {
bool isMe = msg['expediteur'].toString() == expediteur;
return {
'sender': isMe ? 'me' : 'bot',
'sender': msg['expediteur'].toString(),
'text': msg['messages'].toString(),
'timestamp': msg['sent_at'].toString(),
};
}).toList();
_isInitialLoading = false;
});
_scrollToBottom();
}
}
} catch (e) {
@@ -183,16 +189,26 @@ class _ChatScreenState extends State<ChatScreen> {
void _updateButtonState() {
setState(() {
_isButtonEnabled = _controller.text.isNotEmpty;
_isButtonEnabled = _controller.text.trim().isNotEmpty;
});
}
Future<void> sendMessage(String message) async {
if (expediteur == null || message.trim().isEmpty) return;
final now = DateTime.now();
setState(() {
messages.add({'sender': 'me', 'text': message});
messages.add({
'sender': expediteur!,
'text': message.trim(),
'timestamp': now.toIso8601String(),
});
});
_controller.clear();
_updateButtonState();
_scrollToBottom();
try {
final response = await http.post(
Uri.parse('https://nexuschat.derickexm.be/messages/send_message/'),
@@ -200,8 +216,8 @@ class _ChatScreenState extends State<ChatScreen> {
body: jsonEncode({
'expediteur': expediteur,
'destinataire': destinataire,
'message': message,
'timestamp': DateTime.now().toIso8601String(),
'message': message.trim(),
'sent_at': now.toIso8601String(),
'id_conversation': idConversation,
}),
);
@@ -209,8 +225,13 @@ class _ChatScreenState extends State<ChatScreen> {
final jsonResponse = jsonDecode(response.body);
if (jsonResponse.containsKey('reply')) {
setState(() {
messages.add({'sender': 'bot', 'text': jsonResponse['reply']});
messages.add({
'sender': destinataire,
'text': jsonResponse['reply'],
'timestamp': DateTime.now().toIso8601String(),
});
});
_scrollToBottom();
}
}
} catch (e) {
@@ -218,6 +239,26 @@ class _ChatScreenState extends State<ChatScreen> {
}
}
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
}
void _selectPhoto() {
print('Sélectionner une photo');
}
void _selectGif() {
print('Sélectionner un GIF');
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -236,28 +277,62 @@ class _ChatScreenState extends State<ChatScreen> {
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
final isMe = message['sender'] == 'me';
return Align(
final isMe = message['sender'] == expediteur;
// Formatage de l'heure
final time =
DateTime.tryParse(message['timestamp'] ?? '');
final formattedTime = time != null
? "${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}"
: '';
return Container(
alignment:
isMe ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
vertical: 4, horizontal: 8),
child: Column(
crossAxisAlignment: isMe
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Text(
message['sender'] ?? '',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.grey[700],
),
),
Container(
margin: const EdgeInsets.only(top: 2),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: isMe ? Colors.orange : Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: Text(
message['text']!,
message['text'] ?? '',
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
),
),
),
if (formattedTime.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
formattedTime,
style: TextStyle(
fontSize: 10, color: Colors.grey[600]),
),
),
],
),
);
},
),
@@ -269,19 +344,46 @@ class _ChatScreenState extends State<ChatScreen> {
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Écrire un message...',
focusNode: _focusNode,
style: TextStyle(fontSize: 16),
cursorColor: Colors.orange,
decoration: InputDecoration(
hintText: 'Entrez votre message...',
border: OutlineInputBorder(),
),
minLines: 1,
maxLines: 5,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
onChanged: (text) => _updateButtonState(),
onEditingComplete: () {},
inputFormatters: [
_EnterKeyFormatter(
onEnter: () {
if (_controller.text.trim().isNotEmpty) {
sendMessage(_controller.text);
_controller
.clear(); // 👈 Ajout explicite du clear ici
}
},
),
],
),
),
IconButton(
icon: const Icon(Icons.send),
SizedBox(width: 8),
Container(
decoration: BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: IconButton(
icon: Icon(Icons.send, color: Colors.black),
onPressed: _isButtonEnabled
? () => sendMessage(_controller.text)
: null,
color: _isButtonEnabled ? Colors.orange : Colors.grey,
),
),
SizedBox(width: 8),
],
),
),
@@ -290,3 +392,25 @@ class _ChatScreenState extends State<ChatScreen> {
);
}
}
// ✅ Formatter : Enter = envoi / Shift+Enter = saut de ligne
class _EnterKeyFormatter extends TextInputFormatter {
final VoidCallback onEnter;
_EnterKeyFormatter({required this.onEnter});
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.length > oldValue.text.length &&
newValue.text.endsWith('\n') &&
!RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.shiftLeft) &&
!RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.shiftRight)) {
onEnter();
return const TextEditingValue(text: ''); // vide le champ après envoi
}
return newValue;
}
}

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:nexuschat/listechat.dart';
import 'package:nexuschat/login.dart';
class Inscription extends StatelessWidget {
@@ -13,33 +12,33 @@ class Inscription extends StatelessWidget {
final TextEditingController username = TextEditingController();
Future<void> sendVerificationEmail(BuildContext context, String email) async {
final url = Uri.parse('https://nexuschat.derickexm.be/email/send_email/');
final url = Uri.parse(
'https://nexuschat.derickexm.be/email/send_email?email=$email');
try {
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
"email": email,
}),
headers: {'Accept': 'application/json'},
);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Email de vérification envoyé avec succès!")),
);
_showSnackBar(context, "Email de vérification envoyé avec succès!");
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Impossible d'envoyer l'email de vérification.")),
);
_showSnackBar(context,
"Impossible d'envoyer l'email de vérification. (${response.statusCode})");
print("Réponse serveur : ${response.body}");
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Erreur de connexion à l'API: $e")),
);
_showSnackBar(context, "Erreur de connexion à l'API.");
print("Erreur d'envoi email : $e");
}
}
void _showSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
Future<void> _API_inscription(BuildContext context) async {
const String apiUrl = "https://nexuschat.derickexm.be/users/create_user";
@@ -56,29 +55,20 @@ class Inscription extends StatelessWidget {
if (response.statusCode == 200) {
print("Inscription réussie !");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Inscription réussie !")),
);
_showSnackBar(context, "Inscription réussie !");
sendVerificationEmail(context, email.text);
// Appel de la fonction pour envoyer l'email de confirmation
await sendVerificationEmail(context, email.text);
// Redirection vers la page de login
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Login()),
);
} else {
print("Erreur : ${response.body}");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Erreur lors de l'inscription")),
);
_showSnackBar(context, "Erreur lors de l'inscription");
}
} catch (e) {
print("Erreur réseau : $e");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Problème de connexion au serveur")),
);
_showSnackBar(context, "Problème de connexion au serveur");
}
}
@@ -106,8 +96,6 @@ class Inscription extends StatelessWidget {
const Text("S'inscrire sur NexusChat",
style: TextStyle(fontSize: 24)),
const SizedBox(height: 20),
// Champ Nom d'utilisateur
SizedBox(
width: 400,
child: TextField(
@@ -121,8 +109,6 @@ class Inscription extends StatelessWidget {
),
),
const SizedBox(height: 20),
// Champ Email
SizedBox(
width: 400,
child: TextField(
@@ -136,8 +122,6 @@ class Inscription extends StatelessWidget {
),
),
const SizedBox(height: 20),
// Champ Mot de passe
SizedBox(
width: 400,
child: TextField(
@@ -151,15 +135,11 @@ class Inscription extends StatelessWidget {
),
),
const SizedBox(height: 30),
// Bouton S'inscrire
ElevatedButton(
onPressed: () => _API_inscription(context),
child: const Text("S'inscrire"),
),
const SizedBox(height: 20),
// Bouton Déjà inscrit ?
ElevatedButton(
onPressed: () {
Navigator.push(context,

View File

@@ -99,17 +99,15 @@ class Listechatstate extends State<Listechat> {
if (response.statusCode == 200) {
final data = json.decode(response.body);
if (data is Map<String, dynamic> && data.containsKey("conversations")) {
if (data is Map<String, dynamic>) {
if (data.containsKey("conversations")) {
return List<Map<String, dynamic>>.from(data["conversations"]);
} else {
setState(() {
errorMessage = "Format de réponse inattendu";
});
} else if (data.containsKey("exists") && data["exists"] == false) {
return [];
}
} else {
}
setState(() {
errorMessage = "Erreur serveur: ${response.statusCode}";
errorMessage = "Format de réponse inattendu";
});
return [];
}
@@ -120,6 +118,9 @@ class Listechatstate extends State<Listechat> {
});
return [];
}
// ✅ Ce return évite l'erreur "body might complete normally"
return [];
}
Future<void> _refreshConversations() async {
@@ -146,6 +147,7 @@ class Listechatstate extends State<Listechat> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text("Mes Conversations"),
actions: [
IconButton(
@@ -200,9 +202,21 @@ class Listechatstate extends State<Listechat> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Aucune conversation trouvée"),
const Icon(Icons.chat_bubble_outline,
size: 80, color: Colors.grey),
const SizedBox(height: 20),
ElevatedButton(
const Text(
"Aucune conversation pour linstant",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
const Text(
"Commencez une discussion avec vos contacts.",
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 20),
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
@@ -211,7 +225,8 @@ class Listechatstate extends State<Listechat> {
),
);
},
child: const Text("Démarrer une conversation"),
icon: const Icon(Icons.add_comment),
label: const Text("Démarrer une conversation"),
),
],
),
@@ -234,13 +249,6 @@ class Listechatstate extends State<Listechat> {
? "conversation_${expediteur}_$rawId"
: "conversation_${expediteur}_${destinataire}_$index";
final lastMessage =
conv["last_message"] ?? "Aucun message";
final timestamp = conv["created_at"] ?? "Inconnu";
final formattedDate = formatTimestamp(timestamp);
final truncatedMessage =
truncateMessage(lastMessage, 40);
return Hero(
tag: uniqueTag,
child: Card(
@@ -263,19 +271,20 @@ class Listechatstate extends State<Listechat> {
style: const TextStyle(
fontWeight: FontWeight.bold),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(truncatedMessage),
Text(
formattedDate,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
//
// subtitle: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(truncatedMessage),
// Text(
// formattedDate,
// style: TextStyle(
// fontSize: 12,
// color: Colors.grey[600],
// ),
// ),
// ],
// ),
onTap: () {
Navigator.push(
context,

View File

@@ -36,6 +36,27 @@ class Login extends StatelessWidget {
);
}
Future<void> sendAttemptLogin(String email) async {
final url = Uri.parse(
'https://nexuschat.derickexm.be/email/send_login_attempt_email/?email=$email');
try {
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode != 200) {
print(
"Erreur lors de l'envoi de l'alerte de tentative de connexion : ${response.body}");
} else {
print("Alerte de tentative de connexion envoyée à $email");
}
} catch (e) {
print("Erreur de connexion lors de l'envoi de l'alerte : $e");
}
}
Future<void> checkCredentials(String email, String password) async {
final url =
Uri.parse('https://nexuschat.derickexm.be/users/check_credentials');
@@ -63,6 +84,7 @@ class Login extends StatelessWidget {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Menu()));
await sendAttemptLogin(email);
} else {
_showErrorDialog(
context, "Nom d'utilisateur ou mot de passe incorrect");

View File

@@ -14,12 +14,14 @@ class _MenuState extends State<Menu> {
int _selectedIndex = 0;
String? _userDisplayName;
String? _profilePictureURL;
bool showEmailWarning = true;
static late List<Widget> _widgetOptions;
@override
void initState() {
super.initState();
_widgetOptions = <Widget>[
Listechat(),
Setting(),
@@ -91,6 +93,28 @@ 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),

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:nexuschat/login.dart';
import 'package:shared_preferences/shared_preferences.dart';
late SharedPreferences prefs;
@@ -109,6 +110,7 @@ class _ProfilState extends State<Profil> {
final data = jsonDecode(response.body);
setState(() {
_username = data['username'];
prefs.setString('username', _username!);
});
} else {
_showSnackBar("Impossible de récupérer le nom d'utilisateur.");
@@ -124,6 +126,167 @@ class _ProfilState extends State<Profil> {
);
}
Future<void> _ChangePassword() async {
final email = prefs.getString("user_email");
if (email == null) {
_showSnackBar("Email utilisateur introuvable.");
return;
}
final oldPasswordController = TextEditingController();
final newPasswordController = TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Changer le mot de passe'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: oldPasswordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Ancien mot de passe',
),
),
const SizedBox(height: 10),
TextField(
controller: newPasswordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Nouveau mot de passe',
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
TextButton(
onPressed: () async {
final oldPassword = oldPasswordController.text.trim();
final newPassword = newPasswordController.text.trim();
if (oldPassword.isEmpty || newPassword.isEmpty) {
_showSnackBar("Les deux champs sont requis.");
return;
}
final url = Uri.parse(
'https://nexuschat.derickexm.be/users/change_password');
try {
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
"email": email,
"old_password": oldPassword,
"new_password": newPassword,
}),
);
if (response.statusCode == 200) {
_showSnackBar("Mot de passe mis à jour !");
Navigator.of(context).pop(); // Fermer le popup
} else {
final responseData = jsonDecode(response.body);
_showSnackBar(responseData["error"] ?? "Erreur inconnue.");
}
} catch (e) {
_showSnackBar("Erreur lors de la connexion à l'API.");
}
},
child: const Text('Confirmer'),
),
],
);
},
);
}
Future<void> _deleteAccount() async {
final username = prefs.getString('username');
final email = prefs.getString('user_email');
final passwordController = TextEditingController();
if (username == null || email == null) {
_showSnackBar("Impossible de récupérer les infos du compte.");
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Confirmation'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"Pour supprimer votre compte, entrez votre mot de passe :"),
const SizedBox(height: 10),
TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(labelText: "Mot de passe"),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Annuler"),
),
TextButton(
onPressed: () async {
final password = passwordController.text.trim();
if (password.isEmpty) {
_showSnackBar("Le mot de passe est requis.");
return;
}
final url = Uri.parse(
'https://nexuschat.derickexm.be/users/delete_user');
try {
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
"username": username,
"email": email,
"password": password,
}),
);
if (response.statusCode == 200) {
_showSnackBar("Compte supprimé avec succès.");
await prefs.clear();
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const Login()),
);
} else {
_showSnackBar("Échec de la suppression du compte.");
}
} catch (e) {
_showSnackBar("Erreur de connexion à l'API.");
}
},
child: const Text("Supprimer"),
),
],
);
},
);
}
Future<void> sendVerificationEmail(BuildContext context, String email) async {
final url = Uri.parse(
'https://nexuschat.derickexm.be/email/send_email?email=$_userEmail');
@@ -227,6 +390,7 @@ class _ProfilState extends State<Profil> {
),
),
const SizedBox(height: 15),
if (!_isEmailVerified)
ElevatedButton(
onPressed: () {
if (_userEmail != null) {
@@ -290,6 +454,24 @@ class _ProfilState extends State<Profil> {
},
child: const Text('Changer nom d\'utilisateur'),
),
SizedBox(
height: 10,
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: _ChangePassword,
child: const Text("Changer le mot de passe",
style: TextStyle(color: Colors.white)),
),
SizedBox(
height: 10,
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: _deleteAccount,
child: const Text("Supprimer le compte",
style: TextStyle(color: Colors.white)),
),
],
),
),

View File

@@ -0,0 +1,29 @@
PODS:
- FlutterMacOS (1.0.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.16.2

View File

@@ -27,6 +27,8 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
B8BA568225B7722CCF96E8D8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 715EBAFB738B4F4A7653918C /* Pods_RunnerTests.framework */; };
C2185F62C3CD9FF30DDE5155 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C83475F4656F24A643903FD /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -60,11 +62,13 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
070AC6A69E7291C1C4854DFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
0C83475F4656F24A643903FD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* nexuschat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "nexuschat.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* nexuschat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = nexuschat.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@@ -76,8 +80,14 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
715EBAFB738B4F4A7653918C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
9DC09B5245FF3C3E541B7D09 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
B92B3CB62787A38FD35339CD /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
CF42FF394810944FE028C7BC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
DE1B5073B319B2490AAEB036 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
E34584EA242300AA26F1BA1B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -85,6 +95,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B8BA568225B7722CCF96E8D8 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -92,6 +103,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C2185F62C3CD9FF30DDE5155 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -125,6 +137,7 @@
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
9866A980C9448EA5D225CC03 /* Pods */,
);
sourceTree = "<group>";
};
@@ -172,9 +185,25 @@
path = Runner;
sourceTree = "<group>";
};
9866A980C9448EA5D225CC03 /* Pods */ = {
isa = PBXGroup;
children = (
070AC6A69E7291C1C4854DFA /* Pods-Runner.debug.xcconfig */,
CF42FF394810944FE028C7BC /* Pods-Runner.release.xcconfig */,
E34584EA242300AA26F1BA1B /* Pods-Runner.profile.xcconfig */,
9DC09B5245FF3C3E541B7D09 /* Pods-RunnerTests.debug.xcconfig */,
B92B3CB62787A38FD35339CD /* Pods-RunnerTests.release.xcconfig */,
DE1B5073B319B2490AAEB036 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
0C83475F4656F24A643903FD /* Pods_Runner.framework */,
715EBAFB738B4F4A7653918C /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -186,6 +215,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
239B5D276F88E359251E43FD /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
@@ -204,11 +234,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
0735B088592D17EFE1C39F67 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
976689EE1C53CE19E6E6C9A5 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -291,6 +323,50 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0735B088592D17EFE1C39F67 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
239B5D276F88E359251E43FD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -329,6 +405,23 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
976689EE1C53CE19E6E6C9A5 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -380,6 +473,7 @@
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9DC09B5245FF3C3E541B7D09 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@@ -394,6 +488,7 @@
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B92B3CB62787A38FD35339CD /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@@ -408,6 +503,7 @@
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DE1B5073B319B2490AAEB036 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;

View File

@@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -25,10 +25,10 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="nexuschat">
<link rel="apple-touch-icon" href="assets/logo.png">
<link rel="apple-touch-icon" href="assets/assets/logo.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="assets/logo.png" />
<link rel="icon" type="image/png" href="assets/assets/logo.png" />
<title>nexuschat</title>
<link rel="manifest" href="manifest.json">