mysampark_chat 1.3.0
mysampark_chat: ^1.3.0 copied to clipboard
Official MySampark Chat Widget for Flutter. Add AI-powered live chat with human agent takeover, typing indicators, lead forms, and real-time WebSocket support.
1.3.0 #
- Major Feature: Global SDK Background Initialization
Added
MySamparkChat.initializeBackground()to support standalone global chat tracking, allowing unread badges and popup notifications across any screen. - Major Feature: In-App Notification Banners
New incoming messages trigger a branded, globally visible top banner when the chat screen is closed, simply by passing
navigatorKeyto the SDK initialization. - Major Feature: "Now Live" Presence Tracking
Added
MySamparkChatObserver. Dropping this into yourMaterialAppautomatically syncs the visitor's currently viewed screen with the "Now Live" Admin Dashboard (via a dedicatedvisitWebSocket channel), identical to the web widget behavior. - Fixed: Fixed a critical routing bug where tapping the new notification banner would open the Chat Screen with default properties instead of preserving the custom
baseUrlor user identity passed during initialization.
1.3.0 #
- A human agent who takes a chat over is now named and shown, instead of staying anonymous behind the business's name. Their photo and name replace the logo and title in the chat header for as long as they hold the chat, sign each of their own bubbles, and appear on the typing indicator as "Hemangi is typing" rather than bare dots. The header goes back to the business the moment they hand it over. An agent with no profile picture gets their initial in a coloured circle, never a broken image.
- Added
ChatAgent—id,name,avatarUrland aninitialfor the placeholder. - Added
ChatMessage.agent, set on agent-role messages the backend attributed. Null for visitor and assistant messages, and for agent messages from before this existed, which keep the old "Support Agent" label. - Added
ChatService.activeAgentStreamandChatService.activeAgent— who has the conversation right now, null for the AI. Use it to put the person in your own header if you don't useMySamparkChatScreen. An AI reply counts as null, which is also how a takeover that quietly expired on the server clears itself on the client. - Added
ChatService.typingAgentStream— who the current typing indicator belongs to, null when it is the AI thinking. Emitted just beforetypingStreamon each start, so a listener watching both always has the right name by the time the dots appear. - The AI's waiting state now says what it is waiting for. Three dots covered everything from a one-second answer to a minute-long booking turn, which reads as a hung app; the bubble now carries a line of text, shimmer bars roughly the size of the answer so the thread doesn't jump when it lands, and copy that admits it is slow once the wait passes 7 and 18 seconds. A backend that pushes a
statuswith its typing tick ("Checking available slots…") replaces that copy with the real thing. - Added
ChatService.assistantStatusStream— what the AI is doing right now, or null. Only emitted by backends that send it; older ones never do and the UI falls back to its own copy. - The send button no longer turns into a spinner while waiting. It dims instead: a second spinner on the control the visitor just pressed said "stuck", not "coming", and the thread already shows the wait where the answer will appear.
- Added
MessagePayload.quickReplies, rendered as tappable chips under the newest reply. A booking turn used to read back twenty numbered time slots and ask the visitor to "reply with the number"; now they tap one. Chips send their label verbatim, go through the same pre-chat survey gate as typing, and clear as soon as the conversation moves on. Backends that don't sendquick_repliesare unaffected. - Added
ChatService.reportTyping(draft)andChatService.stopTyping().MySamparkChatScreenalready calls them, so the agent sees the visitor composing and the first 500 characters of what they are writing, and can start on the answer before the question arrives. Safe to call per keystroke: ticks are throttled to one every two seconds, with a trailing one shortly after typing stops. typingStreamkeeps itsStream<bool>shape, so nothing existing breaks — the agent's identity arrives on the new streams beside it.- Requires a backend with agent attribution (
active_agenton/history,agenton agent messages,agent_joined/agent_left). Older backends send none of it and the chat behaves exactly as in 1.2.4. - Fixed: product and offer cards disappeared from every reply as soon as the chat was reopened.
/historyreturns the cards beside the stored reply rather than inside it, and only the reply was being read — so the thread came back as plain text. Both places are read now, the reply's own cards still winning when it has them. - Fixed: message times on a reloaded chat were shown in UTC. The backend sends UTC, the package now converts to the device's zone — an IST visitor was seeing every restored bubble 5.5 hours early, while live ones (built from
DateTime.now()) were correct, so the same thread disagreed with itself. - Fixed: scrolling up to the top of a long chat loaded nothing, while sitting at the bottom loaded page after page. The thread is a reversed list, so "near offset 0" is the newest message, not the oldest; older pages are now fetched when the visitor actually reaches the top.
- Fixed: a bare
www.example.comlink in a reply opened nothing — the scheme was being added by replacing the URL withhttps://instead of prefixing it, which threw the host away. - Fixed:
wsUrlno longer defaults to production. Left unset it followsbaseUrl, so a staging or self-hosted backend opens its own socket instead of production's; the backend's advertisedws_urlis preferred when it is on that same host, matching the web widget. Production resolves to exactly the URL that used to be hardcoded, so nothing changes for apps on the defaultbaseUrl. Pass an empty string to turn the live connection off. - Fixed: a
TapGestureRecognizerwas created for every link on every rebuild and never disposed. They are cached per URL now and disposed with the screen. - Fixed: submitting the lead form or pre-chat survey, or picking an image, could call
setState()after the chat had been closed. - Fixed: when a reply arrived with no readable text, the bubble printed the raw reply object (
{type: text, message: …}). It falls back to the backend's ownreply_textinstead. - Prices now group thousands (
₹97,500, not₹97500), the way the web widget already did, using a fixed pattern so the same card reads the same on every device. - Added
WidgetConfig.wsUrlandChatService.defaultWsUrlFor(baseUrl).
1.2.4 #
- Fix missing confirm escalation buttons due to WebSocket fallback logic.
1.2.3 #
- Added
userPhone,userTagsanduserPropertiestoMySamparkChatScreen,MySamparkChatBubbleandChatService, sent asuser_phone,user_tagsanduser_properties— the same visitor details the web widget now accepts throughwindow.MySamparkIdentify/MySamparkChat.identify(). Tags add to the visitor's existing tags; properties merge key by key, and anullvalue removes that key. Requires a backend with thevisitor_tags/visitor_propertiesmigration; older backends ignore the fields. - Added
ChatService.resetVisitor(widgetKey)— call it on logout (the web widget'sMySamparkChat.reset()). It clears the stored session, conversations and survey flag for that widget, closes every open connection for it, and makes an open chat screen or bubble start over as a new anonymous visitor. Before this, the session ID lived for the life of the install, so a bubble left mounted after logout kept the previous user's socket open and could show their agent's replies as notifications to whoever used the device next. The backend never handed one user's identified conversation to another; this closes the client side. MySamparkChatScreenandMySamparkChatBubblenow restart when the host app passes a differentuserId/userHash(login, logout or a user switch while they're mounted). Previously both kept the identity they were first built with until disposed.- The pre-chat survey is skipped when the app already passed a name, email or phone, matching the web widget. It used to be skipped only for a verified login or details the backend already had.
- A malformed
userEmailis now dropped (with a debug message) instead of being sent. The backend rejects the whole request on an invalid email, which stopped the chat from loading at all. - Text and icons on the brand colors (headers, visitor bubbles, buttons, launcher, offer badges) now switch to dark on light colors instead of always being white — white stays while it has at least 3:1 contrast, the same rule as the web widget. A light green or orange widget no longer has white-on-light text.
- Fixed: the pre-chat survey avatar drew a white icon on a 10% tint of the action color, so it was nearly invisible. It now uses the action color.
- Deprecated
ChatService.generateHash. The hash must be computed on your own backend; running it in the app means shipping the widget secret inside the APK/IPA. It still works and will be removed in 2.0.0.
1.2.2 #
- Added a "Select" button to product cards, shown when the backend sends
select_products: trueon a reply (only for widgets that have a booking-style action/sequence which actually uses the id it sends back — a plain Q&A bot's cards are unaffected). Tapping it sends [ProductCard.id] back asselected_product_idon the next message, so the backend settles which item the conversation is about exactly, instead of guessing it back from the product name as plain text. Distinct from the existing "View product" button, which still opens the product itself (in-app viaonProductTap, or the browser). ChatService.sendMessage()takes an optionalselectedProductIdparameter, sent asselected_product_id.- Added
MessagePayload.selectProducts, parsed fromselect_products. Requires a backend that sends it; older backends simply leave it false and no Select button renders.
1.2.1 #
- Added
ProductCard.features— vendor/sku/barcode/attributes as synced from Shopify/WooCommerce/the push API. Null for a manually-entered product or one with no synced attributes. Requires a backend that sendsfeaturesonproducts_referencedcards; older backends simply leave it null. - Added
ProductCard.raw— the card exactly as the backend sent it, so a host app can read a field this class doesn't model yet without waiting on a package update.
1.2.0 #
- Added
onProductTaptoMySamparkChatScreenandMySamparkChatBubble— a product card can now open inside the host app instead of throwing the visitor out to a browser. Returntruefrom the callback and the package does nothing further; returnfalse, or leave it unset, and the card's URL opens exactly as before, so an app only writes code for the products it actually stocks. TheBuildContextpassed in is the chat's own, which is what pops the bubble's bottom sheet — the host app's context can't, and pushing from it leaves the product page hidden behind the sheet. - Added
ProductCard.externalIdandProductCard.source— the merchant's own id for the product and which catalogue it came from (api,shopify,woocommerce).ProductCard.idhas always been MySampark's internal id, which a host app can't match against its own catalogue;externalIdis the one to look up. Null for products the merchant typed in by hand, and for cards restored from conversations that predate this release, both of which fall back to the browser on their own. - A product card with no URL is now tappable when
onProductTapis set, so an app-only listing can still open — previously the whole card and its button were hidden without a link. - Requires a backend that sends
external_product_idonproducts_referenced. Older backends simply leave it null. - Fixed: every non-INR price rendered with a bare
$.ProductCard.formattedPricehad one branch —₹for INR,$for everything else — so a Singapore merchant's SGD 48 card read as US dollars, and so did AUD, CAD, HKD and NZD. Prices now carry their regional prefix (S$,A$,C$,NZ$,HK$,NT$,R$,MX$), the major non-dollar symbols are listed too, and an unlisted currency falls back to its ISO code (MYR 48) rather than guessing.intl'sNumberFormat.simpleCurrencyis not used for this — its own table maps SGD, AUD, CAD, HKD and NZD all to a bare$. - Fixed: the header ignored the merchant's support hours.
support_onlinehas always arrived oninit()and been parsed intoWidgetConfig.supportOnline, but nothing read it — the status dot reflected only whether the WebSocket was connected. A merchant who marked Sundays closed saw "Offline" on their own website widget and "Online" here. The status now combines both, exactly as the web widget does. - Fixed: "talk to a human" buttons appeared outside support hours. A
confirm_escalationreply rendered its Yes/No buttons whenever it was on screen, including when replayed from history after closing time — offering the visitor a handoff nobody was there to take. They now render only while support is online.
1.1.8 #
- Fixed: product cards never rendered in chat. The backend sends
products_referencedalongside aproduct_list/product_detailreply, and the reply text is only an introduction — "Here are some products currently available: A, B, C." — so the app showed that sentence and nothing else, with no picture, price or link. Cards now render under the reply as a horizontal carousel, matching the web widget. - Fixed: offer cards never rendered in chat. Same cause, worse symptom: an
offer_listreply's text is "Here are our current offers:" and the cards were dropped, leaving a bubble that ended on a colon. In-chat offers now use the same tile as the Offers tab. (The Offers tab itself was unaffected — it loads from/offersseparately.) - Fixed: merchant-defined Track Order fields were ignored.
order_tracking_extra_fieldsarrives oninit()and the web widget renders one input per entry, sending them back underparams. This package rendered none and sent none, so a merchant whose order API requires an extra field (a postcode, a phone number) got an incomplete request and the visitor got "we couldn't find that order" for a correctly filled form. Shopify-backed order tracking was never affected — that path takes no extra parameters. - Added
ProductCardandOrderTrackingFieldmodels, and exportedMessagePayload,MessageHighlight,MessageSectionandMessageCta— all four were already reachable throughChatMessage.payloadwithout being nameable. ChatService.trackOrder()takes an optionalparamsmap.
1.1.7 #
- pub.dev score maintenance, no behavior changes:
- Added dartdoc comments across the public API (
MySamparkChatBubble,MySamparkChatScreen,ChatService, and all exported models) for the pub.dev "Provide documentation" score. - Bumped
intlto^0.20.3andflutter_lintsto^6.0.0(up-to-date dependencies score). - Fixed all analyzer lints flagged by
flutter_lints6.0.0 (withOpacity→withValues,unnecessary_library_name,curly_braces_in_flow_control_structures, string concatenation → interpolation) and applieddart format.
- Added dartdoc comments across the public API (
1.1.6 #
- Fixed:
MySamparkChatBubble's in-app notification banner could pop up on top of aMySamparkChatScreenthe visitor already had open, if that screen was opened some other way than the bubble's own_openChat()(e.g. a host app's own "Support" tab doing its ownNavigator.push) — the bubble only tracked whether it had opened the chat, with no way to know a different screen instance was already showing that exact message live. - Added
MySamparkChatScreen.isAnyScreenOpen— tracks whether any instance is currently mounted, regardless of how it was opened. The bubble now checks this alongside its own state before showing the banner or incrementing the unread badge.
1.1.5 #
- Fixed: conversation starters now render on the Home tab (a card-style list above Offers/Chat/Track, matching the web widget), instead of only as chips inside an already-empty chat view. Tapping one switches to the Chat tab and sends it immediately, going through the same pre-chat survey gate as a normal message.
- Removed the old chat-view starter chips row — the web widget never had this either; starters live on Home only.
1.1.4 #
- Guest-to-login merge: a visitor's session_id is no longer cached per-identity, so a guest who logs in mid-session keeps the same session_id and the backend now merges their guest conversation into their identified thread instead of starting a disconnected second one.
- Backend:
init(),message(), andtrackOrder()all guard this merge against a shared/reused device — a session_id match is only adopted if it isn't already a different identity's conversation, so logging out and having someone else use the same device (or a plain anonymous visitor after that) can never inherit the previous person's thread, name, or email.submitLead()got the equivalent guard against overwriting an already-identified visitor's details.
1.1.3 #
- Fixed:
is_identifiedfrom the backend now also reflects this request's own HMAC verification, not just an existing conversation's persisted flag — a first-time logged-in visitor (verified but with no conversation yet, since those are only created lazily) was incorrectly treated as anonymous, so they'd see the pre-chat survey and the order-tracking email field even though they were already identified. - Fixed:
trackOrder()now sendssession_idand the visitor's identity params (user_id/user_hash/user_name/user_email) — required by the backend's/track-orderfallback path for visitors who reach "Track your order" before ever sending a chat message. Without this, that path could never be reached (missingsession_idfailed validation first), and even once reached, an identified visitor's freshly-created conversation had no email attached, causing a dead-end "Email is required" error with no visible field to fix it.
1.1.2 #
MySamparkChatScreennow mirrors the web widget's close behaviour: if a conversation was started, its WebSocket stays open for a 5-minute grace period after the screen closes instead of disconnecting immediately (ChatService.scheduleGraceClose()). Screens with no started conversation still disconnect right away.- Redesigned the Offers card: tinted header, a highlight badge, and the description's meta facts (min order, usage limit, etc.) now render as scannable pill badges instead of stacked plain-text lines.
OfferItemgained abadgefield (e.g. "10% OFF", "FREE SHIPPING") — set by the backend from the actual discount data, not guessed from free text on the client.
1.1.1 #
ChatServiceWebSocket connection now matches the web widget's resilience pattern: capped auto-reconnect (max 5 attempts, exponential backoff up to 16s) instead of giving up permanently ononDone/onError.- Added a 25s ping keepalive while connected.
- Added a manual-close flag so an intentional
disconnectWebSocket()/dispose()doesn't trigger a reconnect attempt. - Fixes
MySamparkChatBubble's in-app notification banner going silent after the first WebSocket drop (app backgrounded, network blip, etc).
1.1.0 #
- Home tab —
MySamparkChatScreennow opens on a branded greeting screen with Offers / Chat / Track Order cards, matching the web widget's layout. - Offers card — fetches live deals/discount codes from
GET /api/chatbot/offers, tap-to-copy code. - Order tracking card — order lookup form (
POST /api/chatbot/track-order) showing status, carrier, and tracking link; skips the email prompt for HMAC-identified users. WidgetConfiggainedhomeGreetingTitle,homeGreetingSubtitle,showOffers,showOrderTracking, and now correctly readsaction_colorfrom the backend instead of always falling back toprimary_color.- New exported models:
OfferItem,OrderTrackResult. - Home / Chat bottom navigation; hidden while the pre-chat survey or an Offers/Order-Track sheet is open so it can't be used to bypass a non-skippable survey.
1.0.1 #
- Simplified documentation for cleaner Flutter integration.
1.0.0 #
- Initial release
- Full-screen chat screen (MySamparkChatScreen)
- Floating bubble launcher (MySamparkChatBubble)
- AI-powered replies with human agent takeover
- Real-time WebSocket support for live agent messages
- Pre-chat survey gate (admin controlled)
- In-chat lead form (name/email/phone)
- Conversation starters / quick-reply chips
- Typing indicator with 3-dot bounce animation
- Chat history with scroll-up pagination
- Online/Offline status display
- Logo, colors, and all settings synced from admin dashboard
- HMAC-SHA256 identity verification for logged-in users
- Session persistence via SharedPreferences