fix: resolve NPC chat database persistence and modal targeting
Fixed two critical bugs in NPC chat functionality:
1. Database Persistence - Metadata serialization bug
- Empty dict {} was falsy, preventing JSON conversion
- Changed to unconditional serialization in ChatMessageService
- Messages now successfully save to chat_messages collection
2. Modal Targeting - HTMX targeting lost during polling
- poll_job() wasn't preserving hx-target/hx-swap parameters
- Pass targeting params through query string in polling cycle
- Responses now correctly load in modal instead of main panel
Files modified:
- api/app/services/chat_message_service.py
- public_web/templates/game/partials/job_polling.html
- public_web/app/views/game_views.py
This commit is contained in:
@@ -506,6 +506,10 @@ def poll_job(session_id: str, job_id: str):
|
||||
"""Poll job status - returns updated partial."""
|
||||
client = get_api_client()
|
||||
|
||||
# Get hx_target and hx_swap from query params (passed through from original request)
|
||||
hx_target = request.args.get('_hx_target')
|
||||
hx_swap = request.args.get('_hx_swap')
|
||||
|
||||
try:
|
||||
response = client.get(f'/api/v1/jobs/{job_id}/status')
|
||||
result = response.get('result', {})
|
||||
@@ -540,11 +544,14 @@ def poll_job(session_id: str, job_id: str):
|
||||
|
||||
else:
|
||||
# Still processing - return polling partial to continue
|
||||
# Pass through hx_target and hx_swap to maintain targeting
|
||||
return render_template(
|
||||
'game/partials/job_polling.html',
|
||||
session_id=session_id,
|
||||
job_id=job_id,
|
||||
status=status
|
||||
status=status,
|
||||
hx_target=hx_target,
|
||||
hx_swap=hx_swap
|
||||
)
|
||||
|
||||
except APIError as e:
|
||||
@@ -767,12 +774,15 @@ def talk_to_npc(session_id: str, npc_id: str):
|
||||
job_id = result.get('job_id')
|
||||
if job_id:
|
||||
# Return job polling partial for the chat area
|
||||
# Use hx-target="this" and hx-swap="outerHTML" to replace loading div with response in-place
|
||||
return render_template(
|
||||
'game/partials/job_polling.html',
|
||||
job_id=job_id,
|
||||
session_id=session_id,
|
||||
status='queued',
|
||||
is_npc_dialogue=True
|
||||
is_npc_dialogue=True,
|
||||
hx_target='this', # Target the loading div itself
|
||||
hx_swap='outerHTML' # Replace entire loading div with response
|
||||
)
|
||||
|
||||
# Immediate response (if AI is sync or cached)
|
||||
|
||||
@@ -10,10 +10,10 @@ Shows loading state while waiting for AI response, auto-polls for completion
|
||||
{% endif %}
|
||||
|
||||
<div class="loading-state"
|
||||
hx-get="{{ url_for('game.poll_job', session_id=session_id, job_id=job_id) }}"
|
||||
hx-get="{{ url_for('game.poll_job', session_id=session_id, job_id=job_id, _hx_target=hx_target, _hx_swap=hx_swap) }}"
|
||||
hx-trigger="load delay:1s"
|
||||
hx-swap="innerHTML"
|
||||
hx-target="#narrative-content">
|
||||
hx-swap="{{ hx_swap|default('innerHTML') }}"
|
||||
hx-target="{{ hx_target|default('#narrative-content') }}">
|
||||
<div class="loading-spinner-large"></div>
|
||||
<p class="loading-text">
|
||||
{% if status == 'queued' %}
|
||||
|
||||
Reference in New Issue
Block a user