Hubertoo commited on
Commit
9b7ce94
·
verified ·
1 Parent(s): 9d7fbdb

Inscription ne fonctionne pas - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1670 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Sbi Book
3
- emoji: 🐠
4
- colorFrom: yellow
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: sbi-book
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1670 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>SBI BOOK - Réseau Social</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* Custom CSS for elements that can't be done with Tailwind */
11
+ .online-status::after {
12
+ content: '';
13
+ position: absolute;
14
+ bottom: 0;
15
+ right: 0;
16
+ width: 12px;
17
+ height: 12px;
18
+ background-color: #4ade80;
19
+ border-radius: 50%;
20
+ border: 2px solid white;
21
+ }
22
+
23
+ .file-input-label {
24
+ cursor: pointer;
25
+ transition: all 0.3s;
26
+ }
27
+
28
+ .file-input-label:hover {
29
+ transform: scale(1.05);
30
+ }
31
+
32
+ .comment-reply {
33
+ margin-left: 40px;
34
+ border-left: 2px solid #e5e7eb;
35
+ padding-left: 10px;
36
+ }
37
+
38
+ .audio-recorder {
39
+ animation: pulse 2s infinite;
40
+ }
41
+
42
+ @keyframes pulse {
43
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
44
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
45
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
46
+ }
47
+
48
+ .modal {
49
+ transition: opacity 0.3s ease, transform 0.3s ease;
50
+ }
51
+
52
+ .modal.hidden {
53
+ opacity: 0;
54
+ transform: scale(0.9);
55
+ pointer-events: none;
56
+ }
57
+ </style>
58
+ </head>
59
+ <body class="bg-gray-100 min-h-screen flex flex-col">
60
+ <!-- Auth Modal -->
61
+ <div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
62
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
63
+ <div class="flex justify-between items-center mb-4">
64
+ <h2 class="text-2xl font-bold text-blue-600">SBI BOOK</h2>
65
+ <button id="closeAuthModal" class="text-gray-500 hover:text-gray-700">
66
+ <i class="fas fa-times"></i>
67
+ </button>
68
+ </div>
69
+
70
+ <div id="authTabs" class="flex border-b mb-4">
71
+ <button id="loginTab" class="px-4 py-2 font-medium text-blue-600 border-b-2 border-blue-600">Connexion</button>
72
+ <button id="registerTab" class="px-4 py-2 font-medium text-gray-500">Inscription</button>
73
+ </div>
74
+
75
+ <!-- Login Form -->
76
+ <div id="loginForm">
77
+ <div class="mb-4">
78
+ <label class="block text-gray-700 mb-2" for="loginUsername">Nom d'utilisateur</label>
79
+ <input type="text" id="loginUsername" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
80
+ </div>
81
+ <div class="mb-4 relative">
82
+ <label class="block text-gray-700 mb-2" for="loginPassword">Mot de passe</label>
83
+ <input type="password" id="loginPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
84
+ <button id="toggleLoginPassword" class="absolute right-3 top-9 text-gray-500">
85
+ <i class="fas fa-eye"></i>
86
+ </button>
87
+ </div>
88
+ <div class="mb-4 flex justify-between items-center">
89
+ <button id="forgotPassword" class="text-blue-600 text-sm">Mot de passe oublié ?</button>
90
+ </div>
91
+ <button id="loginBtn" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">Se connecter</button>
92
+ </div>
93
+
94
+ <!-- Register Form -->
95
+ <div id="registerForm" class="hidden">
96
+ <div class="mb-4">
97
+ <label class="block text-gray-700 mb-2" for="fullName">Nom complet*</label>
98
+ <input type="text" id="fullName" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
99
+ </div>
100
+ <div class="mb-4">
101
+ <label class="block text-gray-700 mb-2" for="birthDate">Date de naissance*</label>
102
+ <input type="date" id="birthDate" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
103
+ </div>
104
+ <div class="mb-4">
105
+ <label class="block text-gray-700 mb-2" for="location">Lieu de résidence*</label>
106
+ <input type="text" id="location" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
107
+ </div>
108
+ <div class="mb-4">
109
+ <label class="block text-gray-700 mb-2" for="username">Nom d'utilisateur</label>
110
+ <input type="text" id="username" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
111
+ </div>
112
+ <div class="mb-4 relative">
113
+ <label class="block text-gray-700 mb-2" for="password">Mot de passe*</label>
114
+ <input type="password" id="password" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
115
+ <button id="toggleRegisterPassword" class="absolute right-3 top-9 text-gray-500">
116
+ <i class="fas fa-eye"></i>
117
+ </button>
118
+ </div>
119
+ <button id="registerBtn" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">S'inscrire</button>
120
+ </div>
121
+
122
+ <!-- Forgot Password Form -->
123
+ <div id="forgotPasswordForm" class="hidden">
124
+ <h3 class="text-lg font-medium mb-4">Réinitialiser le mot de passe</h3>
125
+ <div class="mb-4">
126
+ <label class="block text-gray-700 mb-2" for="forgotUsername">Nom d'utilisateur</label>
127
+ <input type="text" id="forgotUsername" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
128
+ </div>
129
+ <div class="mb-4">
130
+ <label class="block text-gray-700 mb-2" for="forgotEmail">Email associé</label>
131
+ <input type="email" id="forgotEmail" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
132
+ </div>
133
+ <button id="resetPasswordBtn" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition mb-2">Réinitialiser</button>
134
+ <button id="backToLogin" class="w-full bg-gray-200 text-gray-800 py-2 rounded-lg hover:bg-gray-300 transition">Retour</button>
135
+ </div>
136
+ </div>
137
+ </div>
138
+
139
+ <!-- Main App (hidden until login) -->
140
+ <div id="app" class="hidden flex-1 flex flex-col">
141
+ <!-- Header -->
142
+ <header class="bg-white shadow-sm sticky top-0 z-10">
143
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
144
+ <div class="flex items-center">
145
+ <h1 class="text-2xl font-bold text-blue-600">SBI BOOK</h1>
146
+ </div>
147
+ <div class="flex items-center space-x-4">
148
+ <div id="userMenu" class="relative">
149
+ <button id="userMenuButton" class="flex items-center space-x-2 focus:outline-none">
150
+ <div class="w-10 h-10 rounded-full bg-gray-300 overflow-hidden relative">
151
+ <img id="userAvatar" src="" alt="Profile" class="w-full h-full object-cover">
152
+ <div class="online-status"></div>
153
+ </div>
154
+ <span id="userNameDisplay" class="font-medium"></span>
155
+ </button>
156
+ <div id="userDropdown" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-20">
157
+ <a href="#" id="profileLink" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">Profil</a>
158
+ <a href="#" id="settingsLink" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">Paramètres</a>
159
+ <a href="#" id="logoutLink" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">Déconnexion</a>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </header>
165
+
166
+ <!-- Create Post Section -->
167
+ <div class="container mx-auto px-4 py-4">
168
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
169
+ <div class="flex items-start space-x-3">
170
+ <div class="w-12 h-12 rounded-full bg-gray-300 overflow-hidden relative">
171
+ <img id="currentUserAvatar" src="" alt="Profile" class="w-full h-full object-cover">
172
+ <div class="online-status"></div>
173
+ </div>
174
+ <div class="flex-1">
175
+ <textarea id="postContent" placeholder="À quoi pensez-vous ?" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none" rows="2"></textarea>
176
+ <div id="postPreview" class="mt-2 hidden"></div>
177
+ <div class="flex justify-between items-center mt-3">
178
+ <div class="flex space-x-2">
179
+ <label for="postImage" class="file-input-label text-green-600 cursor-pointer">
180
+ <i class="fas fa-image"></i> Photo
181
+ </label>
182
+ <input type="file" id="postImage" accept="image/*" class="hidden">
183
+
184
+ <label for="postVideo" class="file-input-label text-blue-600 cursor-pointer">
185
+ <i class="fas fa-video"></i> Vidéo
186
+ </label>
187
+ <input type="file" id="postVideo" accept="video/*" class="hidden">
188
+
189
+ <label for="postAudio" class="file-input-label text-purple-600 cursor-pointer">
190
+ <i class="fas fa-music"></i> Audio
191
+ </label>
192
+ <input type="file" id="postAudio" accept="audio/*" class="hidden">
193
+
194
+ <button id="startRecording" class="text-red-600">
195
+ <i class="fas fa-microphone"></i> Vocal
196
+ </button>
197
+ </div>
198
+ <button id="postSubmit" class="bg-blue-600 text-white px-4 py-1 rounded-lg hover:bg-blue-700 transition">Publier</button>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <!-- Main Content -->
206
+ <div class="container mx-auto px-4 flex flex-col md:flex-row gap-4 flex-1">
207
+ <!-- Left Sidebar -->
208
+ <div class="w-full md:w-1/4 lg:w-1/5">
209
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
210
+ <h3 class="font-bold text-lg mb-3">Menu</h3>
211
+ <ul class="space-y-2">
212
+ <li><a href="#" class="flex items-center space-x-2 text-blue-600"><i class="fas fa-home"></i> <span>Fil d'actualité</span></a></li>
213
+ <li><a href="#" class="flex items-center space-x-2"><i class="fas fa-user-friends"></i> <span>Amis</span></a></li>
214
+ <li><a href="#" id="groupsLink" class="flex items-center space-x-2"><i class="fas fa-users"></i> <span>Groupes</span></a></li>
215
+ <li><a href="#" class="flex items-center space-x-2"><i class="fas fa-envelope"></i> <span>Messages</span></a></li>
216
+ </ul>
217
+ </div>
218
+
219
+ <div class="bg-white rounded-lg shadow p-4">
220
+ <h3 class="font-bold text-lg mb-3">Groupes</h3>
221
+ <div id="groupsList" class="space-y-2">
222
+ <!-- Groups will be populated here -->
223
+ </div>
224
+ <button id="createGroupBtn" class="mt-3 w-full bg-gray-200 text-gray-800 py-1 rounded-lg hover:bg-gray-300 transition">+ Créer un groupe</button>
225
+ </div>
226
+ </div>
227
+
228
+ <!-- Main Feed -->
229
+ <div class="w-full md:w-2/4 lg:w-3/5">
230
+ <div id="postsContainer" class="space-y-4">
231
+ <!-- Posts will be populated here -->
232
+ </div>
233
+ </div>
234
+
235
+ <!-- Right Sidebar -->
236
+ <div class="w-full md:w-1/4 lg:w-1/5">
237
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
238
+ <h3 class="font-bold text-lg mb-3">Contacts</h3>
239
+ <div id="contactsList" class="space-y-3">
240
+ <!-- Contacts will be populated here -->
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ <!-- Footer -->
247
+ <footer class="bg-white shadow-md py-3 mt-5">
248
+ <div class="container mx-auto px-4">
249
+ <div class="flex justify-around">
250
+ <a href="#" class="flex flex-col items-center text-gray-700">
251
+ <i class="fas fa-comments text-xl"></i>
252
+ <span class="text-xs mt-1">Discussions</span>
253
+ </a>
254
+ <a href="#" class="flex flex-col items-center text-blue-600">
255
+ <i class="fas fa-newspaper text-xl"></i>
256
+ <span class="text-xs mt-1">Actus</span>
257
+ </a>
258
+ <a href="#" class="flex flex-col items-center text-gray-700">
259
+ <i class="fas fa-users text-xl"></i>
260
+ <span class="text-xs mt-1">Groupes</span>
261
+ </a>
262
+ </div>
263
+ </div>
264
+ </footer>
265
+ </div>
266
+
267
+ <!-- Create Group Modal -->
268
+ <div id="createGroupModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
269
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
270
+ <div class="flex justify-between items-center mb-4">
271
+ <h2 class="text-xl font-bold">Créer un groupe</h2>
272
+ <button id="closeGroupModal" class="text-gray-500 hover:text-gray-700">
273
+ <i class="fas fa-times"></i>
274
+ </button>
275
+ </div>
276
+
277
+ <div class="mb-4">
278
+ <label class="block text-gray-700 mb-2" for="groupName">Nom du groupe*</label>
279
+ <input type="text" id="groupName" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" required>
280
+ </div>
281
+
282
+ <div class="mb-4">
283
+ <label class="block text-gray-700 mb-2" for="groupDescription">Description</label>
284
+ <textarea id="groupDescription" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3"></textarea>
285
+ </div>
286
+
287
+ <div class="mb-4">
288
+ <label class="block text-gray-700 mb-2" for="groupImage">Image de couverture</label>
289
+ <input type="file" id="groupImage" accept="image/*" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
290
+ </div>
291
+
292
+ <button id="createGroupSubmit" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">Créer</button>
293
+ </div>
294
+ </div>
295
+
296
+ <!-- Settings Modal -->
297
+ <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
298
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
299
+ <div class="flex justify-between items-center mb-4">
300
+ <h2 class="text-xl font-bold">Paramètres</h2>
301
+ <button id="closeSettingsModal" class="text-gray-500 hover:text-gray-700">
302
+ <i class="fas fa-times"></i>
303
+ </button>
304
+ </div>
305
+
306
+ <div class="mb-4">
307
+ <h3 class="font-medium mb-2">Profil</h3>
308
+ <div class="flex items-center space-x-4 mb-3">
309
+ <div class="w-16 h-16 rounded-full bg-gray-300 overflow-hidden relative">
310
+ <img id="settingsAvatar" src="" alt="Profile" class="w-full h-full object-cover">
311
+ </div>
312
+ <div>
313
+ <label for="settingsAvatarInput" class="text-blue-600 cursor-pointer">Changer la photo</label>
314
+ <input type="file" id="settingsAvatarInput" accept="image/*" class="hidden">
315
+ </div>
316
+ </div>
317
+
318
+ <div class="mb-3">
319
+ <label class="block text-gray-700 mb-1" for="settingsFullName">Nom complet</label>
320
+ <input type="text" id="settingsFullName" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
321
+ </div>
322
+
323
+ <div class="mb-3">
324
+ <label class="block text-gray-700 mb-1" for="settingsUsername">Nom d'utilisateur</label>
325
+ <input type="text" id="settingsUsername" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
326
+ </div>
327
+
328
+ <div class="mb-3">
329
+ <label class="block text-gray-700 mb-1" for="settingsLocation">Lieu de résidence</label>
330
+ <input type="text" id="settingsLocation" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
331
+ </div>
332
+ </div>
333
+
334
+ <div class="mb-4">
335
+ <h3 class="font-medium mb-2">Sécurité</h3>
336
+ <button id="changePasswordBtn" class="w-full text-left py-2 px-3 bg-gray-100 rounded-lg mb-2 hover:bg-gray-200">Changer le mot de passe</button>
337
+ <button id="blockedUsersBtn" class="w-full text-left py-2 px-3 bg-gray-100 rounded-lg hover:bg-gray-200">Utilisateurs bloqués</button>
338
+ </div>
339
+
340
+ <button id="saveSettings" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">Enregistrer</button>
341
+ </div>
342
+ </div>
343
+
344
+ <!-- Change Password Modal -->
345
+ <div id="changePasswordModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
346
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
347
+ <div class="flex justify-between items-center mb-4">
348
+ <h2 class="text-xl font-bold">Changer le mot de passe</h2>
349
+ <button id="closeChangePasswordModal" class="text-gray-500 hover:text-gray-700">
350
+ <i class="fas fa-times"></i>
351
+ </button>
352
+ </div>
353
+
354
+ <div class="mb-4 relative">
355
+ <label class="block text-gray-700 mb-2" for="currentPassword">Mot de passe actuel</label>
356
+ <input type="password" id="currentPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
357
+ <button id="toggleCurrentPassword" class="absolute right-3 top-9 text-gray-500">
358
+ <i class="fas fa-eye"></i>
359
+ </button>
360
+ </div>
361
+
362
+ <div class="mb-4 relative">
363
+ <label class="block text-gray-700 mb-2" for="newPassword">Nouveau mot de passe</label>
364
+ <input type="password" id="newPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
365
+ <button id="toggleNewPassword" class="absolute right-3 top-9 text-gray-500">
366
+ <i class="fas fa-eye"></i>
367
+ </button>
368
+ </div>
369
+
370
+ <div class="mb-4 relative">
371
+ <label class="block text-gray-700 mb-2" for="confirmNewPassword">Confirmer le nouveau mot de passe</label>
372
+ <input type="password" id="confirmNewPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
373
+ <button id="toggleConfirmNewPassword" class="absolute right-3 top-9 text-gray-500">
374
+ <i class="fas fa-eye"></i>
375
+ </button>
376
+ </div>
377
+
378
+ <button id="submitPasswordChange" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition">Changer</button>
379
+ </div>
380
+ </div>
381
+
382
+ <!-- Blocked Users Modal -->
383
+ <div id="blockedUsersModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
384
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
385
+ <div class="flex justify-between items-center mb-4">
386
+ <h2 class="text-xl font-bold">Utilisateurs bloqués</h2>
387
+ <button id="closeBlockedUsersModal" class="text-gray-500 hover:text-gray-700">
388
+ <i class="fas fa-times"></i>
389
+ </button>
390
+ </div>
391
+
392
+ <div id="blockedUsersList" class="space-y-3 max-h-64 overflow-y-auto">
393
+ <!-- Blocked users will be listed here -->
394
+ </div>
395
+
396
+ <div class="mt-4 text-center text-gray-500" id="noBlockedUsers">
397
+ Aucun utilisateur bloqué
398
+ </div>
399
+ </div>
400
+ </div>
401
+
402
+ <!-- Audio Recorder -->
403
+ <div id="audioRecorder" class="fixed bottom-4 right-4 bg-white p-4 rounded-lg shadow-lg hidden">
404
+ <div class="flex items-center space-x-4">
405
+ <button id="stopRecording" class="text-red-600">
406
+ <i class="fas fa-stop-circle text-2xl"></i>
407
+ </button>
408
+ <div class="flex-1">
409
+ <div class="flex items-center">
410
+ <div id="recordingTimer" class="text-gray-700 mr-3">00:00</div>
411
+ <div id="recordingVisualizer" class="flex-1 h-4 bg-gray-200 rounded-full overflow-hidden">
412
+ <div class="h-full bg-blue-500 animate-pulse" style="width: 30%"></div>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ </div>
417
+ </div>
418
+
419
+ <script>
420
+ // Main App State
421
+ const state = {
422
+ currentUser: null,
423
+ users: JSON.parse(localStorage.getItem('users')) || [],
424
+ posts: JSON.parse(localStorage.getItem('posts')) || [],
425
+ groups: JSON.parse(localStorage.getItem('groups')) || [],
426
+ blockedUsers: JSON.parse(localStorage.getItem('blockedUsers')) || {},
427
+ audioRecorder: null,
428
+ recordingStartTime: null,
429
+ recordingTimer: null
430
+ };
431
+
432
+ // DOM Elements
433
+ const authModal = document.getElementById('authModal');
434
+ const app = document.getElementById('app');
435
+ const loginForm = document.getElementById('loginForm');
436
+ const registerForm = document.getElementById('registerForm');
437
+ const forgotPasswordForm = document.getElementById('forgotPasswordForm');
438
+ const loginTab = document.getElementById('loginTab');
439
+ const registerTab = document.getElementById('registerTab');
440
+ const loginBtn = document.getElementById('loginBtn');
441
+ const registerBtn = document.getElementById('registerBtn');
442
+ const forgotPassword = document.getElementById('forgotPassword');
443
+ const resetPasswordBtn = document.getElementById('resetPasswordBtn');
444
+ const backToLogin = document.getElementById('backToLogin');
445
+ const closeAuthModal = document.getElementById('closeAuthModal');
446
+ const toggleLoginPassword = document.getElementById('toggleLoginPassword');
447
+ const toggleRegisterPassword = document.getElementById('toggleRegisterPassword');
448
+ const loginUsername = document.getElementById('loginUsername');
449
+ const loginPassword = document.getElementById('loginPassword');
450
+ const fullName = document.getElementById('fullName');
451
+ const birthDate = document.getElementById('birthDate');
452
+ const location = document.getElementById('location');
453
+ const username = document.getElementById('username');
454
+ const password = document.getElementById('password');
455
+ const forgotUsername = document.getElementById('forgotUsername');
456
+ const forgotEmail = document.getElementById('forgotEmail');
457
+ const userMenuButton = document.getElementById('userMenuButton');
458
+ const userDropdown = document.getElementById('userDropdown');
459
+ const logoutLink = document.getElementById('logoutLink');
460
+ const profileLink = document.getElementById('profileLink');
461
+ const settingsLink = document.getElementById('settingsLink');
462
+ const userNameDisplay = document.getElementById('userNameDisplay');
463
+ const userAvatar = document.getElementById('userAvatar');
464
+ const currentUserAvatar = document.getElementById('currentUserAvatar');
465
+ const postContent = document.getElementById('postContent');
466
+ const postSubmit = document.getElementById('postSubmit');
467
+ const postImage = document.getElementById('postImage');
468
+ const postVideo = document.getElementById('postVideo');
469
+ const postAudio = document.getElementById('postAudio');
470
+ const postPreview = document.getElementById('postPreview');
471
+ const postsContainer = document.getElementById('postsContainer');
472
+ const startRecording = document.getElementById('startRecording');
473
+ const audioRecorder = document.getElementById('audioRecorder');
474
+ const stopRecording = document.getElementById('stopRecording');
475
+ const recordingTimer = document.getElementById('recordingTimer');
476
+ const createGroupBtn = document.getElementById('createGroupBtn');
477
+ const createGroupModal = document.getElementById('createGroupModal');
478
+ const closeGroupModal = document.getElementById('closeGroupModal');
479
+ const groupName = document.getElementById('groupName');
480
+ const groupDescription = document.getElementById('groupDescription');
481
+ const groupImage = document.getElementById('groupImage');
482
+ const createGroupSubmit = document.getElementById('createGroupSubmit');
483
+ const groupsList = document.getElementById('groupsList');
484
+ const groupsLink = document.getElementById('groupsLink');
485
+ const contactsList = document.getElementById('contactsList');
486
+ const settingsModal = document.getElementById('settingsModal');
487
+ const closeSettingsModal = document.getElementById('closeSettingsModal');
488
+ const settingsAvatar = document.getElementById('settingsAvatar');
489
+ const settingsAvatarInput = document.getElementById('settingsAvatarInput');
490
+ const settingsFullName = document.getElementById('settingsFullName');
491
+ const settingsUsername = document.getElementById('settingsUsername');
492
+ const settingsLocation = document.getElementById('settingsLocation');
493
+ const saveSettings = document.getElementById('saveSettings');
494
+ const changePasswordBtn = document.getElementById('changePasswordBtn');
495
+ const changePasswordModal = document.getElementById('changePasswordModal');
496
+ const closeChangePasswordModal = document.getElementById('closeChangePasswordModal');
497
+ const currentPassword = document.getElementById('currentPassword');
498
+ const newPassword = document.getElementById('newPassword');
499
+ const confirmNewPassword = document.getElementById('confirmNewPassword');
500
+ const submitPasswordChange = document.getElementById('submitPasswordChange');
501
+ const toggleCurrentPassword = document.getElementById('toggleCurrentPassword');
502
+ const toggleNewPassword = document.getElementById('toggleNewPassword');
503
+ const toggleConfirmNewPassword = document.getElementById('toggleConfirmNewPassword');
504
+ const blockedUsersBtn = document.getElementById('blockedUsersBtn');
505
+ const blockedUsersModal = document.getElementById('blockedUsersModal');
506
+ const closeBlockedUsersModal = document.getElementById('closeBlockedUsersModal');
507
+ const blockedUsersList = document.getElementById('blockedUsersList');
508
+ const noBlockedUsers = document.getElementById('noBlockedUsers');
509
+
510
+ // Initialize the app
511
+ function init() {
512
+ // Check if user is already logged in
513
+ const loggedInUser = localStorage.getItem('currentUser');
514
+ if (loggedInUser) {
515
+ state.currentUser = JSON.parse(loggedInUser);
516
+ renderApp();
517
+ } else {
518
+ authModal.classList.remove('hidden');
519
+ }
520
+
521
+ // Set up event listeners
522
+ setupEventListeners();
523
+ }
524
+
525
+ // Set up all event listeners
526
+ function setupEventListeners() {
527
+ // Auth modal
528
+ loginTab.addEventListener('click', () => {
529
+ loginTab.classList.add('border-blue-600', 'text-blue-600');
530
+ loginTab.classList.remove('text-gray-500');
531
+ registerTab.classList.add('text-gray-500');
532
+ registerTab.classList.remove('border-blue-600', 'text-blue-600');
533
+ loginForm.classList.remove('hidden');
534
+ registerForm.classList.add('hidden');
535
+ forgotPasswordForm.classList.add('hidden');
536
+ });
537
+
538
+ registerTab.addEventListener('click', () => {
539
+ registerTab.classList.add('border-blue-600', 'text-blue-600');
540
+ registerTab.classList.remove('text-gray-500');
541
+ loginTab.classList.add('text-gray-500');
542
+ loginTab.classList.remove('border-blue-600', 'text-blue-600');
543
+ registerForm.classList.remove('hidden');
544
+ loginForm.classList.add('hidden');
545
+ forgotPasswordForm.classList.add('hidden');
546
+ });
547
+
548
+ loginBtn.addEventListener('click', handleLogin);
549
+ registerBtn.addEventListener('click', handleRegister);
550
+ forgotPassword.addEventListener('click', () => {
551
+ forgotPasswordForm.classList.remove('hidden');
552
+ loginForm.classList.add('hidden');
553
+ registerForm.classList.add('hidden');
554
+ });
555
+
556
+ resetPasswordBtn.addEventListener('click', handleResetPassword);
557
+ backToLogin.addEventListener('click', () => {
558
+ forgotPasswordForm.classList.add('hidden');
559
+ loginForm.classList.remove('hidden');
560
+ });
561
+
562
+ closeAuthModal.addEventListener('click', () => {
563
+ authModal.classList.add('hidden');
564
+ });
565
+
566
+ toggleLoginPassword.addEventListener('click', () => {
567
+ togglePasswordVisibility(loginPassword, toggleLoginPassword);
568
+ });
569
+
570
+ toggleRegisterPassword.addEventListener('click', () => {
571
+ togglePasswordVisibility(password, toggleRegisterPassword);
572
+ });
573
+
574
+ // Main app
575
+ userMenuButton.addEventListener('click', () => {
576
+ userDropdown.classList.toggle('hidden');
577
+ });
578
+
579
+ logoutLink.addEventListener('click', handleLogout);
580
+ settingsLink.addEventListener('click', () => {
581
+ userDropdown.classList.add('hidden');
582
+ openSettingsModal();
583
+ });
584
+
585
+ profileLink.addEventListener('click', () => {
586
+ userDropdown.classList.add('hidden');
587
+ // In a real app, this would navigate to the profile page
588
+ alert('Page de profil');
589
+ });
590
+
591
+ // Post creation
592
+ postSubmit.addEventListener('click', createPost);
593
+ postImage.addEventListener('change', handleFileSelect);
594
+ postVideo.addEventListener('change', handleFileSelect);
595
+ postAudio.addEventListener('change', handleFileSelect);
596
+ startRecording.addEventListener('click', startAudioRecording);
597
+ stopRecording.addEventListener('click', stopAudioRecording);
598
+
599
+ // Groups
600
+ createGroupBtn.addEventListener('click', () => {
601
+ createGroupModal.classList.remove('hidden');
602
+ });
603
+
604
+ closeGroupModal.addEventListener('click', () => {
605
+ createGroupModal.classList.add('hidden');
606
+ });
607
+
608
+ createGroupSubmit.addEventListener('click', createGroup);
609
+ groupsLink.addEventListener('click', () => {
610
+ // In a real app, this would navigate to the groups page
611
+ alert('Page des groupes');
612
+ });
613
+
614
+ // Settings
615
+ closeSettingsModal.addEventListener('click', () => {
616
+ settingsModal.classList.add('hidden');
617
+ });
618
+
619
+ saveSettings.addEventListener('click', saveUserSettings);
620
+ settingsAvatarInput.addEventListener('change', handleAvatarChange);
621
+
622
+ changePasswordBtn.addEventListener('click', () => {
623
+ settingsModal.classList.add('hidden');
624
+ changePasswordModal.classList.remove('hidden');
625
+ });
626
+
627
+ closeChangePasswordModal.addEventListener('click', () => {
628
+ changePasswordModal.classList.add('hidden');
629
+ settingsModal.classList.remove('hidden');
630
+ });
631
+
632
+ submitPasswordChange.addEventListener('click', changePassword);
633
+
634
+ toggleCurrentPassword.addEventListener('click', () => {
635
+ togglePasswordVisibility(currentPassword, toggleCurrentPassword);
636
+ });
637
+
638
+ toggleNewPassword.addEventListener('click', () => {
639
+ togglePasswordVisibility(newPassword, toggleNewPassword);
640
+ });
641
+
642
+ toggleConfirmNewPassword.addEventListener('click', () => {
643
+ togglePasswordVisibility(confirmNewPassword, toggleConfirmNewPassword);
644
+ });
645
+
646
+ // Blocked users
647
+ blockedUsersBtn.addEventListener('click', () => {
648
+ settingsModal.classList.add('hidden');
649
+ blockedUsersModal.classList.remove('hidden');
650
+ renderBlockedUsers();
651
+ });
652
+
653
+ closeBlockedUsersModal.addEventListener('click', () => {
654
+ blockedUsersModal.classList.add('hidden');
655
+ settingsModal.classList.remove('hidden');
656
+ });
657
+
658
+ // Click outside dropdowns to close them
659
+ document.addEventListener('click', (e) => {
660
+ if (!userMenuButton.contains(e.target) && !userDropdown.contains(e.target)) {
661
+ userDropdown.classList.add('hidden');
662
+ }
663
+ });
664
+ }
665
+
666
+ // Toggle password visibility
667
+ function togglePasswordVisibility(passwordField, toggleButton) {
668
+ if (passwordField.type === 'password') {
669
+ passwordField.type = 'text';
670
+ toggleButton.innerHTML = '<i class="fas fa-eye-slash"></i>';
671
+ } else {
672
+ passwordField.type = 'password';
673
+ toggleButton.innerHTML = '<i class="fas fa-eye"></i>';
674
+ }
675
+ }
676
+
677
+ // Handle login
678
+ function handleLogin() {
679
+ const username = loginUsername.value.trim();
680
+ const password = loginPassword.value.trim();
681
+
682
+ if (!username || !password) {
683
+ alert('Veuillez remplir tous les champs');
684
+ return;
685
+ }
686
+
687
+ const user = state.users.find(u =>
688
+ (u.username === username || u.fullName === username) && u.password === password
689
+ );
690
+
691
+ if (user) {
692
+ state.currentUser = user;
693
+ localStorage.setItem('currentUser', JSON.stringify(user));
694
+ authModal.classList.add('hidden');
695
+ renderApp();
696
+ } else {
697
+ alert('Nom d\'utilisateur ou mot de passe incorrect');
698
+ }
699
+ }
700
+
701
+ // Handle registration
702
+ function handleRegister() {
703
+ const fullNameValue = fullName.value.trim();
704
+ const birthDateValue = birthDate.value;
705
+ const locationValue = location.value.trim();
706
+ const usernameValue = username.value.trim();
707
+ const passwordValue = password.value.trim();
708
+
709
+ if (!fullNameValue || !birthDateValue || !locationValue || !passwordValue) {
710
+ alert('Veuillez remplir tous les champs obligatoires');
711
+ return;
712
+ }
713
+
714
+ // Check if username is already taken
715
+ if (usernameValue && state.users.some(u => u.username === usernameValue)) {
716
+ alert('Ce nom d\'utilisateur est déjà pris');
717
+ return;
718
+ }
719
+
720
+ const newUser = {
721
+ id: Date.now().toString(),
722
+ fullName: fullNameValue,
723
+ birthDate: birthDateValue,
724
+ location: locationValue,
725
+ username: usernameValue || fullNameValue.toLowerCase().replace(/\s+/g, ''),
726
+ password: passwordValue,
727
+ avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(fullNameValue)}&background=random`,
728
+ joinedDate: new Date().toISOString()
729
+ };
730
+
731
+ state.users.push(newUser);
732
+ localStorage.setItem('users', JSON.stringify(state.users));
733
+
734
+ state.currentUser = newUser;
735
+ localStorage.setItem('currentUser', JSON.stringify(newUser));
736
+
737
+ authModal.classList.add('hidden');
738
+ renderApp();
739
+
740
+ alert('Inscription réussie ! Bienvenue sur SBI BOOK');
741
+ }
742
+
743
+ // Handle password reset
744
+ function handleResetPassword() {
745
+ const usernameValue = forgotUsername.value.trim();
746
+ const emailValue = forgotEmail.value.trim();
747
+
748
+ if (!usernameValue || !emailValue) {
749
+ alert('Veuillez remplir tous les champs');
750
+ return;
751
+ }
752
+
753
+ const user = state.users.find(u =>
754
+ (u.username === usernameValue || u.fullName === usernameValue) &&
755
+ u.email === emailValue
756
+ );
757
+
758
+ if (user) {
759
+ // In a real app, we would send a reset link to the email
760
+ alert('Un lien de réinitialisation a été envoyé à votre email');
761
+ forgotPasswordForm.classList.add('hidden');
762
+ loginForm.classList.remove('hidden');
763
+ } else {
764
+ alert('Aucun compte trouvé avec ces informations');
765
+ }
766
+ }
767
+
768
+ // Handle logout
769
+ function handleLogout() {
770
+ state.currentUser = null;
771
+ localStorage.removeItem('currentUser');
772
+ app.classList.add('hidden');
773
+ authModal.classList.remove('hidden');
774
+
775
+ // Reset forms
776
+ loginUsername.value = '';
777
+ loginPassword.value = '';
778
+ fullName.value = '';
779
+ birthDate.value = '';
780
+ location.value = '';
781
+ username.value = '';
782
+ password.value = '';
783
+
784
+ // Show login tab
785
+ loginTab.click();
786
+ }
787
+
788
+ // Render the main app
789
+ function renderApp() {
790
+ app.classList.remove('hidden');
791
+
792
+ // Set user info
793
+ userNameDisplay.textContent = state.currentUser.fullName;
794
+ userAvatar.src = state.currentUser.avatar;
795
+ currentUserAvatar.src = state.currentUser.avatar;
796
+
797
+ // Render posts
798
+ renderPosts();
799
+
800
+ // Render groups
801
+ renderGroups();
802
+
803
+ // Render contacts (non-blocked users)
804
+ renderContacts();
805
+ }
806
+
807
+ // Render posts
808
+ function renderPosts() {
809
+ postsContainer.innerHTML = '';
810
+
811
+ // Filter posts to exclude those from blocked users
812
+ const visiblePosts = state.posts.filter(post => {
813
+ return !state.blockedUsers[state.currentUser.id]?.includes(post.userId);
814
+ });
815
+
816
+ if (visiblePosts.length === 0) {
817
+ postsContainer.innerHTML = `
818
+ <div class="bg-white rounded-lg shadow p-6 text-center">
819
+ <p class="text-gray-500">Aucune publication à afficher. Soyez le premier à publier !</p>
820
+ </div>
821
+ `;
822
+ return;
823
+ }
824
+
825
+ visiblePosts.forEach(post => {
826
+ const postUser = state.users.find(u => u.id === post.userId) || {
827
+ fullName: 'Utilisateur inconnu',
828
+ avatar: 'https://ui-avatars.com/api/?name=Unknown&background=random'
829
+ };
830
+
831
+ const postElement = document.createElement('div');
832
+ postElement.className = 'bg-white rounded-lg shadow p-4';
833
+ postElement.dataset.postId = post.id;
834
+
835
+ let mediaElement = '';
836
+ if (post.media) {
837
+ if (post.media.type.startsWith('image')) {
838
+ mediaElement = `
839
+ <div class="mt-3">
840
+ <img src="${post.media.url}" alt="Post image" class="w-full rounded-lg">
841
+ </div>
842
+ `;
843
+ } else if (post.media.type.startsWith('video')) {
844
+ mediaElement = `
845
+ <div class="mt-3">
846
+ <video controls class="w-full rounded-lg">
847
+ <source src="${post.media.url}" type="${post.media.type}">
848
+ Votre navigateur ne supporte pas les vidéos.
849
+ </video>
850
+ </div>
851
+ `;
852
+ } else if (post.media.type.startsWith('audio')) {
853
+ mediaElement = `
854
+ <div class="mt-3">
855
+ <audio controls class="w-full">
856
+ <source src="${post.media.url}" type="${post.media.type}">
857
+ Votre navigateur ne supporte pas les audios.
858
+ </audio>
859
+ </div>
860
+ `;
861
+ }
862
+ }
863
+
864
+ let groupInfo = '';
865
+ if (post.groupId) {
866
+ const group = state.groups.find(g => g.id === post.groupId);
867
+ if (group) {
868
+ groupInfo = `
869
+ <div class="text-sm text-gray-500 mb-2">
870
+ <i class="fas fa-users mr-1"></i> Publié dans ${group.name}
871
+ </div>
872
+ `;
873
+ }
874
+ }
875
+
876
+ const isLiked = post.likes?.includes(state.currentUser.id) || false;
877
+
878
+ postElement.innerHTML = `
879
+ <div class="flex items-start space-x-3">
880
+ <div class="w-10 h-10 rounded-full bg-gray-300 overflow-hidden relative">
881
+ <img src="${postUser.avatar}" alt="Profile" class="w-full h-full object-cover">
882
+ <div class="online-status"></div>
883
+ </div>
884
+ <div class="flex-1">
885
+ <div class="flex justify-between items-start">
886
+ <div>
887
+ <h3 class="font-medium">${postUser.fullName}</h3>
888
+ <p class="text-xs text-gray-500">${formatDate(post.createdAt)}</p>
889
+ </div>
890
+ <div class="relative">
891
+ <button class="post-options text-gray-500 hover:text-gray-700">
892
+ <i class="fas fa-ellipsis-h"></i>
893
+ </button>
894
+ <div class="post-options-menu hidden absolute right-0 mt-1 w-40 bg-white rounded-md shadow-lg py-1 z-10">
895
+ ${post.userId === state.currentUser.id ? `
896
+ <a href="#" class="block px-4 py-2 text-gray-800 hover:bg-gray-100 delete-post">Supprimer</a>
897
+ ` : `
898
+ <a href="#" class="block px-4 py-2 text-gray-800 hover:bg-gray-100 block-user">Bloquer cet utilisateur</a>
899
+ `}
900
+ </div>
901
+ </div>
902
+ </div>
903
+ ${groupInfo}
904
+ <p class="mt-2">${post.content}</p>
905
+ ${mediaElement}
906
+ <div class="mt-3 flex justify-between text-gray-500 border-t pt-3">
907
+ <button class="like-btn flex items-center space-x-1 ${isLiked ? 'text-blue-600' : ''}">
908
+ <i class="fas fa-thumbs-up"></i>
909
+ <span>${post.likes?.length || 0}</span>
910
+ </button>
911
+ <button class="comment-btn flex items-center space-x-1">
912
+ <i class="fas fa-comment"></i>
913
+ <span>${post.comments?.length || 0}</span>
914
+ </button>
915
+ <button class="share-btn flex items-center space-x-1">
916
+ <i class="fas fa-share"></i>
917
+ <span>Partager</span>
918
+ </button>
919
+ ${post.media ? `
920
+ <a href="${post.media.url}" download class="flex items-center space-x-1 text-green-600">
921
+ <i class="fas fa-download"></i>
922
+ <span>Télécharger</span>
923
+ </a>
924
+ ` : ''}
925
+ </div>
926
+ </div>
927
+ </div>
928
+ <div class="post-comments mt-3 hidden">
929
+ <!-- Comments will be loaded here -->
930
+ </div>
931
+ <div class="mt-3 flex items-center">
932
+ <div class="w-8 h-8 rounded-full bg-gray-300 overflow-hidden mr-2">
933
+ <img src="${state.currentUser.avatar}" alt="Profile" class="w-full h-full object-cover">
934
+ </div>
935
+ <input type="text" placeholder="Écrire un commentaire..." class="comment-input flex-1 px-3 py-2 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500">
936
+ </div>
937
+ `;
938
+
939
+ postsContainer.appendChild(postElement);
940
+
941
+ // Add event listeners for this post
942
+ const likeBtn = postElement.querySelector('.like-btn');
943
+ const commentBtn = postElement.querySelector('.comment-btn');
944
+ const shareBtn = postElement.querySelector('.share-btn');
945
+ const commentInput = postElement.querySelector('.comment-input');
946
+ const postOptionsBtn = postElement.querySelector('.post-options');
947
+ const postOptionsMenu = postElement.querySelector('.post-options-menu');
948
+ const deletePostBtn = postElement.querySelector('.delete-post');
949
+ const blockUserBtn = postElement.querySelector('.block-user');
950
+
951
+ likeBtn.addEventListener('click', () => toggleLike(post.id));
952
+ commentBtn.addEventListener('click', () => toggleComments(postElement, post.id));
953
+ shareBtn.addEventListener('click', () => sharePost(post.id));
954
+
955
+ commentInput.addEventListener('keypress', (e) => {
956
+ if (e.key === 'Enter' && commentInput.value.trim()) {
957
+ addComment(post.id, commentInput.value.trim());
958
+ commentInput.value = '';
959
+ }
960
+ });
961
+
962
+ postOptionsBtn.addEventListener('click', () => {
963
+ postOptionsMenu.classList.toggle('hidden');
964
+ });
965
+
966
+ if (deletePostBtn) {
967
+ deletePostBtn.addEventListener('click', () => {
968
+ if (confirm('Supprimer cette publication ?')) {
969
+ deletePost(post.id);
970
+ }
971
+ });
972
+ }
973
+
974
+ if (blockUserBtn) {
975
+ blockUserBtn.addEventListener('click', () => {
976
+ if (confirm(`Bloquer ${postUser.fullName} ? Vous ne verrez plus ses publications.`)) {
977
+ blockUser(post.userId);
978
+ }
979
+ });
980
+ }
981
+ });
982
+ }
983
+
984
+ // Render groups
985
+ function renderGroups() {
986
+ groupsList.innerHTML = '';
987
+
988
+ if (state.groups.length === 0) {
989
+ groupsList.innerHTML = `
990
+ <div class="text-gray-500 text-sm">Aucun groupe disponible</div>
991
+ `;
992
+ return;
993
+ }
994
+
995
+ state.groups.forEach(group => {
996
+ const groupElement = document.createElement('div');
997
+ groupElement.className = 'flex items-center space-x-2 p-2 hover:bg-gray-100 rounded-lg cursor-pointer';
998
+ groupElement.dataset.groupId = group.id;
999
+
1000
+ groupElement.innerHTML = `
1001
+ <div class="w-10 h-10 rounded-full bg-gray-300 overflow-hidden">
1002
+ <img src="${group.image || 'https://ui-avatars.com/api/?name=' + encodeURIComponent(group.name) + '&background=random'}" alt="${group.name}" class="w-full h-full object-cover">
1003
+ </div>
1004
+ <div>
1005
+ <h4 class="font-medium">${group.name}</h4>
1006
+ <p class="text-xs text-gray-500">${group.members.length} membres</p>
1007
+ </div>
1008
+ `;
1009
+
1010
+ groupElement.addEventListener('click', () => {
1011
+ filterPostsByGroup(group.id);
1012
+ });
1013
+
1014
+ groupsList.appendChild(groupElement);
1015
+ });
1016
+ }
1017
+
1018
+ // Render contacts
1019
+ function renderContacts() {
1020
+ contactsList.innerHTML = '';
1021
+
1022
+ // Filter out current user and blocked users
1023
+ const contacts = state.users.filter(user =>
1024
+ user.id !== state.currentUser.id &&
1025
+ !state.blockedUsers[state.currentUser.id]?.includes(user.id)
1026
+ );
1027
+
1028
+ if (contacts.length === 0) {
1029
+ contactsList.innerHTML = `
1030
+ <div class="text-gray-500 text-sm">Aucun contact disponible</div>
1031
+ `;
1032
+ return;
1033
+ }
1034
+
1035
+ contacts.forEach(user => {
1036
+ const contactElement = document.createElement('div');
1037
+ contactElement.className = 'flex items-center space-x-2 p-2 hover:bg-gray-100 rounded-lg cursor-pointer';
1038
+ contactElement.dataset.userId = user.id;
1039
+
1040
+ contactElement.innerHTML = `
1041
+ <div class="w-10 h-10 rounded-full bg-gray-300 overflow-hidden relative">
1042
+ <img src="${user.avatar}" alt="${user.fullName}" class="w-full h-full object-cover">
1043
+ <div class="online-status"></div>
1044
+ </div>
1045
+ <div>
1046
+ <h4 class="font-medium">${user.fullName}</h4>
1047
+ <p class="text-xs text-gray-500">${user.location}</p>
1048
+ </div>
1049
+ `;
1050
+
1051
+ contactElement.addEventListener('click', () => {
1052
+ // In a real app, this would open a chat or profile
1053
+ alert(`Profil de ${user.fullName}`);
1054
+ });
1055
+
1056
+ contactsList.appendChild(contactElement);
1057
+ });
1058
+ }
1059
+
1060
+ // Render comments for a post
1061
+ function renderComments(postElement, postId) {
1062
+ const post = state.posts.find(p => p.id === postId);
1063
+ if (!post || !post.comments) return;
1064
+
1065
+ const commentsContainer = postElement.querySelector('.post-comments');
1066
+ commentsContainer.innerHTML = '';
1067
+
1068
+ post.comments.forEach(comment => {
1069
+ const commentUser = state.users.find(u => u.id === comment.userId) || {
1070
+ fullName: 'Utilisateur inconnu',
1071
+ avatar: 'https://ui-avatars.com/api/?name=Unknown&background=random'
1072
+ };
1073
+
1074
+ const isLiked = comment.likes?.includes(state.currentUser.id) || false;
1075
+ const isDisliked = comment.dislikes?.includes(state.currentUser.id) || false;
1076
+
1077
+ const commentElement = document.createElement('div');
1078
+ commentElement.className = 'mb-3';
1079
+ commentElement.dataset.commentId = comment.id;
1080
+
1081
+ commentElement.innerHTML = `
1082
+ <div class="flex items-start space-x-2">
1083
+ <div class="w-8 h-8 rounded-full bg-gray-300 overflow-hidden">
1084
+ <img src="${commentUser.avatar}" alt="Profile" class="w-full h-full object-cover">
1085
+ </div>
1086
+ <div class="flex-1">
1087
+ <div class="bg-gray-100 rounded-lg p-2">
1088
+ <div class="flex justify-between items-start">
1089
+ <div>
1090
+ <h4 class="font-medium text-sm">${commentUser.fullName}</h4>
1091
+ <p class="text-xs text-gray-500">${formatDate(comment.createdAt)}</p>
1092
+ </div>
1093
+ ${comment.userId === state.currentUser.id ? `
1094
+ <div class="flex space-x-1">
1095
+ <button class="edit-comment text-gray-500 hover:text-gray-700 text-xs">
1096
+ <i class="fas fa-edit"></i>
1097
+ </button>
1098
+ <button class="delete-comment text-gray-500 hover:text-gray-700 text-xs">
1099
+ <i class="fas fa-trash"></i>
1100
+ </button>
1101
+ </div>
1102
+ ` : ''}
1103
+ </div>
1104
+ <p class="mt-1">${comment.content}</p>
1105
+ </div>
1106
+ <div class="flex items-center space-x-3 mt-1 ml-2 text-xs">
1107
+ <button class="like-comment flex items-center space-x-1 ${isLiked ? 'text-blue-600' : ''}">
1108
+ <i class="fas fa-thumbs-up"></i>
1109
+ <span>${comment.likes?.length || 0}</span>
1110
+ </button>
1111
+ <button class="dislike-comment flex items-center space-x-1 ${isDisliked ? 'text-red-600' : ''}">
1112
+ <i class="fas fa-thumbs-down"></i>
1113
+ <span>${comment.dislikes?.length || 0}</span>
1114
+ </button>
1115
+ <button class="reply-comment text-gray-500 hover:text-gray-700">
1116
+ Répondre
1117
+ </button>
1118
+ </div>
1119
+ <div class="comment-reply-input mt-2 hidden">
1120
+ <div class="flex items-center">
1121
+ <div class="w-6 h-6 rounded-full bg-gray-300 overflow-hidden mr-2">
1122
+ <img src="${state.currentUser.avatar}" alt="Profile" class="w-full h-full object-cover">
1123
+ </div>
1124
+ <input type="text" placeholder="Écrire une réponse..." class="flex-1 px-2 py-1 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 text-xs">
1125
+ </div>
1126
+ </div>
1127
+ </div>
1128
+ </div>
1129
+ `;
1130
+
1131
+ commentsContainer.appendChild(commentElement);
1132
+
1133
+ // Add event listeners for this comment
1134
+ const likeBtn = commentElement.querySelector('.like-comment');
1135
+ const dislikeBtn = commentElement.querySelector('.dislike-comment');
1136
+ const replyBtn = commentElement.querySelector('.reply-comment');
1137
+ const editBtn = commentElement.querySelector('.edit-comment');
1138
+ const deleteBtn = commentElement.querySelector('.delete-comment');
1139
+
1140
+ likeBtn.addEventListener('click', () => toggleCommentLike(postId, comment.id));
1141
+ dislikeBtn.addEventListener('click', () => toggleCommentDislike(postId, comment.id));
1142
+ replyBtn.addEventListener('click', () => toggleReplyInput(commentElement));
1143
+
1144
+ if (editBtn) {
1145
+ editBtn.addEventListener('click', () => editComment(postId, comment.id));
1146
+ }
1147
+
1148
+ if (deleteBtn) {
1149
+ deleteBtn.addEventListener('click', () => {
1150
+ if (confirm('Supprimer ce commentaire ?')) {
1151
+ deleteComment(postId, comment.id);
1152
+ }
1153
+ });
1154
+ }
1155
+
1156
+ // Render replies if any
1157
+ if (comment.replies && comment.replies.length > 0) {
1158
+ renderReplies(commentElement, postId, comment.id, comment.replies);
1159
+ }
1160
+ });
1161
+ }
1162
+
1163
+ // Render replies for a comment
1164
+ function renderReplies(commentElement, postId, commentId, replies) {
1165
+ const repliesContainer = document.createElement('div');
1166
+ repliesContainer.className = 'comment-reply mt-2';
1167
+
1168
+ replies.forEach(reply => {
1169
+ const replyUser = state.users.find(u => u.id === reply.userId) || {
1170
+ fullName: 'Utilisateur inconnu',
1171
+ avatar: 'https://ui-avatars.com/api/?name=Unknown&background=random'
1172
+ };
1173
+
1174
+ const replyElement = document.createElement('div');
1175
+ replyElement.className = 'mb-2';
1176
+ replyElement.dataset.replyId = reply.id;
1177
+
1178
+ replyElement.innerHTML = `
1179
+ <div class="flex items-start space-x-2">
1180
+ <div class="w-6 h-6 rounded-full bg-gray-300 overflow-hidden">
1181
+ <img src="${replyUser.avatar}" alt="Profile" class="w-full h-full object-cover">
1182
+ </div>
1183
+ <div class="flex-1">
1184
+ <div class="bg-gray-100 rounded-lg p-2">
1185
+ <div class="flex justify-between items-start">
1186
+ <div>
1187
+ <h4 class="font-medium text-xs">${replyUser.fullName}</h4>
1188
+ <p class="text-xs text-gray-500">${formatDate(reply.createdAt)}</p>
1189
+ </div>
1190
+ ${reply.userId === state.currentUser.id ? `
1191
+ <div class="flex space-x-1">
1192
+ <button class="edit-reply text-gray-500 hover:text-gray-700 text-xs">
1193
+ <i class="fas fa-edit"></i>
1194
+ </button>
1195
+ <button class="delete-reply text-gray-500 hover:text-gray-700 text-xs">
1196
+ <i class="fas fa-trash"></i>
1197
+ </button>
1198
+ </div>
1199
+ ` : ''}
1200
+ </div>
1201
+ <p class="mt-1 text-xs">${reply.content}</p>
1202
+ </div>
1203
+ </div>
1204
+ </div>
1205
+ `;
1206
+
1207
+ repliesContainer.appendChild(replyElement);
1208
+
1209
+ // Add event listeners for this reply
1210
+ const editBtn = replyElement.querySelector('.edit-reply');
1211
+ const deleteBtn = replyElement.querySelector('.delete-reply');
1212
+
1213
+ if (editBtn) {
1214
+ editBtn.addEventListener('click', () => editReply(postId, commentId, reply.id));
1215
+ }
1216
+
1217
+ if (deleteBtn) {
1218
+ deleteBtn.addEventListener('click', () => {
1219
+ if (confirm('Supprimer cette réponse ?')) {
1220
+ deleteReply(postId, commentId, reply.id);
1221
+ }
1222
+ });
1223
+ }
1224
+ });
1225
+
1226
+ commentElement.appendChild(repliesContainer);
1227
+ }
1228
+
1229
+ // Render blocked users
1230
+ function renderBlockedUsers() {
1231
+ blockedUsersList.innerHTML = '';
1232
+
1233
+ const blockedUserIds = state.blockedUsers[state.currentUser.id] || [];
1234
+
1235
+ if (blockedUserIds.length === 0) {
1236
+ noBlockedUsers.classList.remove('hidden');
1237
+ return;
1238
+ }
1239
+
1240
+ noBlockedUsers.classList.add('hidden');
1241
+
1242
+ blockedUserIds.forEach(userId => {
1243
+ const user = state.users.find(u => u.id === userId);
1244
+ if (!user) return;
1245
+
1246
+ const blockedUserElement = document.createElement('div');
1247
+ blockedUserElement.className = 'flex items-center justify-between p-2 bg-gray-100 rounded-lg';
1248
+ blockedUserElement.dataset.userId = userId;
1249
+
1250
+ blockedUserElement.innerHTML = `
1251
+ <div class="flex items-center space-x-2">
1252
+ <div class="w-8 h-8 rounded-full bg-gray-300 overflow-hidden">
1253
+ <img src="${user.avatar}" alt="${user.fullName}" class="w-full h-full object-cover">
1254
+ </div>
1255
+ <div>
1256
+ <h4 class="font-medium">${user.fullName}</h4>
1257
+ <p class="text-xs text-gray-500">Bloqué</p>
1258
+ </div>
1259
+ </div>
1260
+ <button class="unblock-user text-blue-600 hover:text-blue-800">
1261
+ Débloquer
1262
+ </button>
1263
+ `;
1264
+
1265
+ blockedUsersList.appendChild(blockedUserElement);
1266
+
1267
+ // Add event listener for unblock button
1268
+ const unblockBtn = blockedUserElement.querySelector('.unblock-user');
1269
+ unblockBtn.addEventListener('click', () => {
1270
+ unblockUser(userId);
1271
+ });
1272
+ });
1273
+ }
1274
+
1275
+ // Create a new post
1276
+ function createPost() {
1277
+ const content = postContent.value.trim();
1278
+ const mediaFile = postImage.files[0] || postVideo.files[0] || postAudio.files[0];
1279
+
1280
+ if (!content && !mediaFile) {
1281
+ alert('Veuillez ajouter du texte ou un média');
1282
+ return;
1283
+ }
1284
+
1285
+ const newPost = {
1286
+ id: Date.now().toString(),
1287
+ userId: state.currentUser.id,
1288
+ content: content,
1289
+ createdAt: new Date().toISOString(),
1290
+ likes: [],
1291
+ comments: []
1292
+ };
1293
+
1294
+ if (mediaFile) {
1295
+ // In a real app, we would upload the file to a server
1296
+ // Here we'll just create a local URL for demonstration
1297
+ const fileUrl = URL.createObjectURL(mediaFile);
1298
+
1299
+ newPost.media = {
1300
+ type: mediaFile.type,
1301
+ url: fileUrl,
1302
+ name: mediaFile.name,
1303
+ size: mediaFile.size
1304
+ };
1305
+
1306
+ // Check file size (20MB max)
1307
+ if (mediaFile.size > 20 * 1024 * 1024) {
1308
+ alert('Le fichier est trop volumineux (max 20MB)');
1309
+ return;
1310
+ }
1311
+ }
1312
+
1313
+ state.posts.unshift(newPost);
1314
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1315
+
1316
+ // Reset form
1317
+ postContent.value = '';
1318
+ postPreview.classList.add('hidden');
1319
+ postPreview.innerHTML = '';
1320
+ postImage.value = '';
1321
+ postVideo.value = '';
1322
+ postAudio.value = '';
1323
+
1324
+ // Re-render posts
1325
+ renderPosts();
1326
+ }
1327
+
1328
+ // Handle file selection for posts
1329
+ function handleFileSelect(e) {
1330
+ const file = e.target.files[0];
1331
+ if (!file) return;
1332
+
1333
+ // Clear other file inputs
1334
+ if (e.target.id === 'postImage') {
1335
+ postVideo.value = '';
1336
+ postAudio.value = '';
1337
+ } else if (e.target.id === 'postVideo') {
1338
+ postImage.value = '';
1339
+ postAudio.value = '';
1340
+ } else if (e.target.id === 'postAudio') {
1341
+ postImage.value = '';
1342
+ postVideo.value = '';
1343
+ }
1344
+
1345
+ // Check file size (20MB max)
1346
+ if (file.size > 20 * 1024 * 1024) {
1347
+ alert('Le fichier est trop volumineux (max 20MB)');
1348
+ e.target.value = '';
1349
+ return;
1350
+ }
1351
+
1352
+ // Show preview
1353
+ postPreview.classList.remove('hidden');
1354
+
1355
+ if (file.type.startsWith('image')) {
1356
+ const reader = new FileReader();
1357
+ reader.onload = (e) => {
1358
+ postPreview.innerHTML = `
1359
+ <img src="${e.target.result}" alt="Preview" class="w-full rounded-lg">
1360
+ `;
1361
+ };
1362
+ reader.readAsDataURL(file);
1363
+ } else if (file.type.startsWith('video')) {
1364
+ const videoUrl = URL.createObjectURL(file);
1365
+ postPreview.innerHTML = `
1366
+ <video controls class="w-full rounded-lg">
1367
+ <source src="${videoUrl}" type="${file.type}">
1368
+ Votre navigateur ne supporte pas les vidéos.
1369
+ </video>
1370
+ `;
1371
+ } else if (file.type.startsWith('audio')) {
1372
+ const audioUrl = URL.createObjectURL(file);
1373
+ postPreview.innerHTML = `
1374
+ <audio controls class="w-full">
1375
+ <source src="${audioUrl}" type="${file.type}">
1376
+ Votre navigateur ne supporte pas les audios.
1377
+ </audio>
1378
+ `;
1379
+ }
1380
+ }
1381
+
1382
+ // Start audio recording
1383
+ function startAudioRecording() {
1384
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
1385
+ navigator.mediaDevices.getUserMedia({ audio: true })
1386
+ .then(stream => {
1387
+ state.audioRecorder = new MediaRecorder(stream);
1388
+ const audioChunks = [];
1389
+
1390
+ state.audioRecorder.ondataavailable = e => {
1391
+ audioChunks.push(e.data);
1392
+ };
1393
+
1394
+ state.audioRecorder.onstop = () => {
1395
+ const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
1396
+
1397
+ // Create a preview of the audio recording
1398
+ postPreview.classList.remove('hidden');
1399
+ const audioUrl = URL.createObjectURL(audioBlob);
1400
+ postPreview.innerHTML = `
1401
+ <audio controls class="w-full">
1402
+ <source src="${audioUrl}" type="audio/wav">
1403
+ Votre navigateur ne supporte pas les audios.
1404
+ </audio>
1405
+ `;
1406
+
1407
+ // Create a file object to be used in the post
1408
+ const audioFile = new File([audioBlob], 'recording.wav', { type: 'audio/wav' });
1409
+
1410
+ // Create a fake file input with the recording
1411
+ const dataTransfer = new DataTransfer();
1412
+ dataTransfer.items.add(audioFile);
1413
+ postAudio.files = dataTransfer.files;
1414
+
1415
+ // Stop all tracks
1416
+ stream.getTracks().forEach(track => track.stop());
1417
+ };
1418
+
1419
+ // Start recording
1420
+ state.audioRecorder.start();
1421
+ state.recordingStartTime = Date.now();
1422
+
1423
+ // Update timer
1424
+ state.recordingTimer = setInterval(() => {
1425
+ const elapsed = Math.floor((Date.now() - state.recordingStartTime) / 1000);
1426
+ const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
1427
+ const seconds = (elapsed % 60).toString().padStart(2, '0');
1428
+ recordingTimer.textContent = `${minutes}:${seconds}`;
1429
+ }, 1000);
1430
+
1431
+ // Show recorder UI
1432
+ audioRecorder.classList.remove('hidden');
1433
+ })
1434
+ .catch(err => {
1435
+ console.error('Error accessing microphone:', err);
1436
+ alert('Impossible d\'accéder au microphone');
1437
+ });
1438
+ } else {
1439
+ alert('L\'enregistrement audio n\'est pas supporté par votre navigateur');
1440
+ }
1441
+ }
1442
+
1443
+ // Stop audio recording
1444
+ function stopAudioRecording() {
1445
+ if (state.audioRecorder && state.audioRecorder.state !== 'inactive') {
1446
+ state.audioRecorder.stop();
1447
+ clearInterval(state.recordingTimer);
1448
+ audioRecorder.classList.add('hidden');
1449
+ recordingTimer.textContent = '00:00';
1450
+ }
1451
+ }
1452
+
1453
+ // Toggle like on a post
1454
+ function toggleLike(postId) {
1455
+ const post = state.posts.find(p => p.id === postId);
1456
+ if (!post) return;
1457
+
1458
+ if (!post.likes) {
1459
+ post.likes = [];
1460
+ }
1461
+
1462
+ const likeIndex = post.likes.indexOf(state.currentUser.id);
1463
+ if (likeIndex === -1) {
1464
+ post.likes.push(state.currentUser.id);
1465
+ } else {
1466
+ post.likes.splice(likeIndex, 1);
1467
+ }
1468
+
1469
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1470
+ renderPosts();
1471
+ }
1472
+
1473
+ // Toggle comments visibility
1474
+ function toggleComments(postElement, postId) {
1475
+ const commentsContainer = postElement.querySelector('.post-comments');
1476
+ commentsContainer.classList.toggle('hidden');
1477
+
1478
+ if (!commentsContainer.classList.contains('hidden') && commentsContainer.innerHTML === '') {
1479
+ renderComments(postElement, postId);
1480
+ }
1481
+ }
1482
+
1483
+ // Share a post
1484
+ function sharePost(postId) {
1485
+ const post = state.posts.find(p => p.id === postId);
1486
+ if (!post) return;
1487
+
1488
+ const sharedPost = {
1489
+ id: Date.now().toString(),
1490
+ userId: state.currentUser.id,
1491
+ content: `Partagé depuis ${state.users.find(u => u.id === post.userId)?.fullName || 'un utilisateur'}: ${post.content}`,
1492
+ createdAt: new Date().toISOString(),
1493
+ sharedPostId: postId,
1494
+ likes: [],
1495
+ comments: []
1496
+ };
1497
+
1498
+ if (post.media) {
1499
+ sharedPost.media = {...post.media};
1500
+ }
1501
+
1502
+ state.posts.unshift(sharedPost);
1503
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1504
+
1505
+ renderPosts();
1506
+ alert('Publication partagée !');
1507
+ }
1508
+
1509
+ // Add a comment to a post
1510
+ function addComment(postId, content) {
1511
+ const post = state.posts.find(p => p.id === postId);
1512
+ if (!post) return;
1513
+
1514
+ if (!post.comments) {
1515
+ post.comments = [];
1516
+ }
1517
+
1518
+ const newComment = {
1519
+ id: Date.now().toString(),
1520
+ userId: state.currentUser.id,
1521
+ content: content,
1522
+ createdAt: new Date().toISOString(),
1523
+ likes: [],
1524
+ dislikes: [],
1525
+ replies: []
1526
+ };
1527
+
1528
+ post.comments.push(newComment);
1529
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1530
+
1531
+ // Find the post element and update its comments
1532
+ const postElement = document.querySelector(`[data-post-id="${postId}"]`);
1533
+ if (postElement) {
1534
+ const commentsContainer = postElement.querySelector('.post-comments');
1535
+ if (!commentsContainer.classList.contains('hidden')) {
1536
+ renderComments(postElement, postId);
1537
+ }
1538
+
1539
+ // Update comment count
1540
+ const commentBtn = postElement.querySelector('.comment-btn');
1541
+ if (commentBtn) {
1542
+ const countSpan = commentBtn.querySelector('span');
1543
+ countSpan.textContent = post.comments.length;
1544
+ }
1545
+ }
1546
+ }
1547
+
1548
+ // Toggle comment like
1549
+ function toggleCommentLike(postId, commentId) {
1550
+ const post = state.posts.find(p => p.id === postId);
1551
+ if (!post || !post.comments) return;
1552
+
1553
+ const comment = post.comments.find(c => c.id === commentId);
1554
+ if (!comment) return;
1555
+
1556
+ if (!comment.likes) {
1557
+ comment.likes = [];
1558
+ }
1559
+
1560
+ const likeIndex = comment.likes.indexOf(state.currentUser.id);
1561
+ if (likeIndex === -1) {
1562
+ comment.likes.push(state.currentUser.id);
1563
+
1564
+ // Remove from dislikes if present
1565
+ if (comment.dislikes) {
1566
+ const dislikeIndex = comment.dislikes.indexOf(state.currentUser.id);
1567
+ if (dislikeIndex !== -1) {
1568
+ comment.dislikes.splice(dislikeIndex, 1);
1569
+ }
1570
+ }
1571
+ } else {
1572
+ comment.likes.splice(likeIndex, 1);
1573
+ }
1574
+
1575
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1576
+
1577
+ // Find the comment element and update its likes/dislikes
1578
+ const commentElement = document.querySelector(`[data-comment-id="${commentId}"]`);
1579
+ if (commentElement) {
1580
+ const likeBtn = commentElement.querySelector('.like-comment');
1581
+ const dislikeBtn = commentElement.querySelector('.dislike-comment');
1582
+
1583
+ if (likeBtn) {
1584
+ likeBtn.classList.toggle('text-blue-600');
1585
+ const countSpan = likeBtn.querySelector('span');
1586
+ countSpan.textContent = comment.likes.length;
1587
+ }
1588
+
1589
+ if (dislikeBtn) {
1590
+ dislikeBtn.classList.remove('text-red-600');
1591
+ const countSpan = dislikeBtn.querySelector('span');
1592
+ countSpan.textContent = comment.dislikes?.length || 0;
1593
+ }
1594
+ }
1595
+ }
1596
+
1597
+ // Toggle comment dislike
1598
+ function toggleCommentDislike(postId, commentId) {
1599
+ const post = state.posts.find(p => p.id === postId);
1600
+ if (!post || !post.comments) return;
1601
+
1602
+ const comment = post.comments.find(c => c.id === commentId);
1603
+ if (!comment) return;
1604
+
1605
+ if (!comment.dislikes) {
1606
+ comment.dislikes = [];
1607
+ }
1608
+
1609
+ const dislikeIndex = comment.dislikes.indexOf(state.currentUser.id);
1610
+ if (dislikeIndex === -1) {
1611
+ comment.dislikes.push(state.currentUser.id);
1612
+
1613
+ // Remove from likes if present
1614
+ if (comment.likes) {
1615
+ const likeIndex = comment.likes.indexOf(state.currentUser.id);
1616
+ if (likeIndex !== -1) {
1617
+ comment.likes.splice(likeIndex, 1);
1618
+ }
1619
+ }
1620
+ } else {
1621
+ comment.dislikes.splice(dislikeIndex, 1);
1622
+ }
1623
+
1624
+ localStorage.setItem('posts', JSON.stringify(state.posts));
1625
+
1626
+ // Find the comment element and update its likes/dislikes
1627
+ const commentElement = document.querySelector(`[data-comment-id="${commentId}"]`);
1628
+ if (commentElement) {
1629
+ const dislikeBtn = commentElement.querySelector('.dislike-comment');
1630
+ const likeBtn = commentElement.querySelector('.like-comment');
1631
+
1632
+ if (dislikeBtn) {
1633
+ dislikeBtn.classList.toggle('text-red-600');
1634
+ const countSpan = dislikeBtn.querySelector('span');
1635
+ countSpan.textContent = comment.dislikes.length;
1636
+ }
1637
+
1638
+ if (likeBtn) {
1639
+ likeBtn.classList.remove('text-blue-600');
1640
+ const countSpan = likeBtn.querySelector('span');
1641
+ countSpan.textContent = comment.likes?.length || 0;
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ // Toggle reply input for a comment
1647
+ function toggleReplyInput(commentElement) {
1648
+ const replyInput = commentElement.querySelector('.comment-reply-input');
1649
+ replyInput.classList.toggle('hidden');
1650
+
1651
+ if (!replyInput.classList.contains('hidden')) {
1652
+ const input = replyInput.querySelector('input');
1653
+ input.focus();
1654
+
1655
+ input.addEventListener('keypress', (e) => {
1656
+ if (e.key === 'Enter' && input.value.trim()) {
1657
+ const postId = commentElement.closest('[data-post-id]').dataset.postId;
1658
+ const commentId = commentElement.dataset.commentId;
1659
+ addReply(postId, commentId, input.value.trim());
1660
+ input.value = '';
1661
+ replyInput.classList.add('hidden');
1662
+ }
1663
+ });
1664
+ }
1665
+ }
1666
+
1667
+ // Add a reply to a comment
1668
+ function addReply(postId, commentId, content
1669
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Hubertoo/sbi-book" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1670
+ </html>