Personal noctalia plugins collection

fix: use CalendarService signals for async event loading

The plugin was stuck loading because loadEvents() called
updateEventsFromService() synchronously before CalendarService
had fetched EDS data. Now Main.qml listens to onEventsChanged,
onLoadingChanged, and onAvailableChanged signals to process
events when they actually arrive. Added 15s safety timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+68 -26
+65 -11
weekly-calendar/Main.qml
··· 51 51 onHourLineOpacitySettingChanged: eventsModelChanged() 52 52 onDayLineOpacitySettingChanged: eventsModelChanged() 53 53 54 + // React to CalendarService signals (async event delivery) 55 + Connections { 56 + target: CalendarService 57 + function onAvailableChanged() { 58 + if (CalendarService.available) { 59 + Qt.callLater(loadEvents) 60 + } else { 61 + isLoading = false 62 + if (pluginApi) syncStatus = pluginApi.tr("panel.no_service") 63 + } 64 + } 65 + function onEventsChanged() { 66 + if (isLoading || hasLoadedOnce) { 67 + Qt.callLater(updateEventsFromService) 68 + } 69 + } 70 + function onLoadingChanged() { 71 + if (!CalendarService.loading && isLoading) { 72 + Qt.callLater(updateEventsFromService) 73 + } 74 + } 75 + } 76 + 77 + // Safety timeout: if CalendarService never signals back, stop spinning 78 + Timer { 79 + id: loadingTimeout 80 + interval: 15000 81 + repeat: false 82 + onTriggered: { 83 + if (isLoading) { 84 + console.warn("[weekly-calendar] loading timeout, forcing update") 85 + updateEventsFromService() 86 + } 87 + } 88 + } 89 + 54 90 // IPC 55 91 IpcHandler { 56 92 target: "plugin:weekly-calendar" 57 93 function togglePanel() { pluginApi?.withCurrentScreen(s => pluginApi.togglePanel(s)) } 58 94 } 59 95 60 - Component.onCompleted: initializePluginSettings() 96 + Component.onCompleted: { 97 + initializePluginSettings() 98 + if (CalendarService.available) Qt.callLater(loadEvents) 99 + } 100 + 101 + onPluginApiChanged: { 102 + initializePluginSettings() 103 + if (CalendarService.available) Qt.callLater(loadEvents) 104 + } 61 105 62 106 function initializePluginSettings() { 63 107 if (!pluginApi) return ··· 78 122 loadEvents() 79 123 } 80 124 81 - // Fetch events 125 + // Fetch events - kicks off async request, signals will deliver results 82 126 function loadEvents() { 83 - console.log("[weekly-calendar] loadEvents called, available=" + CalendarService.available + " isLoading=" + isLoading + " hasLoadedOnce=" + hasLoadedOnce) 84 - if (!CalendarService.available || isLoading) return 127 + if (!pluginApi) return 128 + if (!CalendarService.available) { 129 + syncStatus = pluginApi.tr("panel.no_service") 130 + return 131 + } 85 132 86 133 isLoading = true 87 134 syncStatus = pluginApi.tr("panel.loading") 88 - 135 + 89 136 var now = new Date() 90 137 var start = new Date(weekStart), end = new Date(weekEnd) 91 138 start.setDate(start.getDate() - 7) 92 139 end.setDate(end.getDate() + 14) 93 - 140 + 94 141 CalendarService.loadEvents( 95 142 Math.max(0, Math.ceil((end - now) / 86400000)), 96 143 Math.max(0, Math.ceil((now - start) / 86400000)) 97 144 ) 98 - 145 + 99 146 hasLoadedOnce = true 100 - updateEventsFromService() 147 + loadingTimeout.restart() 148 + 149 + // If CalendarService already has events (cached), process them now 150 + if (CalendarService.events && CalendarService.events.length > 0) { 151 + Qt.callLater(updateEventsFromService) 152 + } 101 153 } 102 154 103 155 function updateEventsFromService() { 156 + if (!pluginApi) return 157 + loadingTimeout.stop() 104 158 clearEventModels() 105 - 159 + 106 160 if (!CalendarService.available) { 107 161 syncStatus = pluginApi.tr("panel.no_service") 108 162 } else if (!CalendarService.events?.length) { 109 163 syncStatus = pluginApi.tr("panel.no_events") 110 164 } else { 111 165 var stats = processCalendarEvents(CalendarService.events) 112 - syncStatus = stats.timedCount === 1 166 + syncStatus = stats.timedCount === 1 113 167 ? `${stats.timedCount} ${pluginApi.tr("panel.event")}, ${stats.allDayCount} ${pluginApi.tr("panel.allday")}` 114 168 : `${stats.timedCount} ${pluginApi.tr("panel.events")}, ${stats.allDayCount} ${pluginApi.tr("panel.allday")}` 115 169 } 116 - 170 + 117 171 isLoading = false 118 172 } 119 173
+3 -15
weekly-calendar/Panel.qml
··· 26 26 property real timeColumnWidth: 65 * Style.uiScaleRatio 27 27 property real daySpacing: 1 * Style.uiScaleRatio 28 28 29 - // Attempt at live syncing 30 - Connections { 31 - target: CalendarService 32 - enabled: root.visible 33 - function onEventsChanged() { 34 - if (mainInstance) { 35 - Qt.callLater(() => { 36 - mainInstance.updateEventsFromService() 37 - mainInstance.calculateAllDayEventLayout() 38 - }) 39 - } 40 - } 41 - } 42 - 29 + // Panel doesn't need its own CalendarService connection - Main.qml handles it. 30 + // When panel opens, trigger a fresh load if needed. 43 31 Component.onCompleted: mainInstance?.initializePlugin() 44 32 onVisibleChanged: if (visible && mainInstance) { 45 - mainInstance.updateEventsFromService() 33 + mainInstance.loadEvents() 46 34 mainInstance.goToToday() 47 35 Qt.callLater(root.scrollToCurrentTime) 48 36 }