Skip to content

V0.3#2

Merged
WilleLX1 merged 3 commits intomainfrom
V0.3
Mar 4, 2026
Merged

V0.3#2
WilleLX1 merged 3 commits intomainfrom
V0.3

Conversation

@WilleLX1
Copy link
Copy Markdown
Owner

@WilleLX1 WilleLX1 commented Mar 4, 2026

No description provided.

- Implemented a new FastAPI endpoint for retrieving system version information at /api/v2/system/version.
- Created ConversationReadCursor model to track the last read message for users in conversations.
- Added version_info service to resolve server version details including git commit and build timestamp.
- Introduced read_state_service_v2 to manage read cursor functionality and relay read events.
- Developed typing_service_v2 to handle typing indicators and relay typing events.
- Added integration tests for typing and read cursor functionalities, ensuring proper event fanout and access control.
- Created database migration for conversation_read_cursors table.
Copilot AI review requested due to automatic review settings March 4, 2026 15:24
@WilleLX1 WilleLX1 merged commit 82049dd into main Mar 4, 2026
1 check failed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR advances the Blackwire v0.3 wave by formalizing /api/v2 as the primary surface and adding real-time UX features (typing + read cursors + version visibility) across server, API spec, and the Qt client.

Changes:

  • Add /api/v2 typing indicators and persistent read cursors with WS fanout + federation relays (plus DB migration/model).
  • Add /api/v2/system/version endpoint and client/server version display in the Qt client.
  • Update docs/site to v0.3, plus significant Qt client UX/theme and attachment/markdown rendering updates.

Reviewed changes

Copilot reviewed 49 out of 49 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
spec/api.md Updates API spec to /api/v2 primary, documents typing/read/version + federation endpoints
server/app/services/typing_service_v2.py Implements typing publish + federation relay fanout
server/app/services/read_state_service_v2.py Implements read-cursor persistence, fanout, federation relay
server/migrations/versions/20260301_0009_conversation_read_cursors.py Adds conversation_read_cursors table + indexes
server/app/services/version_info.py Resolves version metadata for /system/version
server/app/api_v2/conversations.py Adds REST endpoints for typing + read cursor write/read
server/app/api_v2/federation.py Adds federation relay endpoints for typing/read
server/app/api_v2/system.py Adds /api/v2/system/version route
server/app/config.py Enables v0.3 defaults + adds typing/read settings/rate limits
server/tests/integration/test_v2_typing_read_version.py Integration coverage for typing/read/version + federation relay plumbing
client-cpp-gui/src/** + include/** Adds typing/read handling, version fetch/display, attachment lifecycle, markdown/media rendering, and UI refreshes
client-cpp-gui/src/ui/theme.cpp + CMakeLists.txt Theme redesign + Qt MultimediaWidgets dependency + versioning
client-cpp-gui/src/ui/theme.cpp.bak Adds an unintended backup file (should be removed)
V0.3_Update.md / README.md / index.html Updates release docs/site to v0.3 and describes new capabilities

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +148 to +159
if last_read_sent_at_ms < existing.last_read_sent_at_ms:
return existing, False
if (
last_read_sent_at_ms == existing.last_read_sent_at_ms
and last_read_message_id == existing.last_read_message_id
):
return existing, False

existing.last_read_message_id = last_read_message_id
existing.last_read_sent_at_ms = last_read_sent_at_ms
existing.user_address = user_address
existing.updated_at = datetime.now(UTC)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_upsert_cursor treats updates with the same last_read_sent_at_ms but a different last_read_message_id as a forward move and overwrites the cursor. This violates the documented monotonicity rule (equal timestamps are not strictly monotonic) and can allow cursor “flip-flopping” if multiple messages share the same sent_at_ms (client-controlled). Consider treating last_read_sent_at_ms == existing.last_read_sent_at_ms as a no-op unless the message id is identical, or enforcing a deterministic tie-break (e.g., compare the message’s server ordering/created_at).

Copilot uses AI. Check for mistakes.
Comment on lines +1078 to +1087
QObject::connect(body, &QLabel::linkActivated, body, [](const QString& url) {
const auto answer = QMessageBox::question(
nullptr,
"Open Link",
QString("Do you want to open this link?\n\n%1").arg(url),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer == QMessageBox::Yes) {
QDesktopServices::openUrl(QUrl(url));
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown link handler opens whatever URL Qt parses via QDesktopServices::openUrl(QUrl(url)). Even with a confirmation dialog, this can still launch potentially unsafe schemes (e.g., file:, custom handlers). Consider validating QUrl(url) and whitelisting allowed schemes (typically http/https, and possibly mailto if desired) before enabling the open action.

Copilot uses AI. Check for mistakes.
Comment on lines +985 to +995
if (media_kind == "image") {
QPixmap image_preview;
image_preview.loadFromData(file_bytes);
if (!image_preview.isNull()) {
auto* image_label = new QLabel(bubble);
image_label->setObjectName("messageImage");
image_label->setPixmap(
image_preview.scaledToWidth(320, Qt::SmoothTransformation));
image_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
bubble_layout->addWidget(image_label);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline image preview decodes and loads attachment bytes directly into a QPixmap without any size/type guard. A malicious (or just very large) attachment could cause excessive memory use or UI hangs. Consider bounding preview generation (e.g., skip preview above a max byte size and/or validate the image dimensions before scaling).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +20
#include "blackwire/ui/theme.hpp"

#include <QApplication>
#include <QColor>
#include <QPalette>

namespace blackwire {

void ApplyAppTheme(QApplication& app) {
QPalette palette;
palette.setColor(QPalette::Window, QColor("#1e1f22"));
palette.setColor(QPalette::WindowText, QColor("#f2f3f5"));
palette.setColor(QPalette::Base, QColor("#2b2d31"));
palette.setColor(QPalette::AlternateBase, QColor("#313338"));
palette.setColor(QPalette::ToolTipBase, QColor("#313338"));
palette.setColor(QPalette::ToolTipText, QColor("#f2f3f5"));
palette.setColor(QPalette::Text, QColor("#f2f3f5"));
palette.setColor(QPalette::Button, QColor("#313338"));
palette.setColor(QPalette::ButtonText, QColor("#f2f3f5"));
palette.setColor(QPalette::BrightText, QColor("#ffffff"));
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client-cpp-gui/src/ui/theme.cpp.bak appears to be a backup artifact committed alongside the real theme.cpp. This adds ~500 lines of duplicate code and increases repo noise/build times. It should be removed from the PR (and ideally added to .gitignore for *.bak).

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +153
- Default: OFF (messages cleared on client restart, privacy-first)
- When enabled: encrypted plaintext cache persists across restarts
- When disabled: `local_messages` stripped at save time, runtime session intact
- Respects user privacy choice — no message history leak on shared devices
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This update doc says the message cache toggle default is OFF, but the implementation defaults SocialPreferences::save_message_cache to true (and unit test SaveMessageCacheDefaultsToTrue asserts that default). Please align the documentation with the actual default (or change the default in code if OFF is intended).

Suggested change
- Default: OFF (messages cleared on client restart, privacy-first)
- When enabled: encrypted plaintext cache persists across restarts
- When disabled: `local_messages` stripped at save time, runtime session intact
- Respects user privacy choice — no message history leak on shared devices
- Default: ON (messages persist across client restarts by default)
- When enabled: encrypted plaintext cache persists across restarts
- When disabled: `local_messages` stripped at save time, runtime session intact
- Respects user privacy choice — users can disable local history to avoid message leaks on shared devices

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants