ypergen

- take a break from javascript

Back to documentation

Websockets chat

Open multiple tabs to see messages pushed out to all listening consumers.

 

Visit this page to try sending a chat message from the backend..


Show sourcesurls.py
from hypergen.hypergen import autourls
from websockets import views

app_name = 'websockets'
urlpatterns = autourls(views, app_name)
views.py
from hypergen.imports import *
from website.templates2 import base_example_template, show_sources
### chat ###

# Channels urls are not (yet) reversible the same as vanilla urls. Little helper to add protocol and port.
chat_ws_url = lambda: ws_url("/ws/chat/hypergen/")

@liveview(perm=NO_PERM_REQUIRED, base_template=base_example_template)
def chat(request):
    h3("Websockets chat")
    p("Open multiple tabs to see messages pushed out to all listening consumers.")
    # Open a websocket on the client. Can be closed at any point with: command("hypergen.websocket.close", url)
    command("hypergen.websocket.open", chat_ws_url())

    # Some custom styling.
    style(""" input, textarea {width: 100%} """)

    # The consumer will write here.
    with p(id="counter"):
        raw(" ")

    # The input field where the user types the chat message.
    input_(
        id_="message",
        type_="text",
        placeholder="Write your message here and press enter.",
        autofocus=True,
        # This callbacks goes to the ChatConsumer in websockets.consumers, because the url starts with "ws://"
        # or "wss://".
        # Will only trigger when the user presses Enter.
        onkeyup=callback(chat_ws_url(), "chat__message_from_frontend", THIS, when=["hypergen.when.keycode", "Enter"],
        clear=True),
    )

    # Chat messages are shown here.
    ul(id_="messages")

    # Backend send.
    p("Visit", a("this page", href=send_message_from_backend.reverse(), target="_blank"),
        "to try sending a chat message from the backend.", sep=" ", end=".")

    show_sources(__file__)

@liveview(perm=NO_PERM_REQUIRED, base_template=base_example_template)
def send_message_from_backend(request):
    from websockets.consumers import ChatConsumer
    group_send(ChatConsumer.group_name, {"type": "chat__message_from_backend", "message": "Server message!"})
    h2("Websocket server command")
    p("The message will appear on the tab room tabs.")

    show_sources(__file__)
consumers.py
from hypergen.imports import *

class ChatConsumer(HypergenWebsocketConsumer):
    group_name = "websockets__consumers__ChatConsumer"

    # django-channels will automatically subscribe the consumer to these groups.
    groups = [group_name]

    # Receives the data sent from the onkeyup callback in views.py.
    def receive_hypergen_callback(self, event_type, *args):
        if event_type == "chat__message_from_frontend":
            message, = args
            assert type(message) is str
            message = message.strip()[:1000]
            if message:
                commands = self.update_page(message)
                # Send commands to entire group.
                self.group_send_hypergen_commands(self.group_name, commands)

        # ... More event types goes here.

    def chat__message_from_backend(self, event):
        commands = self.update_page(event["message"])
        # Send commands to individual channel.
        self.channel_send_hypergen_commands(commands)

    def update_page(self, message):
        return hypergen(self.template, message, settings=dict(action=True, returns=COMMANDS, target_id="counter"))

    # Render the HTML and issue custom commands.
    def template(self, message):
        # Writes into the "counter" id.
        span("Length of last message is: ", len(message))

        # Appends the message to the list of messages. Uses hypergen() directly to render into a string of HTML.
        command("hypergen.append", "messages", hypergen(lambda: li(message)))
routing.py
from hypergen.imports import NO_PERM_REQUIRED
from django.urls import path
from websockets import consumers

websocket_urlpatterns = [path(r'ws/chat/<slug:room_name>/', consumers.ChatConsumer.as_asgi(perm=NO_PERM_REQUIRED))]