| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- //static/js/frontend.js
- function setLoggedIn(loggedInBoolean) {
- let body = document.body;
- body.dataset.loggedIn = !!loggedInBoolean;
- }
- function createExistingVote(voter, vote) {
- let voteDiv = document.createElement("div");
- voteDiv.innerHTML = "";
- if (vote === "yes") {
- voteDiv.classList.add("vote-yes");
- } else if (vote === "no") {
- voteDiv.classList.add("vote-no");
- }
- voteDiv.innerText = voter;
- return voteDiv;
- }
- function createOneDayCard(dayData, currentSession, weather) {
- let date = new Date(dayData.date + "T00:00:00");
- let card = document.createElement("div");
- let temp = "";
- let iconUrl = "50d";
- let weatherDescription = "no data!";
- if (weather && weather.main && weather.weather && weather.weather[0]) {
- temp = weather.main.temp + "°C";
- iconUrl = `https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`;
- weatherDescription = weather.weather[0].description;
- }
- card.innerHTML = `
- <div class="cardtop">
- <div class="date">
- <div class="dow">${date.toLocaleString("en-CA", {
- weekday: "long",
- })}</div>
- <div class="dom">${date.toLocaleString("en-CA", {
- month: "short",
- day: "numeric",
- })}</div>
- </div>
- <div class="weather">
- <div class="temp">
- ${temp}
- </div>
- <div class="weath">
- <img src="${iconUrl}" alt="${weatherDescription}">
- </div>
- </div>
- </div>
- <div class="make-vote">
- <div class="satis-tier forloggedin">
- Can you attend?
- <div class="">
- <button class="vote yes" data-vote="yes">
- Yes ✔️
- </button>
- <button class="vote maybe" data-vote="">
- ??
- </button>
- <button class="vote no" data-vote="no">
- No ❌
- </button>
- </div>
- </div>
- </div>
- <div class="existing-votes">
- </div>
- `;
- card.classList.add("card");
- card.dataset.date = dayData.date;
- if (weather) {
- // you need to figure this part out yourself
- }
- let existingVotesDiv = card.querySelector(".existing-votes");
- for (let [voter, vote] of Object.entries(dayData.votes)) {
- existingVotesDiv.append(createExistingVote(voter, vote));
- }
- return card;
- }
- function updateVotableDays(daysWithVotes, currentSession, weatherForecasts) {
- let daysView = document.querySelector(".days-view");
- if (!daysView) {
- console.error("could not find element to put days into");
- return;
- }
- daysView.innerHTML = "";
- for (let date in daysWithVotes) {
- let votes = daysWithVotes[date];
- let weather = weatherForecasts.list.find((w) => w.dt_txt.startsWith(date));
- daysView.append(
- createOneDayCard({
- date,
- votes,
- },
- currentSession,
- weather
- )
- );
- }
- }
- class FrontendState {
- constructor() {
- this.currentSession = undefined;
- this.daysWithVotes = [];
- this.weatherForecasts = {};
- this.initWebSocket(); //初始化
- }
- initWebSocket() {
- const host = window.location.hostname; //設定WebSocket host ip
- const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; //設定protocol 如果是https 就用wss 反之ws
- const wsPort = window.location.port ? `:${window.location.port}` : ''; //設定ws用的port
- const wsUrl = `${wsProtocol}//${host}${wsPort}`; //拼起來就是完整的WebSocket囉
- this.ws = new WebSocket(wsUrl); //設定完畢並開啟服務
- this.ws.onopen = () => { //onopen ws服務開啟的時候會先執行的
- console.log('WebSocket connected'); //提示ws已經連線成功
- };
- this.ws.onmessage = (event) => { //這邊負責監聽比如從伺服端傳來的訊息
- console.log('Message from server ', event.data); //log 從server端傳來的訊息
- const data = JSON.parse(event.data); //json解析 data
- if (data.type === 'voteUpdate') { //如果type為voteUpdate 更新vote結果 ps: 這是自己定義的
- this.refreshVotesState(true);
- }
- if (data.type === 'testMessage') { //如果type為voteUpdate 更新vote結果 ps: 這是自己定義的
- this.updateWSMessage(data.message);
- }
- };
- this.ws.onerror = (error) => { //這邊負責紀錄傳送異常的訊息
- console.error('WebSocket error', error);
- };
- }
- async refreshAllState(updateView = true) {
- await Promise.all([
- this.refreshVotesState(false),
- this.refreshWeatherState(false),
- this.refreshSessionState(false),
- ]);
- if (updateView) {
- this.updateView();
- }
- }
- async refreshVotesState(updateView = true) {
- let {
- success,
- data,
- error
- } = await getVotesFromBackend();
- if (success) {
- this.daysWithVotes = data;
- } else {
- // ha ha I'm being lazy. can you do better?
- updateErrorMessage(error);
- }
- if (updateView) {
- this.updateView();
- }
- }
- async refreshWeatherState(updateView = true) {
- let {
- success,
- data,
- error
- } = await fetchWeatherForCurrentLocation();
- if (success) {
- this.weatherForecasts = data;
- } else {
- // ha ha I'm being lazy. can you do better?
- updateErrorMessage(error);
- }
- if (updateView) {
- this.updateView();
- }
- }
- async refreshSessionState(updateView = true) {
- let {
- success,
- data,
- error
- } = await getSessionFromBackend();
- if (success) {
- this.currentSession = data;
- } else {
- // 處理錯誤情況
- updateErrorMessage(
- "Unable to refresh session state. Please try again later."
- );
- }
- if (updateView) {
- this.updateView();
- }
- }
- async updateView() {
- // 1. update the whole frontend so that it shows relevant logged-in vs logged-out features
- setLoggedIn(!!this.currentSession);
- // 2. optionally update the header so that it shows the username of the person logged in
- if (this.currentSession) updateHeader(this.currentSession.username);
- else updateHeader("");
- // 3. render the days
- updateVotableDays(
- this.daysWithVotes,
- this.currentSession,
- this.weatherForecasts
- );
- }
- async updateWSMessage(message) {
- const wsMessageDiv = document.querySelector(".ws");
- wsMessageDiv.textContent = message;
- }
- }
- const fes = new FrontendState();
- fes.refreshAllState();
- async function handleAuthEvent(event) {
- event.preventDefault();
- // console.log(event.currentTarget, event.target)
- let usernameInput = event.currentTarget.querySelector("#headerusername");
- let usernameValue = usernameInput.value;
- let passwordInput = event.currentTarget.querySelector("#headerpassword");
- let passwordValue = passwordInput.value;
- let button = event.target.closest("button");
- if (button) {
- let authActionName = button?.dataset?.authAction;
- let authActionFunction = {
- signup: ajaxSignup,
- login: ajaxLogin,
- logout: ajaxLogout,
- } [authActionName];
- if (authActionFunction) {
- let authResult = await authActionFunction(usernameValue, passwordValue);
- if (authResult && authResult.success) {
- await fes.refreshSessionState();
- usernameInput.value = passwordInput.value = "";
- } else if (authResult) {
- // 處理登入、註冊或登出失敗的情況
- updateErrorMessage(authResult.error);
- } else {
- // 處理未知網絡錯誤
- updateErrorMessage("unknown network error");
- }
- }
- }
- }
- function updateHeader(username) {
- const usernameSpan = document.querySelector(".username");
- usernameSpan.textContent = username ? username : "nobody";
- }
- function updateErrorMessage(message) {
- const errorMessageDiv = document.querySelector(".error-message");
- errorMessageDiv.textContent = message;
- }
- const authform = document.querySelector("form.authform");
- authform.addEventListener("click", handleAuthEvent);
- async function handleVoteEvent(event) {
- event.preventDefault();
- let button = event.target.closest("button.vote");
- if (button) {
- let voteVal = button.dataset.vote; // 從按鈕的 data-vote 屬性中獲取投票選項
- let cardDiv = button.closest("div.card");
- let date = cardDiv.dataset.date; // 從卡片的 data-date 屬性中獲取日期
- if (fes.currentSession && fes.currentSession.username && voteVal && date) {
- // 假設用戶已登入,並且 fes.currentSession 中包含 username
- let voteActionResult = await setMyVote(
- fes.currentSession.username,
- date,
- voteVal
- );
- if (voteActionResult.success) {
- await fes.refreshVotesState(); // 重新獲取並顯示最新的投票資訊
- } else {
- // 顯示錯誤訊息
- updateErrorMessage(voteActionResult.error);
- }
- }
- }
- }
- const daysViewDiv = document.querySelector("section.days-view");
- daysViewDiv.addEventListener("click", handleVoteEvent);
- document.getElementById('refreshVotes').addEventListener('click', function () {
- fes.refreshVotesState(true);// 重新獲取並顯示最新的投票資訊 並更新VIEW
- });
|