fix: briefing refresh — weather not populated + workspace conv bleed
Two bugs: 1. Manual 'Refresh' button didn't refresh weather/RSS before compiling. The daily scheduler calls refresh_all_feeds + refresh_location_cache before run_compilation; the manual trigger route called run_compilation directly, reading a stale cache. Manual trigger now mirrors the scheduler: refresh feeds and all configured weather locations first. 2. Navigating to WorkspaceView during a long briefing compilation caused the workspace chat to show the briefing content. triggerNow awaits ~30-60s; on completion it called loadAll() → chatStore.fetchConversation which overwrote the workspace's currentConversation in the shared store. Fixed with a _mounted flag — all post-async state writes in BriefingView are now guarded so they no-op if the component has been unmounted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
import { useBackgroundRefresh } from '@/composables/useBackgroundRefresh'
|
import { useBackgroundRefresh } from '@/composables/useBackgroundRefresh'
|
||||||
import { useVoiceRecorder } from '@/composables/useVoiceRecorder'
|
import { useVoiceRecorder } from '@/composables/useVoiceRecorder'
|
||||||
import { useVoiceAudio } from '@/composables/useVoiceAudio'
|
import { useVoiceAudio } from '@/composables/useVoiceAudio'
|
||||||
@@ -150,10 +150,10 @@ watch(selectedConvId, async (id) => {
|
|||||||
|
|
||||||
// Refresh messages after streaming ends
|
// Refresh messages after streaming ends
|
||||||
watch(() => chatStore.streaming, async (streaming) => {
|
watch(() => chatStore.streaming, async (streaming) => {
|
||||||
if (!streaming && selectedConvId.value === todayConvId.value && todayConvId.value) {
|
if (!streaming && _mounted && selectedConvId.value === todayConvId.value && todayConvId.value) {
|
||||||
const today = await getBriefingToday().catch(() => null)
|
const today = await getBriefingToday().catch(() => null)
|
||||||
if (today) messages.value = today.messages
|
if (_mounted && today) messages.value = today.messages
|
||||||
scrollToBottom()
|
if (_mounted) scrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -239,9 +239,10 @@ async function triggerNow() {
|
|||||||
triggering.value = true
|
triggering.value = true
|
||||||
try {
|
try {
|
||||||
await triggerBriefingSlot('compilation')
|
await triggerBriefingSlot('compilation')
|
||||||
await loadAll()
|
// Guard: user may have navigated away during the long compilation
|
||||||
|
if (_mounted) await loadAll()
|
||||||
} finally {
|
} finally {
|
||||||
triggering.value = false
|
if (_mounted) triggering.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +366,9 @@ useBackgroundRefresh(
|
|||||||
() => !chatStore.streaming && isToday.value && !!todayConvId.value,
|
() => !chatStore.streaming && isToday.value && !!todayConvId.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let _mounted = true
|
||||||
|
onUnmounted(() => { _mounted = false })
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await checkSetup()
|
await checkSetup()
|
||||||
if (!showWizard.value) await loadAll()
|
if (!showWizard.value) await loadAll()
|
||||||
|
|||||||
@@ -242,12 +242,30 @@ async def get_conversation_messages(conv_id: int):
|
|||||||
@briefing_bp.route("/trigger", methods=["POST"])
|
@briefing_bp.route("/trigger", methods=["POST"])
|
||||||
@_REQUIRE
|
@_REQUIRE
|
||||||
async def manual_trigger():
|
async def manual_trigger():
|
||||||
"""Dev/admin endpoint to manually trigger a briefing compilation."""
|
"""Manually trigger a briefing compilation, including a fresh data refresh."""
|
||||||
data = await request.get_json() or {}
|
data = await request.get_json() or {}
|
||||||
slot = data.get("slot", "compilation")
|
slot = data.get("slot", "compilation")
|
||||||
if slot not in ("compilation", "morning", "midday", "afternoon"):
|
if slot not in ("compilation", "morning", "midday", "afternoon"):
|
||||||
return jsonify({"error": "invalid slot"}), 400
|
return jsonify({"error": "invalid slot"}), 400
|
||||||
|
|
||||||
|
# Refresh external data first (mirrors what the scheduler does)
|
||||||
|
try:
|
||||||
|
from fabledassistant.services.rss import refresh_all_feeds
|
||||||
|
config_raw = await get_setting(g.user.id, "briefing_config", "{}")
|
||||||
|
config = json.loads(config_raw) if isinstance(config_raw, str) else {}
|
||||||
|
await refresh_all_feeds(g.user.id)
|
||||||
|
for key, loc in config.get("locations", {}).items():
|
||||||
|
if loc.get("lat") and loc.get("lon"):
|
||||||
|
await weather_svc.refresh_location_cache(
|
||||||
|
user_id=g.user.id,
|
||||||
|
location_key=key,
|
||||||
|
location_label=loc.get("label", key),
|
||||||
|
lat=loc["lat"],
|
||||||
|
lon=loc["lon"],
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Pre-trigger refresh failed for user %d", g.user.id, exc_info=True)
|
||||||
|
|
||||||
from fabledassistant.services.briefing_pipeline import run_compilation
|
from fabledassistant.services.briefing_pipeline import run_compilation
|
||||||
|
|
||||||
model = await get_setting(g.user.id, "default_model", "")
|
model = await get_setting(g.user.id, "default_model", "")
|
||||||
|
|||||||
Reference in New Issue
Block a user