构建一个即时消息应用(八):Home 页面
创始人
2024-03-02 05:58:13
0

本文是该系列的第八篇。

继续前端部分,让我们在本文中完成 home 页面的开发。 我们将添加一个开始对话的表单和一个包含最新对话的列表。

对话表单

转到 static/ages/home-page.js 文件,在 HTML 视图中添加一些标记。

将该表单添加到我们显示 “auth user” 和 “logout” 按钮部分的下方。

page.getElementById('conversation-form').onsubmit = onConversationSubmit

现在我们可以监听 “submit” 事件来创建对话了。

import http from '../http.js'
import { navigate } from '../router.js'

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

    const form = ev.currentTarget
    const input = form.querySelector('input')

    input.disabled = true

    try {
        const conversation = await createConversation(input.value)
        input.value = ''
        navigate('/conversations/' + 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('/api/conversations', { 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('/api/conversations')
}

然后,在标记中添加一个列表来渲染对话。

    将其添加到当前标记的正下方。

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

    因此,我们可以将每个对话添加到这个列表中。

    import { avatar, escapeHTML } from '../shared.js'
    
    function renderConversation(conversation) {
        const messageContent = escapeHTML(conversation.lastMessage.content)
        const messageDate = new Date(conversation.lastMessage.createdAt).toLocaleString()
    
        const li = document.createElement('li')
        li.dataset['id'] = conversation.id
        if (conversation.hasUnreadMessages) {
            li.classList.add('has-unread-messages')
        }
        li.innerHTML = `
     
     
    ${avatar(conversation.otherParticipant)} ${conversation.otherParticipant.username}

    ${messageContent}

    ` return li }

    每个对话条目都包含一个指向对话页面的链接,并显示其他参与者信息和最后一条消息的预览。另外,您可以使用 .hasUnreadMessages 向该条目添加一个类,并使用 CSS 进行一些样式设置。也许是粗体字体或强调颜色。

    请注意,我们需要转义信息的内容。该函数来自于 static/shared.js 文件:

    export function escapeHTML(str) {
        return str
            .replace(/&/g, '&')
            .replace(//g, '>')
            .replace(/"/g, '"')
            .replace(/'/g, ''')
    }
    

    这会阻止将用户编写的消息显示为 HTML。如果用户碰巧编写了类似以下内容的代码:

    
    

    这将非常烦人,因为该脚本将被执行?。所以,永远记住要转义来自不可信来源的内容。

    消息订阅

    最后但并非最不重要的一点,我想在这里订阅消息流。

    const unsubscribe = subscribeToMessages(onMessageArrive)
    page.addEventListener('disconnect', unsubscribe)
    

    homePage() 函数中添加这一行。

    function subscribeToMessages(cb) {
        return http.subscribe('/api/messages', cb)
    }
    

    函数 subscribe() 返回一个函数,该函数一旦调用就会关闭底层连接。这就是为什么我把它传递给 “断开连接” disconnect 事件的原因;因此,当用户离开页面时,事件流将被关闭。

    async function onMessageArrive(message) {
        const conversationLI = document.querySelector(`li[data-id="${message.conversationID}"]`)
        if (conversationLI !== null) {
            conversationLI.classList.add('has-unread-messages')
            conversationLI.querySelector('a > div > p').textContent = message.content
            conversationLI.querySelector('a > div > time').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('conversations')
        if (conversationsOList === null) {
            return
        }
    
        conversationsOList.insertAdjacentElement('afterbegin', renderConversation(conversation))
    }
    
    function getConversation(id) {
        return http.get('/api/conversations/' + 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中国 荣誉推出

    相关内容

    硬核观察 #353 X.o...
    X.org “大眼睛” 11 年来发布第一个小版本X.Org/X1...
    2024-03-02 08:11:09
    构建一个即时消息应用(九)...
    本文是该系列的第九篇,也是最后一篇。第一篇: 模式第二篇: OAu...
    2024-03-02 05:59:04
    构建一个即时消息应用(八)...
    本文是该系列的第八篇。第一篇: 模式第二篇: OAuth第三篇: ...
    2024-03-02 05:58:13
    构建一个即时消息应用(七)...
    本文是该系列的第七篇。第一篇: 模式第二篇: OAuth第三篇: ...
    2024-03-02 05:56:54
    构建一个即时消息应用(四)...
    本文是该系列的第四篇。第一篇: 模式第二篇: OAuth第三篇: ...
    2024-03-02 05:55:36
    构建一个即时消息应用(六)...
    本文是该系列的第六篇。第一篇: 模式第二篇: OAuth第三篇: ...
    2024-03-02 05:55:24

    热门资讯

    “Linux 中国” 开源社区... 这是一个艰难的决定 —— 这不是调侃,而是当你真正做出改变了你十几年生活轨迹的决定时,你就会明白这个...
    硬核观察 #1263 德国铁路... #1 德国铁路公司招聘 Windows for Workgroups 3.11 管理员该职位要求了解...
    人工智能教程(七):Sciki... 在本系列的 上一篇文章 中,我们用 TensorFlow 构建了第一个神经网络,然后还通过 Kera...
    关闭 WSL 中正在运行的 L... 以下是使用 WSL 关闭在 Windows 内运行的 Linux 系统的不同方法。你使用 WSL 在...
    MX Linux 23.2 “... 最新的 MX Linux 23.2 小版本更新带来了基本的改进和新增功能。MX Linux 推出了 ...
    硬核观察 #1262 网络上最... #1 网络上最古老的软件存档库之一即将关闭美国新墨西哥州立大学(NMSU)最近宣布,即将于 2024...
    如何在 RHEL 上通过绑定配... 在 Linux 中,你可以根据自己的需要创建高级网络设置,如 网络绑定 Network Bondin...
    Linux Matrix 消息... Matrix 用户可以升级 Fractal 6 了!Fractal 是目前 最好的 Matrix 客...
    如何通过 VLC 使用字幕 使用 VLC 媒体播放器播放和管理字幕的新手指南。我是一个超级动漫迷,如果你想观看最新的剧集,那么你...
    硬核观察 #1261 X 计划... #1 X 计划在奥斯汀建立内容审核总部据报道,该部门将招聘 100 名全职员工,将主要关注 CSE ...