gvl_comments 1.2.0
gvl_comments: ^1.2.0 copied to clipboard
Drop-in comments widget and comment section for Flutter, with threaded replies, reactions and moderation. Managed backend included, no Firebase setup, just an install key.
Changelog #
All notable changes to the GoodVibesLab Comments Client (Flutter) package will be documented here.
1.2.0 #
Security #
- Tokens are now bound to the user they were issued for. Switching
userwithout callinginvalidateToken()used to reuse the previous user's token for up to an hour, so posts, reactions, reports and profile updates were attributed to the previous user. The SDK now requests a new token whenever the user changes. identify()only sends a profile with a token issued for that same user, so it can no longer overwrite another user's name or avatar.- Fixed a ReDoS in link detection. A single crafted 5000-character comment could block the UI thread for about 800 ms on every rebuild, for every viewer of the thread. The pattern is now bounded, with a worst case under 1 ms.
TopCommentdecodes avatars at display size instead of full resolution. Avatar URLs are user-supplied.
Performance #
- New
CommentsKit.listPage()returnsCommentsPage{items, nextCursor, hasMore}.lastNextCursorandlastHasMoreare deprecated: they were shared by every list and could be overwritten by concurrent loads. commentCount()lookups issued in the same frame, e.g. by everyCommentCountin a feed, are merged into onethreads/inforequest.prefetchThreads()splits large requests into chunks of 50, the API limit, where it used to fail withtoo_many_threads.- Moderation settings are cached as the docs described, instead of being refetched on every
CommentsListmount. identify()skips a profile that has already been synced.- Thread-key validation regexes are compiled once. Link spans are only rebuilt when the text changes.
Added #
- Quota-aware posting: when a project has used up its monthly comment quota,
CommentsKit.post()throwsCommentsQuotaExceededException.CommentsListthen shows "Comments are paused for now" (localized) with no Retry, and the user's text stays in the composer. Reading, reactions and reports keep working. - Comment actions menu (⋯): "Copy" on every comment, and "Report" only on other people's comments when a user is signed in. The menu now sits next to the bubble and no longer covers the text.
- Report confirmation: a dialog is shown before a comment is reported, so a stray tap no longer flags someone.
- An optimistic comment shows "Sending…" in place of the timestamp.
- 6 new localized strings (en/fr/es/de/pt).
Changed #
- The send button is disabled while the input is empty.
composerMaxLinesfrom the theme is honored. The composer used to grow without limit.- Relative timestamps refresh every minute ("Just now" no longer sticks).
- The reaction picker is now a dialog route: Android back, a pop, or predictive back closes it. It used to stay on top of the next screen. The route is named
reactionPickerRouteName(gvl_comments/reaction_picker) soRouteObservers such as analytics screen tracking can ignore it, and it keeps the caller's localTheme. - Links in comments use the theme's primary color.
- A failed reaction is rolled back with a message, instead of keeping a reaction the server never recorded.
- Replying no longer scrolls to the top of the list. Only root comments bring the list back to their position.
- The "Reply" and reactions targets are easier to tap. Accessibility: the reaction counter reads "N reactions", and the sign-in composer is a button.
CommentsListuseslistPage().
Fixed #
- Moderated and reported comments no longer show Reply, the like button or their reaction counts: only the placeholder is left, as with the ⋯ menu.
identify()called before the first token (the defaultCommentsListflow) left an internal in-flight marker stuck. The profile was then never synced for the rest of the session.TopCommentandCommentCountdiscard responses for a previousthreadKey(recycled feed items).TopCommentnow inherits theGvlCommentsThemeDataextension and theGvlCommentsThemeancestor.- Links: trailing punctuation is excluded (
example.com.), full emails are detected (joe@mail.co.uk), and domains starting with "http" (httpbin.org) can be opened. dispose()clears the singleton. Callinginitialize()again closes the previous HTTP client.CommentsList: a slow response for a previous thread or user could overwrite the current one. The reply target and draft were also kept after a thread change, so a reply could be posted in the wrong thread.CommentsList: a reply posted in a collapsed thread (more than 2 replies) was hidden behind "See N more replies". The thread now expands automatically.CommentsList: a pull-to-refresh during a send could drop the new comment or show it twice. The comment is now listed exactly once, including when the refresh response arrives after the post is confirmed.CommentsList: the send button stayed disabled after switching threads during a slow send.CommentsList: threads are only regrouped when the comment list changes, not on every rebuild.CommentsList: an auth error without a message showed the empty state "No comments yet" instead of the error view. In debug, the code pill now shows the code (e.g.invalid_binding).- Reply, reaction and report are no longer possible on a comment that hasn't been confirmed yet (local id unknown to the server). The confirmed comment no longer fades in a second time.
- The "Load previous comments" button is hidden when the API returns no cursor (it did nothing).
- Fallback date (comments older than 7 days): day and year were swapped (
05-03-2026instead of2026-03-05), and the date was computed in UTC instead of local time. - The "Powered by" link didn't open on Android 11+ (
canLaunchUrlwithout<queries>).
Package #
package_info_plus10.x is accepted (>=9.0.0 <11.0.0). Apps on Flutter 3.38+ can upgrade it, and older apps keep resolving 9.x.- The declared minimum is now Flutter 3.19, the real floor already imposed by
package_info_plus9. - README: "real-time posting" was misleading. Posting is instant (optimistic UI), but other users' comments appear on refresh. The wording is fixed.
Upgrade notes #
lastNextCursor/lastHasMoreare deprecated. UselistPage().showCommentReactionPickernow pushes a route: itscontextneeds aNavigatorancestor.- With a custom
commentItemBuilder, the "⋯" actions button is always shown (Copy is always available). It used to appear only when user reports were enabled. GvlCommentsL10nhas 6 new strings. This only matters if you subclass it.
1.1.0 #
Security #
- Comment body validation is now enforced in
CommentsKit.post()— the body is trimmed and rejected (ArgumentError) when empty or longer thanmaxBodyLength(5000), instead of being sent as-is. - User-provided links now require confirmation before opening. Tapping a URL/email in a comment shows a dialog with the target and only opens
http,https, andmailtoschemes (phishing mitigation). - Avatar URLs are validated — only
http(s)images are loaded, decoded size is bounded (cacheWidth), and loading failures fall back to initials. - Server error bodies are no longer leaked —
HttpExceptionand internal error strings are truncated and single-lined; raw response bodies never reach logs or error UIs. - HTTPS is enforced for
GVL_API_BASE(plainhttpallowed only for localhost). - Debug logging hardened — stray
debugPrintcalls that could surface server payloads in release now route throughCommentsLogger. - Identity no longer replayed across users —
invalidateToken()now clears any queuedidentifypayload so a previous user's profile can't be re-sent under a new user's token.
Added #
- Empty state for threads with no comments (icon + localized message), plus an
emptyBuilderonCommentsListto customize it. - Pull-to-refresh (
RefreshIndicator) on the comment list. - Loading skeleton replaces the bare full-screen spinner on first load.
- Composer character limit — the input enforces
maxBodyLengthwith a counter shown as you approach it. - Exported
CommentReactionsBar/showCommentReactionPickerfor use in custom builders.
Changed #
- Failed sends no longer lose the user's text — on error the composer is restored (text + reply target) and a retry
SnackBaris shown, instead of silently dropping the comment. - Pagination and refresh failures are surfaced — inline "Retry" on the load-more row and a
SnackBaron refresh failure, rather than a silently stopped spinner. - Meta row now wraps (timestamp · reply · reactions) instead of overflowing with long names or large text scales.
- RTL support — reply indentation, the report menu, and branding use directional insets/alignment.
- Accessibility — interactive meta actions (reply, react, "see more replies", branding) are exposed as semantic buttons with larger tap targets.
- List items keyed by comment id to avoid state recycling across optimistic inserts.
- Moderation/reported placeholders in
TopCommentand the branding label are now localized.
Fixed #
- Corrected the
CommentCountbuilder signature in the README ((context, count, refresh)).
Note #
- The API proxy no longer trusts a client-supplied
rolewhen issuing SDK tokens — SDK tokens are always end-user tokens (moderator/admin access is dashboard-only). No SDK API change.
1.0.1 #
Added #
onCommentPostedcallback onCommentsList— called with the confirmedCommentModelafter a comment is successfully posted to the server. Useful for analytics, counters, or external state updates.
1.0.0 #
First stable release. The public API is now frozen under semantic versioning.
Breaking changes #
CommentModel.statusis now aCommentStatusenum. Usecomment.commentStatusfor type-safe comparisons. The legacycomment.statusgetter still returns aStringfor backward compatibility.GvlCommentsStrings.fr()factory removed — use the l10n system (GvlCommentsL10n) for localization instead.
Added #
TopCommentwidget — displays the single most-engaged comment for a thread. Supports full builder customization (builder,loadingBuilder,emptyBuilder,errorBuilder).CommentCountwidget — displays the approved comment count for a thread. Reads from cache whenprefetchThreadswas called (zero latency).CommentsKit.prefetchThreads()— batch-fetches thread info (count + top comment) for multiple threads. Ideal for feed/list performance.CommentsKit.topComment()/CommentsKit.commentCount()— individual API accessors with cache support.ThreadInfomodel — holds prefetched count + top comment data.CommentStatusenum (pending,approved,rejected) — replaces string constants.Reactionenum (like,love,laugh,wow,sad,angry) withid,emoji, andemojiFor()helper.LoadMoreButtonBuilderparameter onCommentsList— fully customize the pagination button.- Threaded replies (depth 2) with
parentId,replyToCommentId,replyToUserIdonCommentModel. - Localized reaction labels — reaction picker labels (
Like,Love, etc.) now go throughGvlCommentsL10n. - 4 new locales — German (de), Spanish (es), French (fr), Portuguese (pt) alongside English.
- HTTP request timeout (15 s) on all API calls — prevents indefinite hangs.
- Body validation constants —
CommentsKit.minBodyLength(1) andCommentsKit.maxBodyLength(5000) exposed publicly. topicsfield in pubspec.yaml for pub.dev discoverability..pubignoreto reduce published package size.
Deprecated #
CommentsClient(legacy API) — useCommentsKitinstead. Will be removed in 2.0.Comment,CommentsExternalUser,CommentsUserRole,CommentsApiException— use their modern counterparts.CommentModel.statusPending/statusApproved/statusRejectedstring constants — useCommentStatusenum.
Fixed #
- iOS podspec had a duplicate
Pod::Spec.newblock (now consolidated). - Android
build.gradleversion aligned to1.0.0. - Hardened user identification flow (from 0.9.7).
0.9.6 #
Security #
- Added optional strict install key binding for enhanced application security:
- Android: app signing certificate SHA-256
- iOS: Team ID
- Clear and explicit authentication errors when strict binding is enabled and the app signature does not match.
- Improved protection against API key reuse across unrelated applications.
Added #
- New production-grade error UI for comments:
- Retry action
- Stable debug / support code
- Optional expandable technical details panel
- Improved token request headers including platform, package name, and app version.
Improved #
- SDK initialization and runtime configuration flow.
- Internal logging and diagnostics for easier production debugging.
- Android plugin configuration for wider consumer compatibility.
- More robust handling of authentication and moderation failures (non-fatal where possible).
Internal #
- Plugin scaffold cleanup and release-ready project structure.
- iOS podspec metadata updated.
- iOS privacy manifest (
PrivacyInfo.xcprivacy) included. - Web compatibility improved via conditional platform imports.
0.9.5 #
- Added comment reactions (like, love, etc.) with optimistic UI
- Improved visual polish and interaction feedback
- Better defaults for spacing, bubble layout, and composer
- Overall stability and UX improvements
0.9.4 #
- Example app now runs out-of-the-box with a built-in demo install key
0.9.3 #
- Added debug-only SDK logs (
gvl_comments:prefix) - Clear initialization log on startup
- Improved error diagnostics for missing or invalid install key
- Safe obfuscation of sensitive values in logs (API key, user id)
- Better resilience when authentication fails (non-fatal identify)
- Minor UI improvements
0.9.2 #
- Fix external link handling (URLs without scheme now open correctly)
- Improve composer layout and safe-area padding
- Better avatar rendering
- Minor visual refinements for comment bubbles and input
0.9.1 #
- Initial public release on pub.dev
- Added install key initialization + platform binding headers
- Included example app (Android + iOS)
0.9.0 — 2025-12-09 #
🚀 Initial Production Release #
- First production‑ready release of the GVL Comments Flutter SDK.
- Fully compatible with the GoodVibesLab Comments SaaS platform.
- Public API validated and stabilized for production apps.
✨ Features #
- Comment listing with cursor‑based pagination.
- Comment posting with hydrated server response.
- Real‑time profile sync: name + avatar resolved automatically from token.
- Moderation states:
pending,approved,rejectedis_flagged(AI or user report)- Helpers:
isReported,isModerated
- UI widgets:
GvlCommentsList(full thread viewer)- Built‑in composer with send button
- Customizable builders: avatar, item, composer, separators
- Server filtering:
- Deleted comments never returned
- AI‑rejected comments replaced by placeholder
🔐 Moderation & Reporting #
- User report flow with duplicate prevention.
- AI moderation states surfaced in UI.
- Auto‑hide, soft‑hide, and hard‑hide behaviors handled by backend + SDK helpers.
🎨 Theming & UI #
- Complete theming system:
GvlCommentsTheme+GvlCommentsThemeData. - Presets included:
defaults,neutral,compact,card,bubble. - Owner and others now aligned left (Facebook‑style layout).
- Avatar aligned top, not center.
- Long usernames ellipsized cleanly.
- New relative timestamps ("just now", "il y a 3 min").
- Clickable URLs + email detection.
- Spacing & vertical rhythm refinement for cleaner reading.
- Light fade/slide animation on comment appear.
- Optimistic UI state: pending comments appear with opacity until server confirms.
👤 Avatars #
- Default avatar logic introduced:
- If
avatarUrl→ loadImage.network - On failure → fallback initial
- If avatarBuilder provided → use custom implementation
- If
🧰 Internal Improvements #
- Stronger JSON validation & safer parsing.
- Unified contract with React SDK.
- Cleaner error handling.
- Debug logs improved & standardized.
- Token now exposes plan to allow conditional branding.
🏷️ Branding #
- Free‑tier apps automatically display
"Comments powered by GVL Cloud"
with tappable logo and external link.
Future releases will follow semantic versioning (MAJOR.MINOR.PATCH).
