${messageContent}
+ +${messageContent}
+ + ` + return li +} +``` + +Each message item displays the message content itself with its timestamp. Using `.mine` we can append a different class to the item so maybe you can show the message to the right. + +### Message Form + +![chat heading screenshot][11] + +``` + +``` + +Add that form to the current markup. + +``` +page.getElementById('message-form').onsubmit = messageSubmitter(conversationID) +``` + +Attach an event listener to the “submit” event. + +``` +function messageSubmitter(conversationID) { + return async ev => { + ev.preventDefault() + + const form = ev.currentTarget + const input = form.querySelector('input') + const submitButton = form.querySelector('button') + + input.disabled = true + submitButton.disabled = true + + try { + const message = await createMessage(input.value, conversationID) + input.value = '' + const messagesOList = document.getElementById('messages') + if (messagesOList === null) { + return + } + + messagesOList.appendChild(renderMessage(message)) + } catch (err) { + if (err.statusCode === 422) { + input.setCustomValidity(err.body.errors.content) + } else { + alert(err.message) + } + } finally { + input.disabled = false + submitButton.disabled = false + + setTimeout(() => { + input.focus() + }, 0) + } + } +} + +function createMessage(content, conversationID) { + return http.post(`/api/conversations/${conversationID}/messages`, { content }) +} +``` + +We make use of [partial application][12] to have the conversation ID in the “submit” event handler. It takes the message content from the input and does a POST request to `/api/conversations/{conversationID}/messages` with it. Then prepends the newly created message to the list. + +### Messages Subscription + +To make it realtime we’ll subscribe to the message stream in this page also. + +``` +page.addEventListener('disconnect', subscribeToMessages(messageArriver(conversationID))) +``` + +Add that line in the `conversationPage()` function. + +``` +function subscribeToMessages(cb) { + return http.subscribe('/api/messages', cb) +} + +function messageArriver(conversationID) { + return message => { + if (message.conversationID !== conversationID) { + return + } + + const messagesOList = document.getElementById('messages') + if (messagesOList === null) { + return + + } + messagesOList.appendChild(renderMessage(message)) + readMessages(message.conversationID) + } +} + +function readMessages(conversationID) { + return http.post(`/api/conversations/${conversationID}/read_messages`) +} +``` + +We also make use of partial application to have the conversation ID here. +When a new message arrives, first we check if it’s from this conversation. If it is, we go a prepend a message item to the list and do a POST request to `/api/conversations/{conversationID}/read_messages` to updated the last time the participant read messages. + +* * * + +That concludes this series. The messenger app is now functional. + +~~I’ll add pagination on the conversation and message list, also user searching before sharing the source code. I’ll updated once it’s ready along with a hosted demo 👨💻~~ + +[Souce Code][13] • [Demo][14] + +-------------------------------------------------------------------------------- + +via: https://nicolasparada.netlify.com/posts/go-messenger-conversation-page/ + +作者:[Nicolás Parada][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://nicolasparada.netlify.com/ +[b]: https://github.com/lujun9972 +[1]: https://nicolasparada.netlify.com/posts/go-messenger-schema/ +[2]: https://nicolasparada.netlify.com/posts/go-messenger-oauth/ +[3]: https://nicolasparada.netlify.com/posts/go-messenger-conversations/ +[4]: https://nicolasparada.netlify.com/posts/go-messenger-messages/ +[5]: https://nicolasparada.netlify.com/posts/go-messenger-realtime-messages/ +[6]: https://nicolasparada.netlify.com/posts/go-messenger-dev-login/ +[7]: https://nicolasparada.netlify.com/posts/go-messenger-access-page/ +[8]: https://nicolasparada.netlify.com/posts/go-messenger-home-page/ +[9]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/heading.png +[10]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/list.png +[11]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/form.png +[12]: https://en.wikipedia.org/wiki/Partial_application +[13]: https://github.com/nicolasparada/go-messenger-demo +[14]: https://go-messenger-demo.herokuapp.com/ diff --git a/sources/tech/20181228 2018- Year in review.md b/sources/tech/20181228 2018- Year in review.md deleted file mode 100644 index 91099492ac..0000000000 --- a/sources/tech/20181228 2018- Year in review.md +++ /dev/null @@ -1,173 +0,0 @@ -[#]: collector: (lujun9972) -[#]: translator: ( ) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) -[#]: subject: (2018: Year in review) -[#]: via: (https://jvns.ca/blog/2018/12/23/2018--year-in-review/) -[#]: author: (Julia Evans https://jvns.ca/) - -2018: Year in review -====== - -I wrote these in [2015][1] and [2016][2] and [2017][3] and it’s always interesting to look back at them, so here’s a summary of what went on in my side projects in 2018. - -### ruby profiler! - -At the beginning of this year I wrote [rbspy][4] (docs: