To start your Phoenix server:
- Install dependencies with
mix deps.get
- Setup the project with
mix setup
; it will setup your database schema and install frontend dependencies; - Start Phoenix endpoint with
mix phx.server
or inside IEx withiex -S mix phx.server
Now you can visit localhost:4000
from your browser.
sona_comments
is a Phoenix application with a LiveView front-end. It serves one main LiveView at /
, displaying a fake blog post with the ability to comment. Responsibilities are distributed as follows:
SonaCommentsWeb.CommentLive.Index
is the main LiveView. It retrieves the top-level comments from the database, and sets up a changeset to post new comments. In addition, it subscribes to the comments topic on which theComments
context publishes new comments. If the comment it receives is a reply, it forwards the comment to that specific comment process viasend_update
;SonaCommentsWeb.CommentLive.CommentThreadComponent
is aLiveComponent
that encapsulates the logic for threads (i.e. replies to comments); it incrementally fetches replies through itspreload
implementation, and recursively renders nested replies.
Tests are mainly implemented as integration tests against the SonaCommentsWeb.CommentLive.Index
LiveView.
Tests to CommentThreadComponent
could be implemented in a more isolated way by rendering with live_isolated
, but in my opinion they would be less future-proof, as it would be impossible to test some interactions (e.g. navigation) in that way.
I did not spend a ton of time on a11y of the implementation. Still, I tried to use semantic HTML tags and ARIA attributes, such as aria-live-region
for the comments section. I also implemented a simple autofocus
hook that automatically focuses the reply textarea when it renders after clicking the reply button. The comments themselves are focusable, to display the reply button when navigating with the keyboard (not only on mouse hover).
To quickly style the app I integrated Tailwind via their official Phoenix plugin, and added the @tailwindcss/typography
plugin for text. Classes are repeated across components, but in a real codebase I would consider implementing Phoenix.Component
instances to encapsulate them in reusable components.
The codebase has the structure of a standard Phoenix project generated with mix phx.gen.new
and upgraded to LiveView 1.8; I generated the initial implementation for the LiveView with mix phx.gen.live
, then stripped out most of the unused code. Afterwards, I added credo
and pre_commit
to run it as a pre-commit hook; in a production codebase I would also have integrated dyalixir
for dyalizer checks.
I deployed the webapp on fly.io
, mostly to try out their flyctl
CLI. The Dockerfile in this repo is generated by their launch
command.