I’m new to Elixir and the Elixir forum. I hope my question is clear and I post it on the correct topic!
I’m working on a webchat implementation using Phoenix. Whenever a message comes in, my app sends a post request to another backend server, which will send a response containing text in a stream. Currently, I’m assigning new text to the socket at the moment the text comes in. This way, the text appears in my liveview at (close to) the same pace as my backend serves streams the text.
I want to implement some buffer so I can dictate, in my Elixir app, the maximum rate at which text appears in my liveview, effectively creating a typewriting effect. I’m not sure how to do this using Elixir/Phoenix.
For a typewriter effect I assume you want each character to appear after the other with some delay. So you’d first have to decide how you want to do that
CSS (animation)
JS
on the server
For (3) you’d need to create a timer when you got new text in the buffer, handle the msg of the timer in a handle_info of the LV and there
I’m looking for something like (3). Is this also possible with something like a push queue? Where I would broadcasting to a pubsub-topic, but then this topic has a configurable dispatch rate. This would then publish it to a listener, that assigns the text to a socket, but at a maximum rate. As far as I know, this is not possible with the phoenix pubsub system.
Thank you for sharing the link. I’ve actually already implemented something similar to what was described. However, what I’m looking to incorporate additionally is the ability to control the rate at which the text is rendered or added to the socket. Essentially, rather than rendering the chunks at the pace dictated by the API, I aim to introduce the text into the socket at a constant rate that I can regulate, ensuring it’s either slower or equal to the rate at which the chunks are received.
I am learning elixir, so not sure if this is the best way, but this would be my suggestion.
So basically; have a typed_text_input ‘buffer’ and a typed_text_output assigned on the socket.
Then handle a :tick message which transfers a character from input to output and resends a :tick message to the same process…
This way if you assign something (coming from pubsub or whatever) to your typed_text_input it will end up char by char in typed_text_output.
def mount(_params, _session, socket) do
# start ticker
Process.send_after(self(), :tick, 100)
{:ok, assign(socket, typed_text_input: "Type something...", typed_text_output: "")}
end
def handle_info(:tick, socket) do
# send a message to the client
Process.send_after(self(), :tick, 100)
case socket.assigns.typed_text_input do
"" ->
{:noreply, socket}
input ->
# remove the first character from the input and append it to the output
output = input |> String.slice(0..0)
# reassign input and output
{:noreply,
assign(socket,
typed_text_input: String.slice(input, 1..-1),
typed_text_output: socket.assigns.typed_text_output <> output
)}
end
end
def render(assigns) do
~L"""
<div class="container">
<div>
<%= @typed_text_output %>
</div>
</p>
</div>
"""
end
Sounds like that might work! The only thing that I think needs to be added here is that you stop the “ticking” after the stream is done, so you don’t unnecessarily send tick events.
Thinking more about it, the client-side options seem more efficient. It makes sense to me that it’s better to send all the data as fast as possible to the client and animate it there. Do you know how you would approach such an implementation?