Comment les Smartcalls sont devenus le kit Voximplant - caractéristiques de rebranding et de tueur


Pendant longtemps, nous préparions une mise à jour de Smartcalls, un éditeur visuel pour les appels sortants, et maintenant c'est arrivé. Aujourd'hui, sous la coupe, nous parlerons des changements UI / UX et grimperons sous le capot du mode démo pour montrer comment nous avons apprivoisé JointJS .

Qu'est-ce qui a réellement changé?


Du plus évident - un nouveau nom et une nouvelle URL, ce qui signifie que le kit Voximplant est disponible sur le lien approprié voximplant.com/kit . Nous avons également modifié la page d'inscription , maintenant c'est comme ça:

image

Bien que le concept soit resté le même, l'interface du produit a considérablement changé, devenant plus conviviale. Le menu supérieur a migré vers la gauche, ce qui a rendu la navigation dans les blocs plus logique et plus pratique.

image

En outre, le regroupement et le tri des scripts et des enregistrements audio, la recherche par numéros, ainsi que les fiches de campagne contenant de brèves informations à leur sujet, y compris de nouveaux indicateurs - la durée moyenne d'un appel réussi et le montant total dépensé, sont désormais disponibles.

image

En ce qui concerne les intégrations: l'interface conviviale est arrivée aux paramètres de messagerie, et sur les onglets Dialogflow, SIP, Variables globales, une recherche et un tri des fichiers par ID et hôte sont apparus.

image

En général, beaucoup de nouveautés et de cool! En savoir plus sur les changements dans notre blog .

Mais le plus important c'est l'éditeur


Mode démo (spoiler: c'est la principale caractéristique du tueur).


Exécution en temps réel d'un script avec mise en évidence des blocs impliqués et après exécution - le résultat d'un appel (Flow et Log), ce qui rend le débogage des scripts encore plus facile et plus rapide.

image

Vous pouvez regarder la vidéo du mode démo ici ou le tester vous-même après vous être enregistré avec le kit Voximplant .

Et comment tout cela est mis en œuvre, nous le dirons dans la section suivante. Nouvelles fonctionnalités de l'éditeur:

  • annuler / refaire (1 dans l'image ci-dessous);
  • touches de raccourci (2);
  • menu local où vous pouvez aligner les blocs et les liens entre eux en un seul clic, changer l'échelle, travailler avec miniMap, développer le script en plein écran et également le partager (copier ou enregistrer au format png) (3);
  • menu contextuel clic droit;
  • copier des blocs - non seulement dans le même script, mais aussi entre différents scripts et même (!) différents comptes;
  • verrouiller / déverrouiller un bloc - un bloc verrouillé peut être déplacé, mais il n'est PAS possible de le modifier pour éviter des modifications indésirables;
  • changement de couleur - visuellement, vous pouvez sélectionner plusieurs blocs "liés";
  • recherche par nom et contenu des blocs utilisés;
  • Bloc «menu interactif» - la possibilité de permuter des ports (options de réponse) à certains endroits par simple glisser-déposer.

image

Nous révélons les cartes


Il est temps de comprendre comment l'animation de bloc est implémentée dans le code.


L'éditeur appelle notre méthode API HTTP - StartScenarios - pour exécuter le script cloud. Le cloud Voximplant démarre le script et le remet à l'éditeur media_access_url. À partir de ce moment, l'éditeur extrait media_access_url chaque seconde, recevant des informations sur la façon dont le script «parcourt» les blocs en réponse - sur la base de ces données, l'éditeur met en évidence les blocs nécessaires et anime les connexions entre eux.

L'historique est un objet JSON avec les champs suivants:

  • horodatage;
  • idSource - bloc initial;
  • idTarget - bloc final;
  • port - port (il peut y avoir plusieurs sorties d'un bloc).

image

À l'aide de ces variables personnalisées et de service, le frontal comprend à quel bloc il passe au cours de quels tests. Comment comprend-il cela? Lorsque la construction visuelle se produit (un nouveau bloc est ajouté), un ID lui est immédiatement attribué, qui est ensuite utilisé dans l'historique comme idSource / idTarget.

Pour implémenter cette fonctionnalité, nous avons utilisé la bibliothèque JointJS, mais il y avait du code auto-écrit.

Commençons par la méthode principale selectBlock (), cela fonctionne comme suit: nous parcourons le tableau de l'historique des mouvements (idSource, idTarget) et dès que nous trouvons les points de début et de fin, recherchons la connexion entre eux:

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

S'il y a un lien entre eux, alors nous animons une balle qui longe la ligne de communication:
if (link) this.setLinkAnimation(link);

La méthode selectBlock () est appelée après chaque mise à jour de this.testHistory. Étant donné que plusieurs blocs passés peuvent arriver dans this.testHistory à la fois, nous appelons récursivement selectBlock une fois toutes les 700 ms (c'est le temps approximatif consacré à l'animation du mouvement d'un bloc à l'autre):

setTimeout(this.selectBlock, 700);

Tout le code de cette méthode est le suivant. Faites attention aux méthodes selectTestBlock et getTestLink, lignes 7 et 10 - maintenant nous en parlerons séparément:

selectBlock ( ) : void {
if ( this . historyIndex < this . testHistory . length ) {
const i = ceci . 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 ) ;
}
}
voir brut selectBlock.js hébergé avec ❤ par GitHub


Dessinez une connexion


La méthode getTestLink () aide à obtenir la connexion entre les blocs - elle est basée sur getConnectedLinks (), une méthode JointJS intégrée qui prend une entrée de bloc et renvoie un tableau de liens. Dans notre implémentation, nous recherchons dans le tableau résultant un lien avec un port, où la propriété source a la valeur portId:

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

Ensuite, s'il existe un lien, mettez-le en surbrillance:

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

Code de méthode:

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;
}
 

L'animation d'une balle en cours d'exécution est entièrement mise en œuvre au moyen de JointJS ( voir démo ).

Nous passons au bloc actuel


Nous appelons la méthode selectTestBlock () lorsqu'il est nécessaire de sélectionner le bloc final et d'y déplacer le canevas. Ici, nous obtenons les coordonnées du centre du bloc:

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

Appelez ensuite setTestCell () pour colorer le bloc:

editor.tester.setTestCell(cell);

Enfin, nous avons zoomé en son centre en utilisant la fonction auto-écrite zoomToCell () (c'est la plus intéressante, mais à ce sujet à la fin):

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

Code de méthode:

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éthode de coloration: trouvez l'élément SVG de notre bloc et ajoutez la classe CSS .est-testé pour rendre la couleur du bloc:

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

Zoom fluide


Enfin, zoomToCell ()! JointJS a une méthode intégrée pour déplacer la toile le long des axes X et Y, au début, ils voulaient la prendre. Cependant, cette méthode utilise la transformation comme attribut de la balise SVG; elle ne prend pas en charge l'animation fluide dans le navigateur Firefox + et utilise uniquement le CPU.

Nous avons fait un petit piratage - nous avons écrit notre fonction zoomToCell (), qui, essentiellement, fait la même chose, mais transforme en CSS en ligne, cela permet un rendu à l'aide du GPU (car WebGL est connecté au processus). Ainsi, le problème de la compatibilité entre navigateurs est résolu.

Notre fonction déplace non seulement la toile en XY, mais nous permet également de mettre à l'échelle (zoom) simultanément grâce à l'utilisation de la matrice de transformation.

La propriété will-change de la classe .animate-viewport indique au navigateur que l'élément sera modifié et que les optimisations doivent être appliquées, y compris l'utilisation du GPU, et la propriété de transition définit la fluidité du déplacement du canevas vers le bloc:

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

Tout notre code de méthode est ci-dessous:

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);
 }

Il s'est avéré que parfois même les plus avancés doivent être finis avec un fichier :) Nous espérons que vous avez apprécié de creuser l'intérieur de la baleine (peu importe à quel point cela semble effrayant). Nous vous souhaitons un développement réussi avec le kit Voximplant et plus encore!

All Articles