Cómo Smartcalls se convirtió en Voximplant Kit - características de cambio de marca y asesino


Durante mucho tiempo estuvimos preparando una actualización de Smartcalls, un editor visual para llamadas salientes, y ahora sucedió. Hoy, debajo del gato, hablaremos sobre los cambios de UI / UX y subiremos bajo el capó del modo de demostración para mostrar cómo domesticamos JointJS .

¿Qué ha cambiado realmente?


De lo más obvio: un nuevo nombre y URL, lo que significa que el kit Voximplant está disponible en el enlace apropiado voximplant.com/kit . También modificamos la página de registro , ahora es así:

imagen

Aunque el concepto se ha mantenido igual, la interfaz del producto ha cambiado significativamente, haciéndose más fácil de usar. El menú superior migró a la izquierda, lo que hizo que navegar por los bloques fuera más lógico y conveniente.

imagen

Además, ahora está disponible la agrupación y clasificación de guiones y grabaciones de audio, búsqueda por números, así como tarjetas de campaña con información breve sobre ellos, incluidos nuevos indicadores: la duración promedio de una llamada exitosa y la cantidad total gastada.

imagen

En cuanto a las integraciones: la interfaz fácil de usar llegó a la configuración del correo, y en las pestañas Dialogflow, SIP, Variables globales, apareció una búsqueda y clasificación de archivos por ID y host.

imagen

En general, mucho nuevo y genial! Lea más sobre los cambios en nuestro blog .

Pero lo más importante es el editor.


Modo de demostración (spoiler: esta es la principal característica asesina).


Ejecución de script en tiempo real con resaltado de los bloques involucrados, y después de la ejecución, el resultado de una llamada (Flow and Log), lo que hace que la depuración de scripts sea aún más fácil y rápida.

imagen

Puede ver el video del modo de demostración aquí o probarlo usted mismo después de registrarse con el kit Voximplant .

Y cómo se implementa todo esto, lo diremos en la siguiente sección. Nuevas características del editor:

  • deshacer / rehacer (1 en la imagen a continuación);
  • teclas de acceso rápido (2);
  • menú emergente donde puede alinear los bloques y enlaces entre ellos con un solo clic, cambiar la escala, trabajar con miniMap, expandir el script a pantalla completa y también compartirlo (copiar o guardar como png) (3);
  • haga clic con el botón derecho en el menú contextual;
  • copiar bloques, no solo dentro del mismo script, sino también entre diferentes scripts e incluso (!) diferentes cuentas;
  • bloquear / desbloquear bloque: se puede mover un bloque bloqueado, pero NO es posible editarlo para evitar cambios no deseados;
  • cambio de color: visualmente puede seleccionar varios bloques "relacionados";
  • buscar por nombres y contenidos de bloques usados;
  • Bloque "Menú interactivo": la capacidad de intercambiar puertos (opciones de respuesta) en lugares simplemente arrastrando y soltando.

imagen

Revelamos las cartas


Es hora de descubrir cómo se implementa la animación de bloque en el código.


El editor llama a nuestro método API HTTP - StartScenarios - para ejecutar el script en la nube. La nube Voximplant inicia el script y lo entrega al editor media_access_url. A partir de este momento, el editor extrae media_access_url cada segundo y recibe información sobre cómo el script "viaja" a través de los bloques en respuesta; en función de estos datos, el editor resalta los bloques necesarios y anima las conexiones entre ellos.

El historial es un objeto JSON con los siguientes campos:

  • marca de tiempo
  • idSource - bloque inicial;
  • idTarget - bloque final;
  • puerto - puerto (puede haber varias salidas de 1 bloque).

imagen

Con la ayuda de estas variables personalizadas y de servicio, el front-end comprende a qué bloque pasa durante cada prueba. ¿Cómo entiende esto? Cuando se produce una construcción visual (se agrega un nuevo bloque), se le asigna inmediatamente una identificación, que luego se usa en el historial como idSource / idTarget.

Para implementar esta funcionalidad, utilizamos la biblioteca JointJS, pero había un código auto escrito.

Comencemos con el método principal selectBlock (), funciona de la siguiente manera: revisamos la matriz del historial de movimientos (idSource, idTarget) y tan pronto como encontremos los puntos de inicio y fin, busquemos la conexión entre ellos:

const link = this.editor.getTestLink(sourceCell, portId);

Si hay una conexión entre ellos, entonces animamos una pelota que corre a lo largo de la línea de comunicación:
if (link) this.setLinkAnimation(link);

Se llama al método selectBlock () después de cada actualización de this.testHistory. Dado que varios bloques pasados ​​pueden llegar a this.testHistory a la vez, llamamos recursivamente selectBlock una vez cada 700 ms (este es el tiempo aproximado dedicado a animar el movimiento de bloque a bloque):

setTimeout(this.selectBlock, 700);

Todo el código para este método es el siguiente. Preste atención a los métodos selectTestBlock y getTestLink, líneas 7 y 10; ahora hablaremos de ellos por separado:

selectBlock ( ) : void {
if ( this . historyIndex < this . testHistory . length ) {
const i = esto . historyIndex ;
const targetCellId = this.testHistory[i].idTarget;
const sourceCellId = this.testHistory[i].idSource;
const portId = this.testHistory[i].port;
const targetCell = this.editor.selectTestBlock(targetCellId);
const sourceCell = this.editor.getCell(sourceCellId);
if (sourceCell && targetCell) {
const link = this.editor.getTestLink(sourceCell, portId);
if (link) this.setLinkAnimation(link);
}
this.historyIndex += 1;
setTimeout ( this . selectBlock , 700 ) ;
}
}
ver en bruto selectBlock.js alojado con ❤ por GitHub


Dibujar una conexión


El método getTestLink () ayuda a obtener la conexión entre los bloques; se basa en getConnectedLinks (), un método incorporado de JointJS que toma una entrada de bloque y devuelve una matriz de enlaces. En nuestra implementación, buscamos en la matriz resultante un enlace con un puerto, donde la propiedad de origen tiene el valor portId:

link = this.graph.getConnectedLinks(cell, {outbound : true}).find(item => {
     return item.get('source').port === portId;

Luego, si hay un enlace, resáltelo:

return link ? (link.toFront() && link) : null;

Código de método:

getTestLink(sourceCell: Cell, portId: string): Link {
  let link = null;
  if (sourceCell && sourceCell.id) {
    let cell = null;
    if (sourceCell.type === 'ScenarioStart' || sourceCell.type === 'IncomingStart') {
      cell = this.getStartCell()
    } else {
      cell = this.graph.getCell(sourceCell.id);
    }
    link = this.graph.getConnectedLinks(cell, {outbound : true}).find(item => {
      return item.get('source').port === portId;
    });
  }
  return link ? (link.toFront() && link) : null;
}
 

La animación de una pelota corriendo se implementa completamente por medio de JointJS ( ver demo ).

Nos movemos al bloque actual


Llamamos al método selectTestBlock () cuando es necesario seleccionar el bloque final y mover el lienzo hacia él. Aquí obtenemos las coordenadas del centro del bloque:

const center = cell.getBBox().center();

Luego llame a setTestCell () para colorear el bloque:

editor.tester.setTestCell(cell);

Finalmente, ampliamos su centro usando la función de escritura automática zoomToCell () (es la más interesante, pero al final):

editor.paperController.zoomToCell(center, 1, false);

Código de método:

selectTestBlock(id: string): Cell {
 const cell = (id === 'ScenarioStart') ? editor.tester.getStartCell() : editor.graph.getCell(id);
 if (cell) {
   const center = cell.getBBox().center();
   editor.tester.setTestCell(cell);
   editor.paperController.zoomToCell(center, 1, false);
 }
 return cell;
}

Método para colorear: encuentre el elemento SVG de nuestro bloque y agregue la clase CSS. Es probado para que el bloque sea de color:

setTestCell(cell: Cell): void {
 const view = cell.findView(this.paper);
 if (view) view.el.classList.add('is-tested');
}

Zoom suave


Finalmente, zoomToCell ()! JointJS tiene un método incorporado para mover el lienzo a lo largo de los ejes X e Y, al principio querían tomarlo. Sin embargo, este método usa la transformación como un atributo de la etiqueta SVG; no admite animación fluida en el navegador Firefox + y solo usa la CPU.

Hicimos un pequeño truco: escribimos nuestra función zoomToCell (), que, en esencia, hace lo mismo, pero las conversiones se transforman como CSS en línea, esto permite renderizar usando la GPU (porque WebGL está conectado al proceso). Por lo tanto, se resuelve el problema de compatibilidad entre navegadores.

Nuestra función no solo mueve el lienzo en XY, sino que también nos permite escalar (hacer zoom) simultáneamente mediante el uso de la matriz de transformación.

La propiedad will-change de la clase .animate-viewport le dice al navegador que el elemento se cambiará y que se deben aplicar optimizaciones, incluido el uso de la GPU, y la propiedad de transición establece la suavidad de mover el lienzo al bloque:

.animate-viewport {
 will-change: transform;
 transition: transform 0.5s ease-in-out;

Todo nuestro código de método está a continuación:

public zoomToCell(center: g.Point, zoom: number, offset: boolean = true): void {
   this.updateGridSize();
   const currentMatrix = this.paper.layers.getAttribute('transform');
   //   svg-,        center
   //   ,     style
   const { a, b, c, d, e, f } = this.zoomMatrix(zoom, center, offset);
   //  FireFox    ,       
   this.paper.layers.style.transform = currentMatrix;
   //    FF  ,     ,    
   setTimeout(() => {
     //  CSS- .animate-viewport,    - transition;
     //    style      - transition
     this.paper.layers.classList.add('animate-viewport');
     this.paper.layers.style.transform = `matrix(${ a }, ${ b }, ${ c }, ${ d }, ${ e }, ${ f })`;
     const duration = parseFloat(getComputedStyle(this.paper.layers)['transitionDuration']) * 1000;
     //        style;
     //      joint
     setTimeout(() => {
       this.paper.layers.classList.remove('animate-viewport');
       this.paper.layers.style.transform = null;
       this.paper.matrix(newMatrix);
       this.paper.trigger('paper:zoom');
       this.updateGridSize();
       this.paper.trigger('paper:update');
     }, duration);
   }, 100);
 }

Al final resultó que, a veces, incluso los más avanzados deben terminar con un archivo :) Esperamos que haya disfrutado cavando en el interior de la ballena (no importa lo espeluznante que parezca). ¡Le deseamos un desarrollo exitoso con el kit Voximplant y más!

All Articles