Repository navigation
feat(ui): link web addresses written without a scheme - #205
Conversation
Add findLinks and findLinkAt to stream_core, matching web links with or without a scheme against the IANA top-level domains, and link every web address in StreamMessageText through them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 39 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## ci/pana-sibling-paths #205 +/- ##
=========================================================
+ Coverage 71.46% 71.98% +0.51%
=========================================================
Files 215 216 +1
Lines 8884 9066 +182
=========================================================
+ Hits 6349 6526 +177
- Misses 2535 2540 +5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC
| /// Or directly, from the repository root: | ||
| /// dart run scripts/generate_top_level_domains.dart | ||
| /// | ||
| /// Run it when a link with a new top-level domain is not found, or |
There was a problem hiding this comment.
The generator looks solid (it validates the download, writes nothing on failure, and skips no-op writes). My concern is that nothing ever runs it. It isn't part of generate:all and isn't in CI, so "periodically" in practice means "when someone reports a link that doesn't work". That's the same staleness problem that got the old list removed in GetStream/stream-chat-flutter#2654.
Since the script already makes no change when the set is unchanged, a scheduled GitHub Action (monthly, say) could run melos run generate:tlds and open a PR whenever the file changes. Updates would cost nothing and stay reviewable. If that's out of scope for this PR, could we at least add a ticket or a line in the release checklist so it doesn't depend on someone remembering?
There was a problem hiding this comment.
Added in 66884fb. The update_top_level_domains workflow runs the generator on the 1st of every month and opens a chore(repo) PR with the bot's token, so its checks run. Since the script leaves the file alone when the set hasn't changed, a quiet month opens nothing. It can also be triggered by hand.
Run the generator on the first of every month and open a pull request when the list has changed, so it doesn't depend on someone remembering to. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC
Move stream_core_flutter and stream_core from the link finder PR's branch commit to its squash commit on main (GetStream/stream-core-flutter#205). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC
* test(e2e): add e2e_app, port native e2e suites, one app process per CI batch * chore(repo): ignore e2e mock server, Allure results and allurectl in any app * test(repo): link every e2e test to its Allure TestOps case * test(repo): unmount the e2e app before resetting the client, verify clearComposer * ci(repo): run PR e2e on API 36, use a preinstalled NDK, retry failed builds whole * test(repo): retype into a field that ignored the text * test(repo): keep the copied message id in the e2e app, bound the video step * test(repo): scroll until a message is on screen, stop when the emulator dies * ci(repo): give the e2e app's Gradle the sample app's memory settings * Bump timeout * test(repo): restart a dead emulator during the e2e run * test(repo): time-limit e2e runs so a dead emulator can't hang the job * fix(repo): look up timeout through sh so the lane runs on Linux * ci(repo): split the e2e coverage nightly into 4 batches * ci(repo): give the e2e runners memory headroom * test(repo): retry the tap that opens the image gallery * fix(ui): link web addresses written without a scheme (FLU-1094) A message's text now links an address such as youtube.com or getstream.io/chat when it ends in a known top-level domain, and opens it over https. File names such as config.json, email addresses and link labels are left as they are. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC * test(repo): assert the token date and signature policy (#3123) * test(repo): assert the token date and signature policy (FLU-1083) The SDK refreshes the token on code 40 only. A token used before its issue date (42) is reconnected as-is until the server's clock catches up, and a bad signature (43) is not retried with a new token. The four tests now assert that instead of a refresh. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(repo): tighten the token date and signature tests (FLU-1083) Keep the invalid window open past the refresh so the refreshed token is always rejected, count token requests through an env assertion, and fix the step and file comments to match what the tests assert. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(repo): drop the token signature after login test (FLU-1083) A signing secret doesn't go bad mid-session, and the test only timed the SDK's reconnect backoff. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(repo): keep the login error across the loading screen (FLU-1083) The chooser page is replaced while connecting, so an error it stored itself was gone by the time the login failed. The auth state now carries it, and the rebuilt page shows it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(ui): keep the footer of a sent message before a failed one (#3122) * fix(ui): keep the footer of a sent message before a failed one (FLU-1090) A message that failed now starts its own group, so the sent message before it keeps its timestamp and delivery status. Consecutive failed messages still group together. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(ui): describe every failed message in the changelog (FLU-1090) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(ui): link web addresses through the core link finder (FLU-1094) Pin stream_core_flutter and stream_core to the stream-core-flutter commit that adds the link finder, and drop the local WebUrlSyntax: StreamMessageText from stream_core_flutter now links web addresses itself. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC * chore(repo): sort the stream_core override in the example pubspecs Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC * chore(repo): pin stream-core-flutter to the merged link finder Move stream_core_flutter and stream_core from the link finder PR's branch commit to its squash commit on main (GetStream/stream-core-flutter#205). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC --------- Co-authored-by: Alexey Alter-Pesotskiy <alex@testableapple.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
StreamMessageTextnow links web addresses written without a scheme, such asyoutube.com, as the iOS, Android and React Native chat SDKs already do (FLU-1094).What changed
stream_corefindLinks,findLinkAtandLinkMatch. They find web links with or without a scheme. An address without one counts only when it ends in an IANA top-level domain, soconfig.jsonstays plain text.melos run generate:tlds, a part oflink_finder.dartthat only the finder uses. Theupdate_top_level_domainsworkflow regenerates it monthly and opens a PR when it changes.youtube.comapart fromconfig.json, and the monthly refresh deals with it going stale.stream_corerather thanstream_core_flutterso that v11'sstream_chatandstream_chat_flutter_core, which depend only onstream_core, can use the same matcher.stream_core_flutter(now depends onstream_core)StreamMessageTextlinks every web address throughfindLinkAt, using oneLinkSyntaxsubclass.www.ones included, open overhttps://, the scheme the backend's preview scraper and stream-chat-js's composer already use. Before,www.opened overhttp://.createNode, so inline, reference, nested and escaped labels all behave.inlineSyntaxesstill take priority. Email andftp://links still come from markdown's autolinks.How it was verified
Tests: 66 finder tests in
stream_coreand 18 widget tests that tap the rendered link. Every guard in the finder (21) and in the syntax (6) was broken one at a time and caught by a test.Analyze, format and
check:barrelsare clean. The fullstream_core_fluttersuite fails only its 57 goldens locally, the same count as onmain.packages/stream_core/benchmark/link_finder_benchmark.dart(benchmark_harness, AOT):)or.Every case scales linearly with length, including inputs built to slow a link scanner down.
Markdown parse with the syntax compared to without (AOT): messages with links parse faster, because it takes over from the GFM autolinker. Link-free text costs +5 µs on a typical message and +53 µs per 1k characters. A widget pump comparison shows no measurable difference for messages with links.
🤖 Generated with Claude Code
https://claude.ai/code/session_012vTgFwqhUc2wKCWrw635yC