api: init notification queue
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import functools
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from enum import IntEnum
|
||||
from time import sleep
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from clan_lib.api import MethodRegistry
|
||||
from clan_lib.api import MethodRegistry, message_queue
|
||||
from clan_lib.api.tasks import WebThread
|
||||
from clan_lib.log_manager import LogManager
|
||||
|
||||
@@ -69,6 +71,22 @@ class Webview:
|
||||
if self.size:
|
||||
self.set_size(self.size)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.setup_notify() # Start the notification loop
|
||||
|
||||
def setup_notify(self) -> None:
|
||||
def loop() -> None:
|
||||
while True:
|
||||
try:
|
||||
msg = message_queue.get() # Blocks until available
|
||||
js_code = f"window.notifyBus({json.dumps(msg)});"
|
||||
self.eval(js_code)
|
||||
except Exception as e:
|
||||
print("Bridge notify error:", e)
|
||||
sleep(0.01) # avoid busy loop
|
||||
|
||||
threading.Thread(target=loop, daemon=True).start()
|
||||
|
||||
@property
|
||||
def handle(self) -> Any:
|
||||
"""Get the webview handle, creating it if necessary."""
|
||||
@@ -129,6 +147,7 @@ class Webview:
|
||||
webview=self, middleware_chain=tuple(self._middleware), threads={}
|
||||
)
|
||||
self._bridge = bridge
|
||||
|
||||
return bridge
|
||||
|
||||
# Legacy methods for compatibility
|
||||
|
||||
@@ -25,7 +25,6 @@ class WebviewBridge(ApiBridge):
|
||||
|
||||
def send_api_response(self, response: BackendResponse) -> None:
|
||||
"""Send response back to the webview client."""
|
||||
|
||||
serialized = json.dumps(
|
||||
dataclass_to_dict(response), indent=4, ensure_ascii=False
|
||||
)
|
||||
|
||||
9
pkgs/clan-app/ui/index.d.ts
vendored
Normal file
9
pkgs/clan-app/ui/index.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ProcessMessage } from "./src/hooks/api";
|
||||
|
||||
export {};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
notifyBus: (data: ProcessMessage) => void;
|
||||
}
|
||||
}
|
||||
@@ -99,3 +99,14 @@ export const callApi = <K extends OperationNames>(
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export interface ProcessMessage {
|
||||
topic: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any;
|
||||
origin: string | null;
|
||||
}
|
||||
|
||||
window.notifyBus = (data) => {
|
||||
console.debug("Channel function called with data:", data);
|
||||
};
|
||||
|
||||
@@ -5,11 +5,13 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from functools import wraps
|
||||
from inspect import Parameter, Signature, signature
|
||||
from queue import Queue
|
||||
from types import ModuleType
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Literal,
|
||||
TypedDict,
|
||||
TypeVar,
|
||||
get_type_hints,
|
||||
)
|
||||
@@ -31,6 +33,30 @@ T = TypeVar("T")
|
||||
ResponseDataType = TypeVar("ResponseDataType")
|
||||
|
||||
|
||||
class ProcessMessage(TypedDict):
|
||||
"""
|
||||
Represents a message to be sent to the UI.
|
||||
|
||||
Attributes:
|
||||
- topic: The topic of the message, used to identify the type of message.
|
||||
- data: The data to be sent with the message.
|
||||
- origin: The API operation that this message is related to, if applicable.
|
||||
"""
|
||||
|
||||
topic: str
|
||||
data: Any
|
||||
origin: str | None
|
||||
|
||||
|
||||
message_queue: Queue[ProcessMessage] = Queue()
|
||||
"""
|
||||
A global message queue for sending messages to the UI
|
||||
This can be used to send notifications or messages to the UI. Before returning a response.
|
||||
|
||||
The clan-app imports the queue as clan_lib.api.message_queue and subscribes to it.
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ApiError:
|
||||
message: str
|
||||
|
||||
Reference in New Issue
Block a user