diff --git a/one.org b/one.org index 4234349..f25c627 100644 --- a/one.org +++ b/one.org @@ -1086,6 +1086,529 @@ liveview_registry.add_middleware(auth_middleware) liveview_registry.add_middleware(logging_middleware) #+END_SRC +** Script Execution + +Execute JavaScript code directly from your Python handlers. + +⚠️ *Security Warning:* Only execute scripts from trusted sources. Never pass user input directly to the ~script~ parameter without sanitization, as this can lead to XSS (Cross-Site Scripting) vulnerabilities. + +*** Basic Script Execution + +#+BEGIN_SRC python +from liveview import liveview_handler, send + +@liveview_handler("show_notification") +def show_notification(consumer, content): + message = content["form"]["message"] + + # Execute JavaScript to show a browser notification + send(consumer, { + "script": f""" + if (Notification.permission === 'granted') {{ + new Notification('New Message', {{ + body: '{message}', + icon: '/static/icon.png' + }}); + }} + """ + }) +#+END_SRC + +*** Combining HTML and Script + +You can combine HTML updates with script execution: + +#+BEGIN_SRC python +@liveview_handler("load_chart") +def load_chart(consumer, content): + import json + chart_data = json.dumps(get_chart_data()) + + # Update HTML + html = render_to_string("chart_container.html", { + "chart_id": "sales-chart" + }) + + send(consumer, { + "target": "#chart-container", + "html": html + }) + + # Initialize chart with JavaScript + send(consumer, { + "script": f""" + const ctx = document.getElementById('sales-chart'); + new Chart(ctx, {{ + type: 'bar', + data: {chart_data} + }}); + """ + }) +#+END_SRC + +*** Inline Scripts in HTML + +Django LiveView automatically extracts and executes ~ + ''' + + send(consumer, { + "target": "#component-container", + "html": html + }) +#+END_SRC + +The script will be automatically extracted and executed after the HTML is rendered. + +*** Use Cases + +- Integrating third-party JavaScript libraries (charts, maps, etc.) +- Triggering browser APIs (notifications, geolocation, etc.) +- Initializing complex UI components +- Playing sounds or animations +- Focusing specific elements with custom logic + +*** Best Practices + +1. ✓ Sanitize any user input before including in scripts +2. ✓ Use JSON serialization for data: ~import json; json.dumps(data)~ +3. ✓ Prefer ~ +#+END_SRC + +*** File Size Limitations + +WebSocket has practical limits for Base64-encoded files: + +- ✓ *Small files* (< 1MB): Images, documents, avatars +- ⚠️ *Medium files* (1-5MB): May work but can be slow +- ✗ *Large files* (> 5MB): Not recommended, use traditional HTTP upload + +For large files, use a traditional HTTP POST to upload, then notify via WebSocket. + +*** Security Considerations + +1. ✓ Validate file types (check magic bytes, not just extensions) +2. ✓ Limit file sizes on the server +3. ✓ Scan files for malware if accepting from untrusted users +4. ✓ Store files outside the web root +5. ✓ Use unique filenames to prevent overwrites +6. ✓ Validate image dimensions and format with Pillow/PIL + +** Message Queue System + +Django LiveView automatically queues messages when the WebSocket connection is not ready. + +*** How It Works + +When you call a LiveView handler but the WebSocket is: +- Still connecting +- Temporarily disconnected +- Reconnecting after a network failure + +The message is automatically queued and sent once the connection is restored. + +#+BEGIN_SRC html + +#+END_SRC + +If the user clicks "Save Draft" while offline, the message is queued. When the connection is restored, all queued messages are sent automatically in order. + +*** User Feedback During Queueing + +Show users when their actions are being queued: + +#+BEGIN_SRC html +
+ + +#+END_SRC + +** Network Connectivity Handling + +Django LiveView automatically handles network connectivity changes. + +*** Automatic Detection + +The framework detects when: +- Network goes offline (airplane mode, WiFi disconnect, etc.) +- Network comes back online +- Connection to the server is lost +- Connection to the server is restored + +*** Visual Feedback + +Create a connection status modal that appears when connectivity is lost: + +#+BEGIN_SRC html + +{% load static %} +{% load liveview %} + + + + + {% block title %}My Site{% endblock %} + + + + +
+ ⚠️ Connection lost. Reconnecting... +
+ + {% block content %}{% endblock %} + + + + +#+END_SRC + +The framework automatically shows/hides this modal when connectivity changes. + +*** Reconnection Behavior + +When the connection is lost, Django LiveView: + +1. Shows the ~#no-connection~ modal (if it exists) +2. Queues any new messages +3. Attempts to reconnect automatically +4. Uses exponential backoff between attempts +5. Tries up to 5 times before giving up + +Default reconnection settings: + +- *Initial delay:* 3 seconds +- *Maximum attempts:* 5 +- *Backoff multiplier:* 1.5x +- *Maximum delay:* 30 seconds + +Reconnection delays: 3s → 4.5s → 6.75s → 10.12s → 15.18s + +** Registry Management + +Advanced control over LiveView handler registration. + +*** Listing All Handlers + +#+BEGIN_SRC python +from liveview import liveview_registry + +# Get all registered handler names +handlers = liveview_registry.list_functions() +print(handlers) # ['say_hello', 'load_articles', 'submit_form', ...] +#+END_SRC + +*** Getting a Specific Handler + +#+BEGIN_SRC python +# Get handler by name +handler = liveview_registry.get_handler("say_hello") + +if handler: + print(f"Handler found: {handler.__name__}") +else: + print("Handler not found") +#+END_SRC + +*** Unregistering Handlers + +Remove a handler from the registry: + +#+BEGIN_SRC python +# Unregister a specific handler +liveview_registry.unregister("old_handler") + +# Verify it's gone +if liveview_registry.get_handler("old_handler") is None: + print("Handler successfully unregistered") +#+END_SRC + +Use cases: +- Removing deprecated handlers +- Disabling features at runtime +- Testing and cleanup + +*** Clearing All Handlers + +#+BEGIN_SRC python +# Remove all registered handlers +liveview_registry.clear() + +# Verify +print(liveview_registry.list_functions()) # [] +#+END_SRC + +⚠️ *Warning:* This clears ALL handlers. Only use in testing or when reinitializing the application. + +*** Dynamic Handler Registration + +Register handlers programmatically without the decorator: + +#+BEGIN_SRC python +from liveview import liveview_registry, send + +def dynamic_handler(consumer, content): + send(consumer, { + "target": "#result", + "html": "

Dynamic handler executed!

" + }) + +# Register manually +handler_name = "dynamic_action" +decorated_func = liveview_registry.register(handler_name)(dynamic_handler) + +# Now callable from frontend +#