Linux中國

構建一個即時消息應用(八):Home 頁面

本文是該系列的第八篇。

繼續前端部分,讓我們在本文中完成 home 頁面的開發。 我們將添加一個開始對話的表單和一個包含最新對話的列表。

對話表單

轉到 static/ages/home-page.js 文件,在 HTML 視圖中添加一些標記。

<form id="conversation-form">
    <input type="search" placeholder="Start conversation with..." required>
</form>

將該表單添加到我們顯示 「auth user」 和 「logout」 按鈕部分的下方。

page.getElementById(&apos;conversation-form&apos;).onsubmit = onConversationSubmit

現在我們可以監聽 「submit」 事件來創建對話了。

import http from &apos;../http.js&apos;
import { navigate } from &apos;../router.js&apos;

async function onConversationSubmit(ev) {
    ev.preventDefault()

    const form = ev.currentTarget
    const input = form.querySelector(&apos;input&apos;)

    input.disabled = true

    try {
        const conversation = await createConversation(input.value)
        input.value = &apos;&apos;
        navigate(&apos;/conversations/&apos; + conversation.id)
    } catch (err) {
        if (err.statusCode === 422) {
            input.setCustomValidity(err.body.errors.username)
        } else {
            alert(err.message)
        }
        setTimeout(() => {
            input.focus()
        }, 0)
    } finally {
        input.disabled = false
    }
}

function createConversation(username) {
    return http.post(&apos;/api/conversations&apos;, { username })
}

在提交時,我們使用用戶名對 /api/conversations 進行 POST 請求,並重定向到 conversation 頁面(用於下一篇文章)。

對話列表

還是在這個文件中,我們將創建 homePage() 函數用來先非同步載入對話。

export default async function homePage() {
    const conversations = await getConversations().catch(err => {
        console.error(err)
        return []
    })
    /*...*/
}

function getConversations() {
    return http.get(&apos;/api/conversations&apos;)
}

然後,在標記中添加一個列表來渲染對話。

<ol id="conversations"></ol>

將其添加到當前標記的正下方。

const conversationsOList = page.getElementById(&apos;conversations&apos;)
for (const conversation of conversations) {
    conversationsOList.appendChild(renderConversation(conversation))
}

因此,我們可以將每個對話添加到這個列表中。

import { avatar, escapeHTML } from &apos;../shared.js&apos;

function renderConversation(conversation) {
    const messageContent = escapeHTML(conversation.lastMessage.content)
    const messageDate = new Date(conversation.lastMessage.createdAt).toLocaleString()

    const li = document.createElement(&apos;li&apos;)
    li.dataset[&apos;id&apos;] = conversation.id
    if (conversation.hasUnreadMessages) {
        li.classList.add(&apos;has-unread-messages&apos;)
    }
    li.innerHTML = `
 <a href="/conversations/${conversation.id}">
 <div>
 ${avatar(conversation.otherParticipant)}
 <span>${conversation.otherParticipant.username}</span>
 </div>
 <div>
 <p>${messageContent}</p>
 <time>${messageDate}</time>
 </div>
 </a>
 `
    return li
}

每個對話條目都包含一個指向對話頁面的鏈接,並顯示其他參與者信息和最後一條消息的預覽。另外,您可以使用 .hasUnreadMessages 向該條目添加一個類,並使用 CSS 進行一些樣式設置。也許是粗體字體或強調顏色。

請注意,我們需要轉義信息的內容。該函數來自於 static/shared.js 文件:

export function escapeHTML(str) {
    return str
        .replace(/&/g, &apos;&&apos;)
        .replace(/</g, &apos;<&apos;)
        .replace(/>/g, &apos;>&apos;)
        .replace(/"/g, &apos;"&apos;)
        .replace(/&apos;/g, &apos;&#039;&apos;)
}

這會阻止將用戶編寫的消息顯示為 HTML。如果用戶碰巧編寫了類似以下內容的代碼:

<script>alert(&apos;lololo&apos;)</script>

這將非常煩人,因為該腳本將被執行?。所以,永遠記住要轉義來自不可信來源的內容。

消息訂閱

最後但並非最不重要的一點,我想在這裡訂閱消息流。

const unsubscribe = subscribeToMessages(onMessageArrive)
page.addEventListener(&apos;disconnect&apos;, unsubscribe)

homePage() 函數中添加這一行。

function subscribeToMessages(cb) {
    return http.subscribe(&apos;/api/messages&apos;, cb)
}

函數 subscribe() 返回一個函數,該函數一旦調用就會關閉底層連接。這就是為什麼我把它傳遞給 「斷開連接」 disconnect 事件的原因;因此,當用戶離開頁面時,事件流將被關閉。

async function onMessageArrive(message) {
    const conversationLI = document.querySelector(`li[data-id="${message.conversationID}"]`)
    if (conversationLI !== null) {
        conversationLI.classList.add(&apos;has-unread-messages&apos;)
        conversationLI.querySelector(&apos;a > div > p&apos;).textContent = message.content
        conversationLI.querySelector(&apos;a > div > time&apos;).textContent = new Date(message.createdAt).toLocaleString()
        return
    }

    let conversation
    try {
        conversation = await getConversation(message.conversationID)
        conversation.lastMessage = message
    } catch (err) {
        console.error(err)
        return
    }

    const conversationsOList = document.getElementById(&apos;conversations&apos;)
    if (conversationsOList === null) {
        return
    }

    conversationsOList.insertAdjacentElement(&apos;afterbegin&apos;, renderConversation(conversation))
}

function getConversation(id) {
    return http.get(&apos;/api/conversations/&apos; + id)
}

每次有新消息到達時,我們都會在 DOM 中查詢會話條目。如果找到,我們會將 has-unread-messages 類添加到該條目中,並更新視圖。如果未找到,則表示該消息來自剛剛創建的新對話。我們去做一個對 /api/conversations/{conversationID} 的 GET 請求,以獲取在其中創建消息的對話,並將其放在對話列表的前面。

以上這些涵蓋了主頁的所有內容 ?。 在下一篇文章中,我們將對 conversation 頁面進行編碼。

via: https://nicolasparada.netlify.com/posts/go-messenger-home-page/

作者:Nicolás Parada 選題:lujun9972 譯者:gxlct008 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國