Channel typeahead: ~ links a channel #320
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "channel-typeahead"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Typing
~in a composer offers the account's channels and inserts a reference to the one you pick. Closes #274. Rebased on #313, whose POST rule this follows.Two forms, because Mattermost has two
~nameis linkified client-side against the channel map of the team the message is read in (getChannelsNameMapInCurrentTeam), and slugs are unique per team only (channels_name_teamid_key UNIQUE (name, teamid)). A~namefrom another team therefore arrives as plain grey text. There is no team-qualified~syntax: the tokenizer charset[a-z0-9.\-_]has no:or/.The cross-team form is the channel URL, which is what MM's own "Copy Link" produces and what MM resolves in-app from any team by reading the team name out of the path.
So the list offers both, and the row says which it will insert:
~incidenten~incidentenhttps://host/ops/channels/ops-incidentenSame-team rows first. The live region spells the difference out, since the icon is not readable: "Ops incidenten in Ops Team, voegt een link in".
No Mattermost traffic per keystroke
Candidates come from
mm_targets_service, already cached per(user_id, account_id)for five minutes and already spanning every team, so after the first call a keystroke costs nothing upstream.Targetgainedchannel_nameandteam_name: the two values a~nameand a channel URL are built from, both already fetched. That is a deliberate whitelist, so it is the one thing worth a second look in review.Interaction with #313
Both mention-candidates routes keep the POST shape from #313;
kind: "user" | "channel"sits on the sharedMentionQuerybody model, so the typed query never reaches a query string.channel_idbecomes optional forkind=channel(a new message has no recipient yet) and yields a 400 forkind=user. Two things needed fixing to hold that line:~fetches were on GET with?q=, next to an@that #313 had already moved into a body. Verified after the change with network capture in the running app: every candidates request is a POST and no URL carries the typed text.waggle:recent-channelsjoinsSWEEP_PREFIXESinlocalTraces.ts. Without it the list of channels you link most survived logout on a shared machine. Registered by prefix, so it also covers the-v2key and future versions.Also in here
get_channelmaps 401 toMattermostTokenInvalidError. It was the only path where an expired token surfaced as 502 instead of 401, and the two tests covering it patched the failing call away.@'s empty state names the field it needs ("Kies eerst een kanaal bij Aan") instead of advising the user to keep typing, which could not help.~needs no recipient.nldd-menuhas the right ARIA but a fixed item content model,nldd-list's listbox mode renders its own input, and our query is typed at the caret inside CodeMirror.Checks
2002 pytest at 100% coverage, 1218 vitest, 120 e2e,
vue-tsc, build, all pre-commit hooks. Driven by hand in the preview against the mock: both row kinds, both insertions, the recents hoist, the announcements, and the POST bodies.Two findings the e2e caught that unit tests could not:
nldd-icon[name="link"]never matches in a browser, because Vue sets a property on an upgraded custom element while the vitest stub takes an attribute; and the trigger regex needed theiflag, since MM searches display names while slugs are lowercase.Not in scope
Waggle does not render
~channelin received messages (lib/mentions.tshandles@only), so a link you send shows as literal text here and correctly in Mattermost. Worth its own issue.Typing ~ in a composer offers the account's channels and inserts a reference to the picked one. Candidates come from the mm_targets cache, so a keystroke costs no Mattermost request. A ~name is only linkified against the team the message is read in, and slugs are unique per team only, so a channel in another team is inserted as its URL instead. The row shows which of the two it will be. The @ empty state now names the field it needs ("Kies eerst een kanaal bij Aan") rather than inviting the user to keep typing; ~ needs no recipient. Empty-state text moved to the parent so the visible row and the live region cannot drift. Closes #2748acd4426737bd9bde3b37bd9bde3b3f5b94bb4e2f5b94bb4e223cbaa6f2723cbaa6f27a4fa10cb8e