Votre app Capacitor conserve son écran web, tandis que le transport et la présentation de la conversation restent natifs.
Installer et synchroniser les packages Capacitor publiés
Pour un nouvel hôte, initialisez Capacitor une fois, installez les packages de plateforme Capacitor correspondants ainsi que le wrapper DobroDesk et le package de protocole partagé dans la même version de release. Ajoutez chaque plateforme native une fois, puis synchronisez les projets.
# Run once for a new host.
npx cap init "Your app" com.example.app --web-dir=www
npm install @capacitor/core@8.5.3 @capacitor/cli@8.5.3 @capacitor/ios@8.5.3 @capacitor/android@8.5.3 @dobrodesk/capacitor-sdk@0.2.0 @dobrodesk/mobile-protocol@0.2.0
npx cap add ios --packagemanager CocoaPods
npx cap add android
npx cap syncEnregistrer le dépôt Android inclus
Ajoutez le chemin Maven local du wrapper Capacitor dans les paramètres Android de l'hôte. Le wrapper applique org.jetbrains.kotlin.android sans choisir de version : déclarez donc les plugins Kotlin et Android avant les lignes include générées. L'hôte minimal Capacitor 6.2.1 testé utilise Android Gradle Plugin 8.13.2, Kotlin Gradle Plugin 2.2.0, Gradle 8.14.3 et JDK 17. L'hôte Capacitor 8.5.3 actuel généré utilise AGP 8.13.0, Kotlin 2.2.20 et JDK 21. Conservez les versions générées par la version Capacitor installée. PREFER_SETTINGS garde compatible le dépôt du module Android Capacitor officiel tandis que les artefacts DobroDesk sont résolus depuis le package installé.
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id 'com.android.application' version '8.13.0' apply false
id 'com.android.library' version '8.13.0' apply false
id 'org.jetbrains.kotlin.android' version '2.2.20' apply false
}
import org.gradle.api.initialization.resolve.RepositoriesMode
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven { url = uri("$rootDir/../node_modules/@dobrodesk/capacitor-sdk/native/maven") }
}
}
// Keep the generated include and apply-from lines below this block.Définir les exigences des plateformes natives
Le wrapper publié nécessite iOS 15.1 ou une version ultérieure et Android API 26 ou une version ultérieure. Les deux profils Android testés utilisent compile SDK 36 et minSdk 26. Le profil minimal Capacitor 6.2.1 utilise JDK 17. Pour ce profil, définissez distributionUrl dans android/gradle/wrapper/gradle-wrapper.properties sur https://services.gradle.org/distributions/gradle-8.14.3-all.zip. Les hôtes Capacitor 8.5.3 actuels utilisent JDK 21 avec le wrapper et le jeu de versions compatibles générés. Définissez la cible iOS avant pod install et augmentez minSdkVersion du projet Android généré de 24 à 26.
# ios/App/Podfile
platform :ios, "15.1"
# android/variables.gradle
minSdkVersion = 26
compileSdkVersion = 36
# android/gradle/wrapper/gradle-wrapper.properties for Capacitor 6.2.1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zipConfigurer et présenter le wrapper natif
Le wrapper prend en charge Capacitor 6 à 8. Après l'installation des packages, la synchronisation des projets natifs et la configuration du dépôt, fournissez credentialProvider depuis votre application hôte.
L'écran web de l'hôte reste une WebView Capacitor normale, mais le widget et le transport DobroDesk sont natifs. Ne chargez pas le renderer web du widget dans la WebView à la place de cette intégration.
import { DobroDeskCapacitorClient } from "@dobrodesk/capacitor-sdk";
import type { DobroDeskSdkConfig } from "@dobrodesk/mobile-protocol";
const config: DobroDeskSdkConfig = {
apiOrigin: "https://dobrodesk.com",
integrationId: "wgt_0123456789abcdef0123456789abcdef",
accountScope: currentCustomer.id,
credentialProvider: async () => ({
authorizationToken: await loadDobroDeskToken(),
}),
};
const client = new DobroDeskCapacitorClient(config);
await client.presentWidget();Utiliser un déclencheur personnalisé ou une surface intégrée
Utilisez launcher: hidden lorsque votre application contrôle le déclencheur. Pour une surface intégrée, définissez une largeur et une hauteur stables avant le montage afin d'éviter les changements de mise en page pendant le chargement.
Le bouton personnalisé doit rester accessible au clavier et exposer aria-controls ainsi que aria-expanded. Transmettez le callback onError typé si l'hôte doit afficher son propre état de récupération. La fonction dispose de l'exemple retire le listener du bouton et libère la surface sans réinitialiser la session ni le brouillon.
mountWidget fait partie du package Mobile SDK 0.2.0 publié. Utilisez-le pour une surface native limitée et appelez await handle.dispose() lorsque l'hôte retire le conteneur.
import type {
DobroDeskCapacitorClient,
DobroDeskEmbeddedWidget,
} from "@dobrodesk/capacitor-sdk";
type SupportPanelOptions = Readonly<{
onError?: (error: unknown) => void;
}>;
export async function mountSupportPanel(
client: DobroDeskCapacitorClient,
{ onError }: SupportPanelOptions = {},
) {
const supportSlot = document.querySelector<HTMLElement>("#support-slot");
const helpButton = document.querySelector<HTMLButtonElement>("#help-button");
if (
!supportSlot ||
!helpButton ||
!supportSlot.isConnected ||
!helpButton.isConnected
) {
throw new Error("Connect #support-slot and #help-button before mounting");
}
Object.assign(supportSlot.style, { width: "100%", height: "560px" });
helpButton.setAttribute("aria-controls", "support-slot");
let visible = true;
let mountedHandle: DobroDeskEmbeddedWidget | undefined;
const toggle = () => {
visible = !visible;
mountedHandle?.setVisible(visible);
helpButton.setAttribute("aria-expanded", String(visible));
};
const onClose = () => {
visible = false;
helpButton.setAttribute("aria-expanded", "false");
};
helpButton.addEventListener("click", toggle);
try {
mountedHandle = await client.mountWidget(
supportSlot,
{ locale: "en", view: "home" },
{ onClose, onError },
);
mountedHandle.setVisible(true);
helpButton.setAttribute("aria-expanded", "true");
} catch (error) {
helpButton.removeEventListener("click", toggle);
onError?.(error);
throw error;
}
if (!mountedHandle) throw new Error("Support widget did not mount");
const handle = mountedHandle;
return {
handle,
dispose: async () => {
helpButton.removeEventListener("click", toggle);
await handle.dispose();
},
};
}Séparer les cycles hybrides et natifs
Le credentialProvider fourni par l'hôte doit appeler un endpoint backend qui renvoie un JWT d'identité client de courte durée. Il ne doit pas exposer de support API token ni de signing secret dans le bundle web. Appelez closeWidget lorsque la route hôte est détruite, resetSession lorsque la conversation est abandonnée et logout avant un changement de compte.
Le presenter natif respecte le réglage système de réduction des mouvements lorsqu'aucun override n'est fourni. Gardez les transitions de page de l'hôte indépendantes afin qu'un changement de route WebView ne crée pas un second widget natif.
Gérer le chargement, la visibilité et l'accessibilité
Utilisez les hooks de chargement ou les événements typés pour l'état de l'écran hôte. La surface affiche elle-même le chargement des messages, l'attente et les nouvelles tentatives afin d'éviter un second indicateur sur le champ de saisie.
Masquez ou affichez la surface lors d'une navigation temporaire en conservant le client. Le widget respecte les préférences système de réduction des mouvements. Le retrait ou la fermeture ne supprime ni la session ni le brouillon.
- Lors du nouveau montage, réutilisez le même client et des dimensions stables pour le conteneur.
- Utilisez la fonction de visibilité pour un masquage temporaire, sans réinitialiser la session.
- Terminez la session uniquement lors de la déconnexion ou d'un changement de compte volontaire.
Vérifier le flux Capacitor
- Exécutez npx cap doctor après npx cap sync, puis ouvrez le widget depuis l'écran web. Confirmez que la présentation est native et qu'aucun second iframe de widget web n'apparaît.
- Testez start, resume, send avec un idempotency key stable, refresh et flushOutbox pendant que la disponibilité du réseau change.
- Appelez logout avant de changer le client signé et confirmez que le client suivant n'hérite pas de la session native précédente.