diff --git a/core/clients/xmpp.py b/core/clients/xmpp.py index 34394aa..f354bf9 100644 --- a/core/clients/xmpp.py +++ b/core/clients/xmpp.py @@ -1033,7 +1033,7 @@ class XMPPComponent(ComponentXMPP): sender="XMPP", text=body, ts=int(now().timestamp() * 1000), - # outgoing=detail.is_outgoing_message, ????????? TODO: + outgoing=True, ) self.log.info("Stored a message sent from XMPP in the history.") diff --git a/core/templates/partials/compose-panel.html b/core/templates/partials/compose-panel.html index 575e794..6e790b3 100644 --- a/core/templates/partials/compose-panel.html +++ b/core/templates/partials/compose-panel.html @@ -284,6 +284,17 @@ {% endif %}
@@ -538,6 +549,18 @@ #{{ panel_id }} .compose-msg-meta { margin: 0; } + #{{ panel_id }} .compose-ticks { + display: inline-flex; + align-items: center; + gap: 0.22rem; + margin-left: 0.4rem; + color: #6b7787; + font-size: 0.72rem; + } + #{{ panel_id }} .compose-tick-time { + font-size: 0.66rem; + color: #616161; + } #{{ panel_id }} .compose-platform-switch { margin-top: 0.32rem; } @@ -1736,7 +1759,41 @@ if (msg.author) { metaText += " · " + String(msg.author); } - meta.textContent = metaText; + meta.textContent = metaText; + // Render delivery/read ticks and a small time label when available. + if (msg.read_ts) { + const tickWrap = document.createElement("span"); + tickWrap.className = "compose-ticks"; + tickWrap.title = "Read at " + String(msg.read_display || msg.read_ts || ""); + const icon = document.createElement("span"); + icon.className = "icon is-small"; + const i = document.createElement("i"); + i.className = "fa-solid fa-check-double has-text-info"; + icon.appendChild(i); + const timeSpan = document.createElement("span"); + timeSpan.className = "compose-tick-time"; + timeSpan.textContent = String(msg.read_display || ""); + tickWrap.appendChild(icon); + tickWrap.appendChild(timeSpan); + meta.appendChild(document.createTextNode(" ")); + meta.appendChild(tickWrap); + } else if (msg.delivered_ts) { + const tickWrap = document.createElement("span"); + tickWrap.className = "compose-ticks"; + tickWrap.title = "Delivered at " + String(msg.delivered_display || msg.delivered_ts || ""); + const icon = document.createElement("span"); + icon.className = "icon is-small"; + const i = document.createElement("i"); + i.className = "fa-solid fa-check-double has-text-grey"; + icon.appendChild(i); + const timeSpan = document.createElement("span"); + timeSpan.className = "compose-tick-time"; + timeSpan.textContent = String(msg.delivered_display || ""); + tickWrap.appendChild(icon); + tickWrap.appendChild(timeSpan); + meta.appendChild(document.createTextNode(" ")); + meta.appendChild(tickWrap); + } bubble.appendChild(meta); row.appendChild(bubble); diff --git a/core/views/compose.py b/core/views/compose.py index 08566ee..cce6c51 100644 --- a/core/views/compose.py +++ b/core/views/compose.py @@ -324,6 +324,14 @@ def _serialize_message(msg: Message) -> dict: ) display_text = text_value if text_value.strip() else ("(no text)" if not image_url else "") author = str(msg.custom_author or "").strip() + delivered_ts = int(msg.delivered_ts or 0) + read_ts = int(msg.read_ts or 0) + delivered_display = _format_ts_label(int(delivered_ts)) if delivered_ts else "" + read_display = _format_ts_label(int(read_ts)) if read_ts else "" + ts_val = int(msg.ts or 0) + delivered_delta = int(delivered_ts - ts_val) if delivered_ts and ts_val else None + read_delta = int(read_ts - ts_val) if read_ts and ts_val else None + return { "id": str(msg.id), "ts": int(msg.ts or 0), @@ -335,6 +343,12 @@ def _serialize_message(msg: Message) -> dict: "hide_text": hide_text, "author": author, "outgoing": _is_outgoing(msg), + "delivered_ts": delivered_ts, + "read_ts": read_ts, + "delivered_display": delivered_display, + "read_display": read_display, + "delivered_delta": delivered_delta, + "read_delta": read_delta, } diff --git a/docker-compose.yml b/docker-compose.yml index beaa77f..630b5e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -271,6 +271,48 @@ services: # memory: 0.25G #network_mode: host + # Optional watcher service to restart the runtime router (UR) when core code changes. + # This runs the `docker/watch_and_restart.py` script inside the same image and + # will restart the `ur_gia` container when files under `/code/core` change. + watch_ur: + image: xf/gia:prod + container_name: watch_ur_gia + build: + context: . + args: + OPERATION: ${OPERATION} + command: sh -c '. /venv/bin/activate && python docker/watch_and_restart.py' + volumes: + - ${REPO_DIR}:/code + - ${REPO_DIR}/docker/uwsgi.ini:/conf/uwsgi.ini + - ${APP_DATABASE_FILE}:/conf/db.sqlite3 + - type: bind + source: /code/vrun + target: /var/run + environment: + WATCH_PATHS: "/code/core" + TARGET_CONTAINER: "ur_gia" + + # Optional watcher service to restart the scheduling process when app code changes. + watch_scheduling: + image: xf/gia:prod + container_name: watch_scheduling_gia + build: + context: . + args: + OPERATION: ${OPERATION} + command: sh -c '. /venv/bin/activate && python docker/watch_and_restart.py' + volumes: + - ${REPO_DIR}:/code + - ${REPO_DIR}/docker/uwsgi.ini:/conf/uwsgi.ini + - ${APP_DATABASE_FILE}:/conf/db.sqlite3 + - type: bind + source: /code/vrun + target: /var/run + environment: + WATCH_PATHS: "/code/app" + TARGET_CONTAINER: "scheduling_gia" + redis: image: redis container_name: redis_gia diff --git a/docker/uwsgi.ini b/docker/uwsgi.ini index d846186..93980ab 100644 --- a/docker/uwsgi.ini +++ b/docker/uwsgi.ini @@ -23,5 +23,7 @@ log-level=debug # Autoreload on code changes (graceful reload) py-autoreload=1 -py-autoreload-on-edit=/code/GIA/core -py-autoreload-on-edit=/code/GIA/app \ No newline at end of file +# In the container the repository is mounted at /code, not /code/GIA +# point autoreload at the actual in-container paths +py-autoreload-on-edit=/code/core +py-autoreload-on-edit=/code/app \ No newline at end of file diff --git a/docker/watch_and_restart.py b/docker/watch_and_restart.py index efa2208..1e683a6 100644 --- a/docker/watch_and_restart.py +++ b/docker/watch_and_restart.py @@ -1,6 +1,27 @@ #!/usr/bin/env python3 """ -Watch for code changes in core/ and app/ and restart ur_gia container. +Watch for code changes and restart a target container. + +This script watches one or more directories (set via the `WATCH_PATHS` +environment variable, comma-separated) and restarts the container named by +`TARGET_CONTAINER` (defaults to `ur_gia`) when a filesystem change is detected. + +Typical usage in this repo (examples are provided in `docker-compose.yml`): + + - To restart the runtime router (UR) when core code changes set: + WATCH_PATHS=/code/core + TARGET_CONTAINER=ur_gia + + - To restart the scheduling command when app code changes set: + WATCH_PATHS=/code/app + TARGET_CONTAINER=scheduling_gia + +If you need to force a restart manually (for example to refresh a running +management command), you can "touch" any file under the watched path: + + docker compose exec