mirror of
https://github.com/logos-co/logos-chat-ui.git
synced 2026-08-27 09:11:11 +00:00
* fix: format every timestamp through one authority Message bubbles formatted their own clock time in QML while the conversation list formatted its in the model, so the same kind of value showed up in two shapes at once, "16:44:34" next to "4:45 PM". Both now render the system locale's short time with the seconds field dropped, and the bubble takes its label from a model role instead of formatting. * fix: switch conversations without flashing the previous one Selecting a conversation updated the header, the message model and the roster as three independent streams, and the view renders them through separate replicas, so a switch showed the previous conversation's rows under the new title, then a false "No messages yet", then the real thread. The backend now reports which conversation the models actually hold, and the panes render rows only while that matches the selection: a thread still loading stays blank for a moment, then shows placeholder bubbles, and only a settled model may call a thread empty. The sidebar and the header no longer wait for that round trip at all: a clicked row carries its own name, kind and description, which the view applies straight away and drops once the backend confirms the switch. * fix: tell the user why the composer is closed The composer showed "Chat not connected" whenever it was disabled, so a connected app with no conversation selected reported a connection problem it did not have. It now names the actual cause, keeping the offline wording for offline and asking for a selection otherwise. * fix: keep a message the module refused The composer cleared on submit and the failure path only raised a toast, so a rejected message was gone: the text had already been dropped from the field and from the conversation's draft. The backend now reports the refused message and the thread offers it back in the composer, leaving a composer the user has already typed into alone. * fix: let status messages go quiet The status bar kept the last event it was handed, so "Address ready" or an invite confirmation sat there for the rest of the session and read as the app's current state. A message now clears after a few seconds; the connectivity label and the identity stay, since those are state rather than announcements. * fix: say what a pending member means without the protocol Inviting someone reported "peer joins when the group commits", the explainer named deMLS and its commit timer, and a pending row's tooltip read "waiting for commit". None of that means anything to the person waiting for a friend to show up in a group. The same facts, including the up-to-a-minute wait, are now stated in the user's terms. * fix: count invited members separately in the group details The details dialog rendered its plural source string literally, "1 member(s)", and counted only the committed roster, so a group with an invitation in flight looked like it had lost the invited person. It now reads "3 members, 1 invited", with the backend reporting outstanding invitations alongside the roster size. * fix: mark a conversation someone else started as unseen A conversation arriving from an invitation appeared in the sidebar with nothing to distinguish it, so a group someone added you to slid in unnoticed among the existing rows. An incoming conversation now carries the unread badge until it is opened, the same as an unread message does. * feat: start conversations from one New menu in the sidebar The header carried two side-by-side buttons that truncated their own labels to "New gr...", plus a connectivity dot the status bar already shows. A single "+ New" button now opens a menu with an entry per kind of conversation, and the dot is gone. Sharing your own address stays the full-width action at the foot of the list. * feat: give every copyable value a copy button next to it Copying a member's address meant clicking the row with nothing saying so, and the dialogs put their copy action in the footer, away from the value it acts on. Each copyable value now carries a copy button beside it, keeping the row click and the inline confirmation as they were. The icon ships with the plugin rather than coming from the design system, so it renders on hosts whose design system predates it. * feat: show details for a direct conversation A direct conversation had no details surface at all: the Details button was reserved for groups, so there was nowhere to read or copy the address of the person you were talking to. Details now opens for any conversation, with a dialog for direct ones carrying the peer's address and the conversation id. The address comes from the conversation's roster, which the backend now fetches for direct conversations too, and its row stays hidden until the roster provides one. * feat: copy a message from its context menu Selecting message text worked, but the desktop reflex of right-clicking a message offered nothing, so copying meant dragging over the text first. A right-click now opens a menu whose Copy takes the selection when there is one and the whole message otherwise. The rows share one menu. * fix: make the open conversation and an empty one readable in the list The selected row's tint sat close enough to the hover tint that the open conversation was hard to pick out, and a conversation with no messages left its second line blank, which read as a rendering gap. The selection is now distinctly tinted and an empty conversation says so where its last message would be. * fix: size dialog display areas to what they show The address and description areas were fixed-height boxes taller than their usual contents, so a one-word description sat in an 80px well with a scrollbar down the side of it. The address now takes the height of its content and stops at the height it used to have, scrolling past that. The description, where a content-sized scroll view sheared the label beneath it, is instead a plain text bounded to a few lines. * fix: make the group self-marker legible The "you" tag that marks the current user in a group's member list was set in the badge text size, small enough to be awkward to read beside the member name. Size it like the name. * fix: keep the error toast clear of the composer The transient error toast was bottom-centered in the window overlay, which laid it over the message input and send button, the exact controls a failed action points the user back to. Show it as a top-center banner instead. * refactor: move the components' private helpers into a d object An underscore prefix only labels a function internal while leaving it callable on the component's surface; holding it in a child QtObject actually scopes it away, the shape the design system's own controls use. The component tests switch from the removed helpers to the action buttons' clicked signal, which exercises the same guards through the surface a user reaches. * chore: pin chat_module to the roster and design system this view needs The redesign draws on design-system controls and tokens that reach this flake through chat_module's builder, and the member panel needs a roster that flags an invite the group has not committed yet and that answers for a direct conversation. One rev carries both; release tags predate it. That design system ships its QML as static modules an app links, so no built artifact holds a loose Logos tree for the QML checks to import; they take its source tree at the rev this flake resolves, which also spares that job a build of the standalone app. * feat: keep this account's address on screen Sharing an address took three actions (a button, a dialog, a copy) for a value the app already knows the moment it is online, and the footer printed an installation id nobody can act on instead. The backend now reads get_address() on the online transition and publishes it, and an account card at the foot of the sidebar carries the short identity, the connection state and the address itself with a copy beside it. * feat: draw the chat's own icon set The redesign leans on glyphs the design system does not carry (a group, a person, an info toggle, a send arrow, an add-member, a caret), and the hosts' Qt has no SVG image plugin, so the module can only load rasters. The sources live under tools/icons with a script that renders them, so a glyph can be edited rather than redrawn, and the existing copy icon is re-cut from the same stroke family. * feat: give every identity an avatar A list of hex labels reads as one undifferentiated column, and the eye needs something faster than eight characters to tell one conversation or member from another. Every model now carries the initials and a colour ramp for its row's identity, hashed the same way in one place so an account keeps its colour across the sidebar, the roster and this account's own card, and a group is squared off and glyphed so it reads apart from a person. * refactor: lay the sidebar out as cards The panes met edge to edge with only a hairline between them, so nothing on screen was grouped and the eye had to find the boundaries. The sidebar is now a conversations card over the account card on an inset background, its rows carry the avatar with the time and unread count stacked out of the name's way, and one full-width New chat button naming what each entry starts replaces the header and its menu. The status strip goes with it: the connection state belongs on the account card, and the messages it carried were either failures the toast already reports or narration of what the list itself shows. * refactor: rebuild the thread around who is speaking A thread of identical bubbles under a bare title gave no answer to "who is this and who else is here", and the composer carried a labelled button and a growing row of hints for a field that only ever sends text. The header now names the conversation with its avatar, one elided line of description and the faces of its members, and offers its remaining facts behind one toggle; a message carries its sender's face and name in their own colour where a run of theirs begins, dates ride on a chip over the thread rather than a rule through it, and the composer is one pill with the send action at its end. * refactor: give the right column the roster and the facts on demand A group's roster had a header and a floating button and nothing else, while the conversation's own facts (its full description, what kind it is, its id, the other participant's address) were locked in two modal dialogs that had to be dismissed before the thread could be read again. The roster is now a card with its count in the heading and Add member pinned to the foot, and one details panel above it carries what both dialogs did, opening and closing from the thread header without covering anything. * refactor: copy a member's address from the row's own menu A copy button appeared on every roster row the pointer crossed, so a list whose whole job is to say who is in the group was never at rest. A right-click on a row now offers Copy address, and the row itself is nothing but the member's face, identity and standing, with an invitation that has not committed saying so in words. * test: cover the redesign's new components standalone Every component has to instantiate from its declared properties alone, with mock data and no host context, and the chat vocabulary the redesign added had no such proof: the icon button, the day chip, the facepile and its overflow, the panel heading and its count, a detail row's copy, and the thread header holding one line of description whatever it is given. * refactor: take the design system's monospace token Every hex identifier the chat shows named the platform's generic monospace family itself, behind a local token standing in for one the design system did not have. It has one now, so the stand-in and its upstream TODO go. * fix: hold the side columns to their widths and open a conversation ready to type A layout nested in another layout fills by default, so the sidebar and the right column took the slack meant for the thread: at 1024px the sidebar rendered 415px against a design of 320. Both are pinned to their widths now, the thread carries a floor so a window too narrow for three columns clips the right one rather than the middle, and selecting a conversation puts the caret in its composer. * fix: keep a pending member on the roster across chat switches The pending marker was tracked in the backend as a single set of addresses for whichever conversation was open, so switching away had to clear it or its rows would leak into the next group. Coming back re-read the roster from the module, which listed committed members only, and the invite vanished from the panel until the group committed it minutes later. The module's roster now flags an invite it has not committed yet, so the marker is read per conversation from the module instead of being tracked here, and survives any number of switches. memberCount stays committed-only, and a pending invite no longer counts as being on the roster when a message arrives from an address the panel has not seen. The same rev answers the roster call for a direct conversation, where it used to error, so the details panel resolves the peer's address instead of leaving the row blank. * docs: bring the README and the exchange screenshots up to the redesigned UI The screenshots showed the pre-card layout, the README claimed a group opened without a dialog and listed roles two models no longer carry, and a hero image nothing referenced sat in docs/ showing a UI that no longer exists. The captures are regenerated from the current app, the README's structure and component tables name the roster model and the identity and time-formatting helpers alongside the two list models, and the walkthrough records why the icons are missing from an offscreen capture. * ci: run the macOS doc-test without the Logos cache The delivery-module .lgx cached in Attic was packaged without the libiconv its librln.dylib loads, so a macOS run that substitutes it instead of building it crashes the doc-test. Without the cache the module builds from source, which realises the dependency. Revert this once the repinned delivery module bundles what its dylibs load.