firebase 掲示板テスト
ここまでのものは
掲示板
掲示板
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.12.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.12.0/firebase-storage-compat.js"></script>
<div>
<h3>掲示板</h3>
<p id="postCount"></p>
<input type="text" id="nameInput" placeholder="名前 (15文字以内)" maxlength="15" required><br>
<input type="email" id="emailInput" placeholder="メールアドレス (任意)"><br>
<input type="file" id="iconInput" accept="image/*"><br>
<div id="stampGallery"></div>
<textarea id="commentInput" placeholder="コメントを入力してください" rows="3" cols="40" required></textarea><br>
<button id="submitBtn">投稿</button>
<select id="sortSelect">
<option value="created">新着順</option>
<option value="likes">いいね順</option>
</select>
<div id="toast" style="display:none; position:fixed; bottom:10px; right:10px; background:#333; color:#fff; padding:10px; border-radius:5px;">投稿しました</div>
<div id="commentList"></div>
</div>
<script>
const firebaseConfig = {
apiKey: "AIzaSyBXk0dW2k1W5w32f1f12viaV5s-j6iM1k",
authDomain: "keiko-salon-board.firebaseapp.com",
projectId: "keiko-salon-board",
storageBucket: "keiko-salon-board.appspot.com",
messagingSenderId: "462083553388",
appId: "1:462083553388:web:07d442b8d80ce9a3f53d219"
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
const storage = firebase.storage();
let selectedStamp = "";
window.addEventListener("DOMContentLoaded", async () => {
document.getElementById("submitBtn").addEventListener("click", postComment);
document.getElementById("sortSelect").addEventListener("change", loadComments);
loadComments();
await loadStampGallery();
});
function showToast(msg) {
const toast = document.getElementById("toast");
toast.textContent = msg;
toast.style.display = "block";
setTimeout(() => toast.style.display = "none", 3000);
}
async function loadStampGallery() {
const stampRef = storage.ref("stamps");
const result = await stampRef.listAll();
const gallery = document.getElementById("stampGallery");
gallery.innerHTML = "<p>スタンプを選んでください:</p>";
result.items.forEach(async (itemRef) => {
const url = await itemRef.getDownloadURL();
const img = document.createElement("img");
img.src = url;
img.width = 40;
img.style.margin = "5px";
img.style.cursor = "pointer";
img.onclick = () => {
selectedStamp = url;
document.querySelectorAll('#stampGallery img').forEach(i => i.style.border = 'none');
img.style.border = '2px solid red';
};
gallery.appendChild(img);
});
}
async function postComment() {
const name = document.getElementById("nameInput").value.trim().substring(0, 15);
const email = document.getElementById("emailInput").value.trim();
const text = document.getElementById("commentInput").value.trim();
const file = document.getElementById("iconInput").files[0];
const stamp = selectedStamp;
if (!name || !text) return showToast("名前とコメントは必須です");
let iconURL = "";
try {
if (file) {
const storageRef = storage.ref("icons/" + Date.now() + "_" + file.name);
await storageRef.put(file);
iconURL = await storageRef.getDownloadURL();
}
} catch (e) {
console.error("🔥 画像アップロードに失敗:", e);
showToast("画像アップロードに失敗しました");
return;
}
try {
await db.collection("comments").add({
name,
email,
text,
icon: iconURL,
stamp: stamp,
created: new Date(),
likes: 0,
parentId: null,
reports: 0
});
// フォームリセット
document.getElementById("commentInput").value = "";
document.getElementById("iconInput").value = "";
document.getElementById("emailInput").value = "";
selectedStamp = "";
showToast("投稿が完了しました");
loadComments();
} catch (e) {
console.error("🔥 Firestoreへの投稿に失敗:", e);
showToast("投稿に失敗しました");
}
}
await db.collection("comments").add({
name,
email,
icon: iconURL,
stamp: selectedStamp,
text,
created: new Date(),
likes: 0,
parentId: null,
reports: 0
});
showToast("投稿しました");
document.getElementById("commentInput").value = "";
document.getElementById("iconInput").value = "";
selectedStamp = "";
loadComments();
} catch (e) {
console.error("画像付き投稿エラー:", e);
showToast("画像投稿に失敗しました");
}
}
async function loadComments() {
const sort = document.getElementById("sortSelect").value;
const snapshot = await db.collection("comments").where("parentId", "==", null).orderBy(sort, "desc").get();
document.getElementById("postCount").innerText = `総投稿数:${snapshot.size}`;
const html = await Promise.all(snapshot.docs.map(async doc => {
const data = doc.data();
const replies = await db.collection("comments").where("parentId", "==", doc.id).orderBy("created").get();
const replyHTML = replies.docs.map(replyDoc => {
const reply = replyDoc.data();
return `
<div style="margin-left: 40px;">
<strong>${reply.name}</strong>:${reply.text}<br>
${reply.stamp ? `<img src="${reply.stamp}" width="30">` : ""}
<button onclick="likeComment('${replyDoc.id}')">❤️ ${reply.likes || 0}</button>
<button onclick="reportComment('${replyDoc.id}')">🚩</button>
</div>`;
}).join("");
const iconTag = data.icon ? `<img src="${data.icon}" width="40" style="border-radius:50%; margin-right:10px;">` : "";
return `
<div style="border-bottom:1px solid #ccc; margin-bottom:10px;">
<div style="display:flex; align-items:center;">
${iconTag}<strong>${data.name}</strong>(${new Date(data.created.toDate()).toLocaleString()})
</div>
<div>${data.text.replace(/\n/g, "<br>")}</div>
${data.stamp ? `<img src="${data.stamp}" width="30">` : ""}<br>
<button onclick="likeComment('${doc.id}')">❤️ ${data.likes || 0}</button>
<button onclick="showReplyBox('${doc.id}')">返信</button>
<button onclick="reportComment('${doc.id}')">🚩</button>
<div id="replyBox-${doc.id}" style="display:none;">
<input type="text" placeholder="名前 (15文字以内)" id="replyName-${doc.id}" maxlength="15" required><br>
<input type="email" placeholder="メールアドレス (任意)" id="replyEmail-${doc.id}"><br>
<textarea rows="3" cols="40" placeholder="返信コメント" id="replyText-${doc.id}" required></textarea><br>
<button onclick="submitReply('${doc.id}')">返信する</button>
</div>
${replyHTML}
</div>`;
}));
document.getElementById("commentList").innerHTML = html.join("");
}
function showReplyBox(id) {
document.getElementById(`replyBox-${id}`).style.display = "block";
}
async function submitReply(parentId) {
const name = document.getElementById(`replyName-${parentId}`).value.trim().substring(0, 15);
const email = document.getElementById(`replyEmail-${parentId}`).value.trim();
const text = document.getElementById(`replyText-${parentId}`).value.trim();
if (!name || !text) return;
await db.collection("comments").add({
name,
email,
icon: "",
stamp: "",
text,
created: new Date(),
likes: 0,
parentId,
reports: 0
});
loadComments();
}
async function likeComment(id) {
const key = `liked-${id}`;
if (localStorage.getItem(key)) return;
const ref = db.collection("comments").doc(id);
await ref.update({ likes: firebase.firestore.FieldValue.increment(1) });
localStorage.setItem(key, true);
loadComments();
}
async function reportComment(id) {
const ref = db.collection("comments").doc(id);
await ref.update({ reports: firebase.firestore.FieldValue.increment(1) });
showToast("通報しました");
}
</script>
すべてはアイデアから始まります。ビジネスを始める場合でも、趣味をただの楽しみ以上のものにする場合でも、これは同じです。または、創造的なプロジェクトを世界に発信する計画の途中かもしれませんね。何をするにしても、ストーリーをオンラインでどのように伝えるかによって、結果は大きく変わります。
専門的に聞こえるかどうかは気にせず、自分らしさを出しましょう。オンラインに存在する15億を超えるWebサイトの中で、あなたのサイトを特別にするのが、あなただけのストーリーです。自分の文章を読み返してもまだピンとこないなら、チャンスだと思いましょう。それは、まだ改善の余地があるということですから。
分かりすく堂々とした語り口調を心がけ、あまり考えすぎないようにしましょう。あなたのストーリーは、進化し続けるからこそ美しいのです。そしてあなたのサイトも、ストーリーとともに発展し続けます。今この瞬間、正しいと思えることを目標にしましょう。今後のことは、きっとどうにでもなります。いつだってそういうものです。
掲示板
Hello, World!