Skip to content

Commit 9121427

Browse files
committed
v3
1 parent 49eb291 commit 9121427

File tree

1 file changed

+88
-48
lines changed

1 file changed

+88
-48
lines changed

chat/index.html

Lines changed: 88 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
});
6666
// Set __app_id to your Firebase projectId for Firestore pathing
6767
window.__app_id = "chatv1-5432a";
68-
window.__initial_auth_token = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;
68+
window.__initial_auth_token = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null; // Kept for consistency, but not used in authentication flow now
6969

7070
// Log the Firebase config as a JSON string and then parsed object for debugging
7171
console.log("Firebase config string on load:", window.__firebase_config);
@@ -84,7 +84,7 @@
8484
// and "Missing or insufficient permissions" (which indicate Firestore rules issues):
8585
//
8686
// 1. **CRITICAL: ENABLE ANONYMOUS AUTHENTICATION IN FIREBASE CONSOLE**
87-
// This app uses anonymous sign-in by default. If this method is not enabled,
87+
// This app now exclusively uses anonymous sign-in. If this method is not enabled,
8888
// Firebase Auth will fail to initialize, leading to "auth/configuration-not-found".
8989
// Steps:
9090
// a. Go to your Firebase Project Console: https://console.firebase.google.com/
@@ -107,20 +107,21 @@
107107
// rules_version = '2';
108108
// service cloud.firestore {
109109
// match /databases/{database}/documents {
110-
// // This path must match exactly where your messages are stored.
111-
// // window.__app_id is used here, which you've set to "chatv1-5432a"
110+
// // This path MUST EXACTLY MATCH where your messages are stored.
111+
// // Ensure 'appId' in the rules matches 'window.__app_id' in your code ("chatv1-5432a").
112112
// match /artifacts/{appId}/public/data/messages/{document=**} {
113113
// allow read, write: if request.auth != null; // Allows any authenticated user (including anonymous)
114114
// }
115115
// }
116116
// }
117+
// Remember to click "Publish" after making changes to your rules.
117118
// ====================================================================================
118119

119120

120121
// Import Firebase modules from CDN
121122
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
122-
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
123-
import { getFirestore, collection, addDoc, query, onSnapshot, serverTimestamp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
123+
import { getAuth, signInAnonymously, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
124+
import { getFirestore, collection, addDoc, query, onSnapshot, serverTimestamp, doc, deleteDoc } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
124125

125126
// Main App component for the chat application (inlined)
126127
function App() {
@@ -133,6 +134,8 @@
133134
const [isAuthReady, setIsAuthReady] = React.useState(false); // To track if authentication is ready
134135
const [userName, setUserName] = React.useState(localStorage.getItem('chatUserName') || '');
135136
const [nameInput, setNameInput] = React.useState('');
137+
const [isAdmin, setIsAdmin] = React.useState(userName === 'admin'); // Track admin status
138+
const [showAdminConfirmScreen, setShowAdminConfirmScreen] = React.useState(false); // For second 'admin' prompt
136139

137140

138141
// Initialize Firebase and handle authentication
@@ -146,24 +149,10 @@
146149
setDb(firestoreDb);
147150
setAuth(firebaseAuth);
148151

149-
// Sign in with custom token if available, otherwise anonymously
150-
if (window.__initial_auth_token) {
151-
signInWithCustomToken(firebaseAuth, window.__initial_auth_token)
152-
.then((userCredential) => {
153-
console.log("Signed in with custom token:", userCredential.user.uid);
154-
})
155-
.catch(error => {
156-
console.error("Error signing in with custom token:", error);
157-
// Fallback to anonymous sign-in if custom token fails
158-
signInAnonymously(firebaseAuth)
159-
.then((userCredential) => console.log("Signed in anonymously (fallback):", userCredential.user.uid))
160-
.catch(err => console.error("Error signing in anonymously (fallback):", err));
161-
});
162-
} else {
163-
signInAnonymously(firebaseAuth)
164-
.then((userCredential) => console.log("Signed in anonymously:", userCredential.user.uid))
165-
.catch(error => console.error("Error signing in anonymously:", error));
166-
}
152+
// Sign in anonymously (removed custom token logic as it's not applicable for static hosting without backend)
153+
signInAnonymously(firebaseAuth)
154+
.then((userCredential) => console.log("Signed in anonymously:", userCredential.user.uid))
155+
.catch(error => console.error("Error signing in anonymously:", error));
167156

168157
// Listen for authentication state changes
169158
const unsubscribeAuth = onAuthStateChanged(firebaseAuth, (user) => {
@@ -172,13 +161,10 @@
172161
setIsAuthReady(true);
173162
console.log("Auth state changed: User is authenticated. User ID:", user.uid, "isAuthReady:", true);
174163
} else {
175-
// Only set userId to random UUID if auth failed or user is not found,
176-
// AND only if isAuthReady hasn't already been set by a successful auth.
177-
// The anonymous sign-in ensures a user always exists for this app's purpose.
178-
if (!userId) { // Check if userId is not already set by a successful login
164+
if (!userId) {
179165
setUserId(crypto.randomUUID());
180166
}
181-
setIsAuthReady(true); // Always set to true once auth state is determined
167+
setIsAuthReady(true);
182168
console.log("Auth state changed: User is NOT authenticated. (Anonymous expected). isAuthReady:", true);
183169
}
184170
});
@@ -198,7 +184,6 @@
198184
const chatCollectionRef = collection(db, `artifacts/${window.__app_id}/public/data/messages`);
199185

200186
// Create a query to order messages by timestamp
201-
// Data will be sorted in memory.
202187
const q = query(chatCollectionRef);
203188

204189
// Subscribe to real-time updates
@@ -207,7 +192,6 @@
207192
id: doc.id,
208193
...doc.data()
209194
}));
210-
// Sort messages by timestamp in memory
211195
fetchedMessages.sort((a, b) => (a.timestamp?.toMillis() || 0) - (b.timestamp?.toMillis() || 0));
212196
setMessages(fetchedMessages);
213197
}, (error) => {
@@ -224,36 +208,74 @@
224208
if (newMessage.trim() === '' || !db || !userId) return;
225209

226210
try {
227-
// Add a new document to the 'messages' collection
228211
await addDoc(collection(db, `artifacts/${window.__app_id}/public/data/messages`), {
229212
text: newMessage,
230213
userId: userId,
231-
userName: userName || 'Anonymous', // Store the user's chosen name
232-
timestamp: serverTimestamp() // Firestore server timestamp for consistency
214+
userName: userName || 'Anonymous',
215+
timestamp: serverTimestamp()
233216
});
234-
setNewMessage(''); // Clear the input field after sending
217+
setNewMessage('');
235218
} catch (error) {
236219
console.error("Error sending message:", error);
237220
}
238221
};
239222

223+
// Function to delete a message (only for admin)
224+
const deleteMessage = async (messageId) => {
225+
if (!db || !isAdmin) { // Only allow if db is ready and user is admin
226+
console.warn("Permission denied: Only admin can delete messages.");
227+
return;
228+
}
229+
try {
230+
const messageRef = doc(db, `artifacts/${window.__app_id}/public/data/messages`, messageId);
231+
await deleteDoc(messageRef);
232+
console.log(`Message ${messageId} deleted successfully.`);
233+
} catch (error) {
234+
console.error("Error deleting message:", error);
235+
}
236+
};
237+
240238
// Handle key press for sending message (e.g., Enter key)
241239
const handleKeyPress = (e) => {
242240
if (e.key === 'Enter') {
243241
sendMessage();
244242
}
245243
};
246244

247-
// Function to set the user's name
245+
// Function to handle initial name setting or admin first attempt
248246
const setChatUserName = () => {
249-
if (nameInput.trim() !== '') {
247+
if (nameInput.trim() === 'admin') {
248+
setShowAdminConfirmScreen(true); // Show confirmation for admin
249+
setNameInput(''); // Clear input for re-entry
250+
} else if (nameInput.trim() !== '') {
250251
setUserName(nameInput.trim());
251-
localStorage.setItem('chatUserName', nameInput.trim()); // Persist name
252+
localStorage.setItem('chatUserName', nameInput.trim());
253+
setIsAdmin(false); // Not admin if name is not 'admin'
254+
setShowAdminConfirmScreen(false);
252255
}
253256
};
254257

255-
// Conditional rendering: Show name input if userName is not set
256-
if (!userName) {
258+
// Function to confirm admin access on the second 'admin' entry
259+
const confirmAdminAccess = () => {
260+
if (nameInput.trim() === 'admin') {
261+
setUserName('admin');
262+
localStorage.setItem('chatUserName', 'admin');
263+
setIsAdmin(true); // Grant admin rights
264+
setShowAdminConfirmScreen(false);
265+
setNameInput(''); // Clear input
266+
} else {
267+
// If wrong password for admin, reset to initial name input
268+
setUserName('');
269+
localStorage.removeItem('chatUserName');
270+
setIsAdmin(false);
271+
setShowAdminConfirmScreen(false);
272+
setNameInput('');
273+
alert('Incorrect input for admin. Please enter your name again.'); // Simple message for incorrect entry
274+
}
275+
};
276+
277+
// Conditional rendering: Show name input or admin confirmation
278+
if (!userName || showAdminConfirmScreen) {
257279
return React.createElement(
258280
"div",
259281
{
@@ -264,33 +286,41 @@
264286
{
265287
className: "text-2xl font-bold text-gray-800 mb-6"
266288
},
267-
"Welcome to the Chat!"
289+
showAdminConfirmScreen ? "Confirm Admin Access" : "Welcome to the Chat!"
268290
),
269291
React.createElement(
270292
"p",
271293
{
272294
className: "text-gray-600 mb-4"
273295
},
274-
"Please enter your name to start chatting."
296+
showAdminConfirmScreen ? "Please re-enter 'admin' to confirm administrator privileges." : "Please enter your name to start chatting."
275297
),
276298
React.createElement(
277299
"input",
278300
{
279301
type: "text",
280302
value: nameInput,
281303
onChange: e => setNameInput(e.target.value),
282-
onKeyPress: (e) => { if (e.key === 'Enter') setChatUserName(); },
283-
placeholder: "Your Name",
304+
onKeyPress: (e) => {
305+
if (e.key === 'Enter') {
306+
if (showAdminConfirmScreen) {
307+
confirmAdminAccess();
308+
} else {
309+
setChatUserName();
310+
}
311+
}
312+
},
313+
placeholder: showAdminConfirmScreen ? "Type 'admin' again" : "Your Name",
284314
className: "p-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 transition duration-200 w-full max-w-sm mb-4"
285315
}
286316
),
287317
React.createElement(
288318
"button",
289319
{
290-
onClick: setChatUserName,
320+
onClick: showAdminConfirmScreen ? confirmAdminAccess : setChatUserName,
291321
className: "px-8 py-3 bg-blue-600 text-white font-semibold rounded-xl shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-200"
292322
},
293-
"Start Chatting"
323+
showAdminConfirmScreen ? "Confirm" : "Start Chatting"
294324
)
295325
);
296326
}
@@ -340,8 +370,10 @@
340370
onClick: () => {
341371
setUserName(''); // Clear name in state
342372
localStorage.removeItem('chatUserName'); // Clear from localStorage
373+
setIsAdmin(false); // Remove admin status
343374
setNewMessage(''); // Clear any message in input
344375
setMessages([]); // Clear messages to reset view
376+
setShowAdminConfirmScreen(false); // Ensure admin confirm screen is hidden
345377
},
346378
className: "ml-4 px-3 py-1 bg-blue-700 text-white text-xs rounded-full hover:bg-blue-800 transition duration-200"
347379
},
@@ -403,6 +435,15 @@
403435
className: "text-right text-xs mt-1 opacity-75"
404436
},
405437
new Date(msg.timestamp.toMillis()).toLocaleTimeString()
438+
),
439+
// Render delete button only if current user is admin
440+
isAdmin && React.createElement(
441+
"button",
442+
{
443+
onClick: () => deleteMessage(msg.id),
444+
className: "ml-2 px-2 py-1 bg-red-500 text-white text-xs rounded-full hover:bg-red-600 transition duration-200 self-end"
445+
},
446+
"Delete"
406447
)
407448
)
408449
);
@@ -429,8 +470,7 @@
429470
"button",
430471
{
431472
onClick: sendMessage,
432-
className: "px-6 py-3 bg-blue-600 text-white font-semibold rounded-xl shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
433-
disabled: !isAuthReady || newMessage.trim() === ''
473+
className: "px-6 py-3 bg-blue-600 text-white font-semibold rounded-xl shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
434474
},
435475
"Send"
436476
)

0 commit comments

Comments
 (0)