one_viewr_flutter 0.2.0
one_viewr_flutter: ^0.2.0 copied to clipboard
Client Flutter embarquable pour Oneviewr CRM — identify/track/registerPushToken et source d'acquisition via une clé publique non secrète. Port direct de @oneviewr/js.
0.2.0 #
- Vidéo YouTube dans les messages in-app (balisage « version 3 » de
l'éditeur Oneviewr). Le bloc Vidéo accepte un lien YouTube : le message
montre la vignette de la vidéo ; un toucher l'ouvre hors de l'application
(l'app YouTube si elle est installée, sinon le navigateur), compte une fois
comme clic (
CLICKED) et laisse le message affiché.- Le message ne porte que l'identifiant de la vidéo
(
data-oneviewr-youtube, 11 caractères), revérifié côté page puis côté Dart ; l'adresse est construite par le plugin, jamais lue dans le message. - Le plugin annonce
sdkCapabilities.compilerVersion: 3: le serveur ne remet un message à vidéo YouTube qu'aux applications qui l'annoncent. showInAppMessage(onVideoOpened:): appelé à la première ouverture d'une vidéo pendant l'affichage.- Version mineure : une contrainte
^0.1.xne la prend pas ; passer àone_viewr_flutter: ^0.2.0. Aucun autre changement d'API.
- Le message ne porte que l'identifiant de la vidéo
(
0.1.2 #
- Outils de développement pour l'attribution et les liens profonds.
OneViewr.debugSimulateInstall(playReferrer:, linkCode:, clickedAt:)rejoue un premier lancement sans magasin ni vrai clic : le referrer Google Play simulé passe par le même lecteur que le vrai (UTM,gclid, publicité Meta,ov_link_code), le lien d'acquisition est résolu par le vrai serveur etdeferredDeepLinkle remet ensuite, une fois. Android, iOS et simulateurs. La source d'acquisition de l'appareil est remplacée durablement ; l'identité connectée ne bouge pas.OneViewr.debugAttributionState()montre ce que le paquet a retenu : premier et dernier contact, premier contact envoyé ou non, lien différé (code, destination résolue, déjà remis, échecs).toString()en JSON indenté, pour un journal.- Les deux lèvent
StateErrordans une build--release. - README : section « Tester sans magasin », avec les commandes
adbetxcrun simctlqui font ouvrir un lien par le système.
0.1.1 #
- Sondages, carrousels et coupons des messages in-app réparés. Depuis la
0.1.0, toucher une réponse de sondage, une flèche de carrousel, « Copier »
ou « Envoyer » ne faisait rien : le script lisait l'action en retirant
12 caractères, la longueur de l'ancien préfixe
neero-inapp:, au lieu des 15 deoneviewr-inapp:(« p:choice » au lieu de « choice »). Le préfixe est maintenant déclaré une seule fois (inAppLinkScheme), partagé avec l'interception des liens, et l'action est lue d'après sa longueur. Le bouton d'action et la croix n'étaient pas touchés.
0.1.0 #
-
Renommage Neero → Oneviewr du balisage in-app : le paquet lit désormais
oneviewr-inapp:*,data-oneviewr-*et les canauxOneviewrInAppHeight/OneviewrInAppAction, comme le serveur (migration V329). Rupture : une application qui embarque une version antérieure du paquet ne comprend plus les messages in-app (liens et sondages inertes) — elle doit être mise à jour. -
Rapports d'installation Apple : au premier lancement sur iOS, le paquet enregistre l'installation auprès de SKAdNetwork (valeur de conversion 0). Sans cet appel, Apple n'envoie aucun rapport d'installation pour une publicité Meta, Google, TikTok… — ni à la régie, ni la copie qu'Oneviewr reçoit pour compter les installations iOS par régie et par campagne. Apple répercute cet appel dans AdAttributionKit : les rapports des deux cadres sont couverts, sans appeler AdAttributionKit ni le lier.
- Une fois par installation, en arrière-plan (
bootne l'attend pas) ; un échec est retenté aux lancements suivants, trois fois au plus. Le marqueur survit àreset(). Rien avecattribution: false, ni sur Android ou le web. Aucun consentement ATT requis. boot(appleAdAttribution: false)laisse un autre SDK (Meta, Firebase, AppsFlyer…) gérer les valeurs de conversion.- À faire dans l'application pour qu'Oneviewr reçoive les rapports : clés
Info.plistNSAdvertisingAttributionReportEndpoint(copies SKAdNetwork) etAdAttributionKit›AttributionCopyEndpoint(copies AdAttributionKit), toutes deux =https://onvw.link(+OptInForReengagementPostbackCopies, facultatif). Voir la section « Rapports d'installation Apple » du README. - Nouveau code natif iOS (
registerAdAttribution) : l'application hôte doit être reconstruite. FrameworkStoreKitajouté.
- Une fois par installation, en arrière-plan (
-
birthDaterejoint enfin les parcours d'anniversaire. La date partait dans l'attributbirth_date, que rien ne lit : l'import CSV, la fiche client et les parcours d'anniversaire lisentbirthday. Elle part désormais dans le champbirthdaydeidentify, au formatAAAA-MM-JJ(une date ISO complète est acceptée, seule la date telle qu'écrite est gardée). Une date mal formée lèveArgumentErroravant tout appel réseau, au lieu de faire échouer tout l'identifycôté serveur. Les contacts déjà créés gardent leur ancien attributbirth_date: un nouvelidentifyremplitbirthday. -
Positions :
trackLocation,trackLocations— transmission de positions fournies par l'application hôte, lots de 100, file hors-ligne AES-GCM limitée à 500 échantillons et 24 heures, avec rejeu idempotent. -
Liens d'acquisition différés — le lien Oneviewr sur lequel l'utilisateur a cliqué avant d'installer l'application est retrouvé au premier lancement et rendu à l'application, qui l'envoie où le marchand l'a voulu (un écran précis). Nouveau
OneViewr.deferredDeepLink()(une seule remise) etOneViewr.resolveDeepLink(uri)(application déjà installée, lien universel / App Link), qui rendent unDeepLink(status,appRoute,appParams,fallbackUrl…) viaPOST /api/v1/public/deep-link/resolve. Le paquet ne navigue jamais lui-même.- Android :
ov_link_code, lu dans le Play Install Referrer — silencieux. - iOS : jeton signé copié dans le presse-papiers par la page de redirection.
Désactivé par défaut :
boot(readIosClipboard: true), car iOS 16 affiche « Autoriser le collage ? ». Seul un texte de la forme exacte d'un jeton Oneviewr est gardé ; il est effacé du presse-papiers après lecture. Un jeton expiré (dix minutes) n'est pas rattaché à l'installation. AttributionTouch.acquisitionLinkCode: le code du lien, envoyé avecidentify/track(premier/dernier contact) ; il compte à lui seul comme signal.recordDeepLinkle relève dans/l/{code}ou dans/{code}suronvw.link.boot(deferredLinkSource:)remplace la recherche par défaut (DeferredLinkSource, commeinstallAttributionpour l'installation).- Nouveau code natif iOS (
pasteboardToken) : l'application hôte doit être reconstruite après la mise à jour. - Au mieux, jamais garanti : voir la section « Liens d'acquisition Oneviewr » du README.
- Android :
-
OneViewr.signupProgress(step:, stepNumber:, attemptId:, properties:): une étape du formulaire d'inscription, envoyée comme l'événementsignup_progress(step,step_number,attempt_id), même contrat que le SDK JS. L'inscription se termine partrack('signup'); la fiche client Oneviewr montre alors l'étape atteinte, les tentatives et les dates. -
Sondages : on voit si l'on peut cocher une ou plusieurs réponses. Le compilateur d'Oneviewr pose devant chaque réponse un rond (choix unique) ou un carré (plusieurs), et le plugin y écrit ● ou ✓ quand la réponse est choisie — comme le SDK JS. Écrits en échappements JavaScript (
\u25CF,\u2713) : le script reste en ASCII, insensible à l'encodage. Un message compilé avant (sans picto) s'affiche comme avant. -
Messages in-app « version 2 » (Growth OS, lot 5.1) : carrousel, compte à rebours, coupon (copie dans le presse-papiers natif), sondage (réponses envoyées par
answerInAppSurvey, fermeture automatique après 1,8 s, comptée comme un clic), barre de progression, vidéo. Le plugin annoncesdkCapabilities.compilerVersion: 2: jusqu'ici, le serveur ne lui remettait jamais ces messages, ni ceux à écrans ciblés. -
OneViewr.setScreen(nom, context:): écran courant pour les « Écrans ciblés », envoyé avec chaque demande ; avec un contexte, la vérification repart aussitôt. -
Sécurité du rendu : un script constant du paquet anime les blocs (aucun
innerHTML, aucune URL, aucun réseau) ; un canalOneviewrInAppActiondont chaque message est revalidé ; clics synthétiques neutralisés ; toute navigation autre que le document etoneviewr-inapp:cta/dismissest désormais refusée (avant, un lienhttpsnaviguait dans la boîte). -
Encodage : le document du message déclare
utf-8— sans lui, un moteur pouvait relire « fidélité » en « fidélité ». -
Publicités Meta sur Android : le bloc chiffré part au serveur. Au lieu d'être jeté, le détail du clic que Meta chiffre dans le referrer Google Play est transmis tel quel dans
metaInstallReferrer(nouveau champ d'AttributionTouch, hors UTM, plafonné à 8 192 caractères). Le serveur Oneviewr l'ouvre avec la clé que le marchand enregistre dans Paramètres › Sources publicitaires, en tire la campagne, l'ensemble de publicités et la publicité, puis le jette — jamais stocké. -
Source d'acquisition (Growth OS, lot 3.1, volet Flutter). Le paquet note d'où vient l'appareil et envoie le premier et le dernier contact dans
identify(attribution) et le dernier danstrack(context.attribution), au format du SDK JS. Android : Play Install Referrer (UTM,gcliddes campagnes d'application Google Ads, publicité Meta — campagne chiffrée non déchiffrée). iOS : jeton Apple AdServices, échangé auprès d'Apple contre la campagne Apple Search Ads. Web : l'adresse de la page. NouveauOneViewr.recordDeepLink(uri)pour les liens qui ouvrent l'application.boot(attribution: false)désactive et efface ;boot(installAttribution:)relaie un outil de mesure mobile. Tant que le premier contact n'est pas fixé,identifyn'envoie aucune source (le serveur prendrait sinon le dernier contact pour le premier), puis le paquet le renvoie en arrière-plan dès qu'il l'est. -
Le paquet devient un plugin natif (
android/,ios/, canalone_viewr_flutter/attribution) : dépendancecom.android.installreferrer:installreferrer, frameworkAdServices. Une application hôte doit être reconstruite après la mise à jour. -
Les identifiants d'installation (
clientRefin-app,deviceIdpush) sont résolus par un seul corps, qui mémoïse leFutureet non la valeur. Deux appels concurrents arrivés avant que l'identifiant n'existe lisaient auparavant tous deux un stockage vide, généraient chacun leur UUID et en écrivaient un par-dessus l'autre : les deux requêtes partaient avec des identifiants différents pour le même appareil — exactement ce que cet identifiant existe pour éviter. Le cas n'est pas théorique, l'application hôte enregistrant son token push et vérifiant ses messages in-app depuis deux blocs qui avancent en parallèle. -
registerPushTokenaccepte undeviceIdexplicite, en plus de celui que le paquet persiste déjà. De typeFutureOr<String>?, parce que les sources d'identifiant d'appareil (FirebaseInstallations.getId(),device_info_plus) sont asynchrones ; résolu au moment de l'appel, une fois les gardes passées — unFuturefourni n'est donc jamais attendu pour un appel qui échoue sur une identité manquante. Omis, rien ne change : le paquet envoie le sien. -
FULLSCREENs'affiche sur leNavigatorracine et non sur celui d'où vient leBuildContext. Le contexte passé àcheckInAppMessagesvient presque toujours d'un écran d'onglet, donc d'unNavigatorimbriqué : la route « plein écran » ne couvrait alors que la zone de cet onglet, et la barre de navigation de l'application hôte restait visible par-dessus le message, onglets toujours actifs.BOTTOM_SHEETposait déjàuseRootNavigator: truepour cette raison ;BANNERsuit désormais la même règle côtéOverlay. -
FULLSCREENpose unScaffoldet uneSafeArea, et la hauteur allouée déduit les encoches. SansSafeArea, le haut du message passait sous la barre d'état et son bouton d'action sous l'indicateur d'accueil — pas seulement mal placé, intouchable. SansScaffold, les bandes laissées libres par les encoches ne peignaient rien de défini, uneMaterialPageRouten'ayant pas de fond propre. -
reset()referme le message in-app affiché au moment de l'appel, et interrompt une vérification en cours — il appartenait à l'identité qu'on vient d'oublier. Aucun acquittement n'est émis dans ce cas : personne ne l'a fermé, il a été retiré de l'écran. Aligne le paquet sur@oneviewr/js, dont leresetdémonte lui aussi l'overlay en place. -
checkInAppMessagesne tourne plus qu'une fois à la fois : un appel concurrent rend la main en silence au lieu d'empiler une seconde boîte de dialogue. Même verrou que lepolling/activedu poller côté SDK JS, et le cas est courant ici — la méthode est censée être rappelée à chaque retour au premier plan, donc pendant qu'un message est déjà affiché. -
ackInAppMessagerefuse unmessageIdvide et uneventhorsSHOWN/CLICKED/DISMISSED, avant tout appel réseau (ArgumentError). Sans ce contrôle, une faute de frappe partait sur le réseau — et comme cette méthode avale volontairement lesApiException, l'appelant ne voyait jamais son erreur. -
registerPushTokenenvoie maintenant undeviceIdstable (généré une fois par appareil, persisté via le mêmeKeyValueStorequeclientRef, survit àreset()) — permet au backend de mettre à jour la même ligne côté serveur quand le token FCM tourne (normal, périodique) au lieu d'en créer une nouvelle à chaque fois. Aucun changement de signature publique.
0.0.1 #
- Port initial du SDK JS public
@oneviewr/js(neero-crm/sdk-js) :boot,identify,track,registerPushToken,unregisterPushToken,reset. checkInAppMessages/ackInAppMessage— affichage des messages in-app (canalIN_APP) via une WebView (webview_flutter), avec ouverture du bouton d'action viaurl_launcher(« rupture de doctrine », voir le README) — appel manuel requis aprèsidentify()et à chaque retour au premier plan, contrairement au SDK JS.trackNotificationClick— signale qu'une notification push a été tapée (POST /messages/{id}/ack), à appeler depuis votre propre intégration Firebase (onMessageOpenedApp/getInitialMessage) — ce paquet ne s'abonne jamais à Firebase lui-même. Ne lève jamais, renvoiefalsepour toute notification qui n'est pas d'origine Oneviewr ou en cas d'échec.