Newer
Older
2025-shino / archive.js
// ===============================
// Supabase 設定
// ===============================
const SUPABASE_URL = "https://ogtlmtnjkpsxsqzqlacj.supabase.co";
const SUPABASE_KEY =
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9ndGxtdG5qa3BzeHNxenFsYWNqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjMyOTU3NjUsImV4cCI6MjA3ODg3MTc2NX0.JnCE7oUQwrSgGqiu-QRbwnaLBZrO8JX1_RUb37VIMFI";

const supa = window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY);

// ===============================
// URL パラメータ
// ===============================
const p = new URLSearchParams(location.search);
const currentGroup = p.get("group") || "";

document.getElementById("groupName").textContent = currentGroup;

// ===============================
// グローバル
// ===============================
let map;
let currentHostName = "";
let targetMarker = null;
let meetIcon;

// ===============================
// ユーティリティ:日付表示
// ===============================
function formatDateTime(isoString) {
  if (!isoString) return "";
  const d = new Date(isoString);
  return d.toLocaleString("ja-JP", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  });
}

// ===============================
// ピン生成(ホスト:緑 / その他:金)
// ===============================
function createLabeledMarker(lat, lng, name, isHostUser) {
  const pinUrl = isHostUser
    ? "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png"
    : "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-gold.png";

  const html = `
    <div class="pin-box">
      <img src="${pinUrl}" class="pin-img">
      <div class="pin-label">${name}</div>
    </div>
  `;

  return L.marker([lat, lng], {
    icon: L.divIcon({
      className: "custom-pin",
      html: html,
      iconSize: [40, 50],
      iconAnchor: [20, 50],
    }),
  });
}

// ===============================
// グループ情報読み込み(ホスト名表示)
// ===============================
async function loadGroup() {
  const { data, error } = await supa
    .from("groups")
    .select("*")
    .eq("group_name", currentGroup)
    .maybeSingle();

  if (error) {
    console.error("groups 読み込みエラー", error);
  }

  if (!data) {
    alert("このグループは存在しません。");
    location.href = "index.html";
    return false;
  }

  // active なら「まだ解散していない」ので index に戻す
  if (data.is_active === true) {
    alert("このグループはまだ解散されていません。");
    location.href = "index.html";
    return false;
  }

  currentHostName = data.host_name || "";
  document.getElementById("hostName").textContent = currentHostName || "(不明)";

  return true;
}

// ===============================
// 地図初期化
// ===============================
function initMap() {
  map = L.map("map").setView([35.681236, 139.767125], 13);

  L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
    maxZoom: 19,
  }).addTo(map);

  // 待ち合わせ用アイコン
  meetIcon = L.icon({
    iconUrl:
      "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-violet.png",
    shadowUrl:
      "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png",
    iconSize: [25, 41],
    iconAnchor: [12, 41],
  });
}

// ===============================
// メンバー最後の位置取得(map4 風)
// ===============================
async function loadLastLocations() {
  const list = document.getElementById("memberList");
  list.innerHTML = "";

  const { data, error } = await supa
    .from("locations")
    .select("*")
    .eq("group_name", currentGroup)
    .order("updated_at", { ascending: false });

  if (error) {
    console.error("locations 読み込みエラー", error);
    return;
  }

  const latestByDevice = {};
  (data || []).forEach((r) => {
    if (!latestByDevice[r.device_id]) latestByDevice[r.device_id] = r;
  });

  const markers = [];

  Object.values(latestByDevice).forEach((row) => {
    const isHostUser = row.user_name === currentHostName;

    // メンバー一覧の1行
    const li = document.createElement("li");

    const crown = isHostUser ? "👑 " : "";
    const initial = row.user_name.charAt(0);
    const bgColor = isHostUser ? "#4CAF50" : "#FFD700";
    const timeStr = formatDateTime(row.updated_at);

    li.innerHTML = `
      <div class="member-icon"
           style="background:${bgColor};
                  display:flex;
                  align-items:center;
                  justify-content:center;
                  color:white;
                  font-weight:bold;
                  font-size:16px;">
        ${initial}
      </div>
      <div>
        <div class="member-name">${crown}${row.user_name}</div>
        <div style="font-size:12px; opacity:0.7;">最終更新:${timeStr}</div>
      </div>
    `;

    list.appendChild(li);

    // 地図ピン
    if (typeof row.lat === "number" && typeof row.lng === "number") {
      const mk = createLabeledMarker(row.lat, row.lng, row.user_name, isHostUser)
        .addTo(map);
      markers.push(mk);
    }
  });

  // マーカーがあれば全体が入るように調整
  if (markers.length > 0) {
    const group = new L.featureGroup(markers);
    map.fitBounds(group.getBounds().pad(0.2));
  }
}

// ===============================
// 最後の待ち合わせ場所
// ===============================
async function loadLastTarget() {
  const box = document.getElementById("lastTarget");

  const { data, error } = await supa
    .from("shared_target")
    .select("*")
    .eq("group_name", currentGroup)
    .maybeSingle();

  if (error) {
    console.error("shared_target 読み込みエラー", error);
  }

  if (!data) {
    box.textContent = "記録なし";
    return;
  }

  // 説明テキスト(数字は隅に小さく)
  box.innerHTML = `
    最後に設定された待ち合わせ場所があります。<br>
    地図上の <b>紫のピン</b> を確認してください。<br>
    <span style="font-size:12px; opacity:0.7;">
      (lat: ${data.lat.toFixed(5)}, lng: ${data.lng.toFixed(5)})
    </span>
  `;

  if (typeof data.lat === "number" && typeof data.lng === "number") {
    if (targetMarker) map.removeLayer(targetMarker);

    targetMarker = L.marker([data.lat, data.lng], { icon: meetIcon })
      .addTo(map)
      .bindPopup("最終待ち合わせ場所");
  }
}

// ===============================
// チャット履歴読み込み(LINE風 左寄せ)
// ===============================
async function loadChat() {
  const box = document.getElementById("chatList");

  const { data, error } = await supa
    .from("messages")
    .select("*")
    .eq("group_name", currentGroup)
    .order("created_at", { ascending: true });

  if (error) {
    console.error("messages 読み込みエラー", error);
    return;
  }

  box.innerHTML = (data || [])
    .map((m) => {
      return `
        <div class="chat-line">
          <div class="chat-bubble chat-left">
            <strong>${m.user_name}</strong><br>
            ${m.message}
          </div>
        </div>
      `;
    })
    .join("");

  box.scrollTop = box.scrollHeight;
}

// ===============================
// メイン
// ===============================
async function main() {
  // グループチェック(解散済みかなど)
  const ok = await loadGroup();
  if (!ok) return;

  initMap();
  await loadLastLocations();
  await loadLastTarget();
  await loadChat();

  // 戻るボタン
  const backBtn = document.getElementById("backBtn");
  if (backBtn) {
    backBtn.onclick = () => (location.href = "index.html");
  }
}

window.addEventListener("DOMContentLoaded", main);