Tu app Capacitor conserva su pantalla web, mientras el transporte y la presentación de la conversación siguen siendo nativos.
Instala y sincroniza los paquetes Capacitor publicados
En un host nuevo, inicializa Capacitor una vez, instala los paquetes de plataforma de Capacitor compatibles y el wrapper de DobroDesk junto con el paquete de protocolo en la misma versión de lanzamiento. Añade cada plataforma nativa una vez y después sincroniza los proyectos.
# 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 syncRegistra el repositorio Android incluido
Añade la ruta Maven local del wrapper de Capacitor en la configuración Android del host. El wrapper aplica org.jetbrains.kotlin.android sin elegir una versión, así que declara los plugins de Kotlin y Android antes de las líneas include generadas. El host mínimo probado de Capacitor 6.2.1 usa Android Gradle Plugin 8.13.2, Kotlin Gradle Plugin 2.2.0, Gradle 8.14.3 y JDK 17. El host actual generado de Capacitor 8.5.3 usa AGP 8.13.0, Kotlin 2.2.20 y JDK 21. Conserva las versiones generadas por la versión de Capacitor instalada. PREFER_SETTINGS mantiene compatible el repositorio del módulo Android oficial de Capacitor mientras los artefactos de DobroDesk se resuelven desde el paquete instalado.
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.Configura los requisitos de las plataformas nativas
El wrapper publicado requiere iOS 15.1 o posterior y Android API 26 o posterior. Ambos perfiles Android probados usan compile SDK 36 y minSdk 26. El perfil mínimo de Capacitor 6.2.1 usa JDK 17. Para ese perfil, establece distributionUrl en android/gradle/wrapper/gradle-wrapper.properties como https://services.gradle.org/distributions/gradle-8.14.3-all.zip. Los hosts actuales de Capacitor 8.5.3 usan JDK 21 con el wrapper y la combinación compatible generados. Configura el destino de iOS antes de pod install y sube minSdkVersion del proyecto Android generado de 24 a 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.zipConfigura y presenta el wrapper nativo
El wrapper admite Capacitor de 6 a 8. Después de instalar los paquetes, sincronizar los proyectos nativos y configurar el repositorio, proporciona credentialProvider desde tu aplicación host.
La pantalla web del host sigue siendo una WebView normal de Capacitor, pero el widget y el transporte de DobroDesk son nativos. No cargues el renderer web del widget en la WebView como sustituto de esta integración.
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();Usar un disparador propio o una superficie integrada
Usa launcher: hidden cuando tu aplicación controla el disparador. Para una superficie integrada, define un ancho y una altura estables antes del montaje para que el diseño no salte durante la carga.
El botón propio debe ser accesible con teclado y exponer aria-controls y aria-expanded. Pasa el callback onError tipado si el host necesita su propio aviso de recuperación. La función dispose del ejemplo elimina el listener del botón y libera la superficie sin reiniciar la sesión ni el borrador.
mountWidget forma parte del paquete publicado de Mobile SDK 0.2.0. Úsalo para una superficie nativa acotada y llama a await handle.dispose() cuando el host retire el contenedor.
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();
},
};
}Mantén separados los ciclos híbrido y nativo
El credentialProvider del host debe llamar a un endpoint de backend que devuelva un JWT de identidad del cliente de corta duración. No debe exponer un support API token ni un signing secret en el bundle web. Llama a closeWidget al destruir la ruta host, a resetSession al descartar la conversación y a logout antes de cambiar de cuenta.
El presenter nativo respeta la preferencia del sistema para reducir movimiento cuando no se proporciona un override. Mantén independientes las transiciones de la página host para que un cambio de ruta de la WebView no cree un segundo widget nativo.
Gestionar carga, visibilidad y accesibilidad
Usa los hooks de carga o los eventos tipados para el estado de la pantalla anfitriona. La superficie muestra por sí misma la carga de mensajes, la espera y los reintentos para evitar un segundo spinner sobre el campo de entrada.
Oculta o muestra la superficie cuando una ruta cambia temporalmente y conserva el cliente. El widget respeta las preferencias del sistema para reducir movimiento. Quitar o cerrar la vista no descarta la sesión ni el borrador.
- Al volver a montar, reutiliza el mismo cliente y dimensiones estables del contenedor.
- Usa la función de visibilidad para ocultar temporalmente, no una función de reinicio de sesión.
- Finaliza la sesión solo al cerrar sesión o cambiar de cuenta de forma intencionada.
Verifica el flujo de Capacitor
- Ejecuta npx cap doctor después de npx cap sync y abre el widget desde la pantalla web. Confirma que la presentación es nativa y que no aparece un segundo iframe del widget web.
- Prueba start, resume, send con un idempotency key estable, refresh y flushOutbox mientras cambias la disponibilidad de red.
- Llama a logout antes de cambiar el cliente firmado y confirma que el siguiente cliente no hereda la sesión nativa anterior.