Wie Smartcalls zu Voximplant Kit wurden - Rebranding- und Killer-Funktionen


Lange Zeit haben wir ein Update für Smartcalls vorbereitet, einen visuellen Editor für ausgehende Anrufe, und jetzt ist es passiert. Heute werden wir unter dem Schnitt über UI / UX-Änderungen sprechen und unter die Haube des Demo-Modus klettern, um zu zeigen, wie wir JointJS gezähmt haben .

Was hat sich tatsächlich geändert?


Aus dem offensichtlichsten - ein neuer Name und eine neue URL, was bedeutet, dass das Voximplant Kit unter dem entsprechenden Link voximplant.com/kit verfügbar ist . Wir haben auch die Registrierungsseite geändert , jetzt ist es so:

Bild

Obwohl das Konzept unverändert geblieben ist, hat sich die Produktoberfläche erheblich geändert und ist benutzerfreundlicher geworden. Das Hauptmenü wurde nach links verschoben, wodurch das Navigieren in den Blöcken logischer und bequemer wurde.

Bild

Darüber hinaus stehen jetzt die Gruppierung und Sortierung von Skripten und Audioaufnahmen, die Suche nach Zahlen sowie Kampagnenkarten mit kurzen Informationen dazu, einschließlich neuer Indikatoren - die durchschnittliche Dauer eines erfolgreichen Anrufs und der Gesamtbetrag der Ausgaben - zur Verfügung.

Bild

Was die Integrationen betrifft: Die benutzerfreundliche Oberfläche hat die E-Mail-Einstellungen aufgerufen, und auf den Registerkarten Dialogflow, SIP, Globale Variablen wurde eine Suche und Sortierung der Dateien nach ID und Host angezeigt.

Bild

Im Allgemeinen viel Neues und Cooles! Lesen Sie mehr über die Änderungen in unserem Blog .

Das Wichtigste ist jedoch der Herausgeber


Demo-Modus (Spoiler: Dies ist die Hauptkiller-Funktion).


Echtzeitausführung eines Skripts mit Hervorhebung der beteiligten Blöcke und nach der Ausführung - das Ergebnis eines Aufrufs (Flow and Log), wodurch das Debuggen von Skripten noch einfacher und schneller wird.

Bild

Sie können das Video des Demo-Modus hier ansehen oder es selbst testen, nachdem Sie sich beim Voximplant Kit registriert haben .

Und wie dies alles umgesetzt wird, erfahren Sie im nächsten Abschnitt. Neue Funktionen des Editors:

  • rückgängig machen / wiederholen (1 im Bild unten);
  • Hotkeys (2);
  • Popup-Menü, in dem Sie die Blöcke und Verknüpfungen mit einem Klick ausrichten, den Maßstab ändern, mit miniMap arbeiten, das Skript auf Vollbild erweitern und auch freigeben (kopieren oder als PNG speichern) können (3);
  • Rechtsklick auf das Kontextmenü;
  • Kopierblöcke - nicht nur innerhalb desselben Skripts, sondern auch zwischen verschiedenen Skripten und sogar (!) verschiedenen Konten;
  • Block sperren / entsperren - Ein gesperrter Block kann verschoben werden, es ist jedoch NICHT möglich, ihn zu bearbeiten, um unerwünschte Änderungen zu vermeiden.
  • Farbwechsel - visuell können Sie mehrere "verwandte" Blöcke auswählen;
  • Suche nach Namen und Inhalten der verwendeten Blöcke;
  • Block „Interaktives Menü“ - die Möglichkeit, Ports (Antwortoptionen) durch einfaches Ziehen und Ablegen an bestimmten Stellen auszutauschen.

Bild

Wir enthüllen die Karten


Es ist Zeit herauszufinden, wie Blockanimationen im Code implementiert sind.


Der Editor ruft unsere HTTP-API-Methode StartScenarios auf, um das Cloud-Skript auszuführen. Die Voximplant-Cloud startet das Skript und gibt es an den Editor media_access_url weiter. Von diesem Moment an zieht der Editor jede Sekunde media_access_url und erhält Informationen darüber, wie das Skript als Antwort durch die Blöcke „wandert“. Basierend auf diesen Daten hebt der Editor die erforderlichen Blöcke hervor und animiert die Verbindungen zwischen ihnen.

Der Verlauf ist ein JSON-Objekt mit den folgenden Feldern:

  • Zeitstempel;
  • idSource - Anfangsblock;
  • idTarget - letzter Block;
  • Port - Port (es können mehrere Ausgänge von 1 Block vorhanden sein).

Bild

Mithilfe dieser benutzerdefinierten Variablen und Dienstvariablen versteht das Front-End, an welchen Block es während welcher Tests übergeben wird. Wie versteht er das? Wenn eine visuelle Konstruktion erfolgt (ein neuer Block wird hinzugefügt), wird ihm sofort eine ID zugewiesen, die dann im Verlauf als idSource / idTarget verwendet wird.

Um diese Funktionalität zu implementieren, haben wir die JointJS-Bibliothek verwendet, aber es gab selbst geschriebenen Code.

Beginnen wir mit der Hauptmethode selectBlock (). Sie funktioniert wie folgt: Wir gehen das Array des Bewegungsverlaufs (idSource, idTarget) durch und suchen nach der Verbindung zwischen ihnen, sobald wir den Start- und Endpunkt gefunden haben:

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

Wenn es eine Verbindung zwischen ihnen gibt, animieren wir einen Ball, der entlang der Kommunikationslinie läuft:
if (link) this.setLinkAnimation(link);

Die Methode selectBlock () wird nach jeder Aktualisierung von this.testHistory aufgerufen. Da mehrere übergebene Blöcke gleichzeitig in this.testHistory eintreffen können, rufen wir selectBlock rekursiv alle 700 ms auf (dies ist die ungefähre Zeit, die für die Animation der Bewegung von Block zu Block aufgewendet wird):

setTimeout(this.selectBlock, 700);

Der gesamte Code für diese Methode lautet wie folgt. Beachten Sie die Methoden selectTestBlock und getTestLink, Zeilen 7 und 10 - jetzt werden wir separat darüber sprechen:

selectBlock ( ) : void {
if ( this . historyIndex < this . testHistory . length ) {
const i = this . 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 ) ;
}}
}}


Zeichnen Sie eine Verbindung


Die Methode getTestLink () hilft beim Herstellen der Verbindung zwischen den Blöcken. Sie basiert auf getConnectedLinks (), einer integrierten JointJS-Methode, die eine Blockeingabe verwendet und ein Array von Links zurückgibt. In unserer Implementierung suchen wir im resultierenden Array nach einer Verknüpfung mit einem Port, wobei die Quelleigenschaft den Wert portId hat:

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

Wenn es dann einen Link gibt, markieren Sie ihn:

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

Methodencode:

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

Die Animation eines laufenden Balls wird vollständig mittels JointJS implementiert ( siehe Demo ).

Wir gehen zum aktuellen Block


Wir rufen die Methode selectTestBlock () auf, wenn der letzte Block ausgewählt und die Zeichenfläche dorthin verschoben werden muss. Hier erhalten wir die Koordinaten der Blockmitte:

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

Rufen Sie dann setTestCell () auf, um den Block einzufärben:

editor.tester.setTestCell(cell);

Schließlich haben wir mit der selbstgeschriebenen Funktion zoomToCell () in die Mitte gezoomt (es ist die interessanteste, aber am Ende darüber):

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

Methodencode:

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

Methode zum Färben: Suchen Sie das SVG-Element unseres Blocks und fügen Sie die CSS-Klasse .is-getestet hinzu, um die Blockfarbe zu erhalten:

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

Reibungsloser Zoom


Zum Schluss zoomToCell ()! JointJS verfügt über eine integrierte Methode zum Verschieben der Leinwand entlang der X- und Y-Achse. Zunächst wollten sie diese aufnehmen. Diese Methode verwendet jedoch die Transformation als Attribut des SVG-Tags. Sie unterstützt keine reibungslose Animation im Firefox + -Browser und verwendet nur die CPU.

Wir haben einen kleinen Hack gemacht - wir haben unsere Funktion zoomToCell () geschrieben, die im Wesentlichen dasselbe tut, aber die Transformation als Inline-CSS umwandelt. Dies ermöglicht das Rendern mit der GPU (da WebGL mit dem Prozess verbunden ist). Damit ist das Problem der Cross-Browser-Kompatibilität gelöst.

Unsere Funktion verschiebt nicht nur die Leinwand in XY, sondern ermöglicht es uns auch, mithilfe der Transformationsmatrix gleichzeitig zu skalieren (zu zoomen).

Die Eigenschaft will-change der Klasse .animate-viewport teilt dem Browser mit, dass das Element geändert wird und Optimierungen angewendet werden müssen, einschließlich der Verwendung der GPU, und die Übergangseigenschaft legt die Glätte des Verschiebens der Zeichenfläche in den Block fest:

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

Unser gesamter Methodencode ist unten aufgeführt:

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

Wie sich herausstellte, müssen manchmal sogar die fortgeschrittensten mit einer Datei fertig werden :) Wir hoffen, Sie haben es genossen, in das Innere des Wals zu graben (egal wie gruselig es klingt). Wir wünschen Ihnen eine erfolgreiche Entwicklung mit dem Voximplant Kit und mehr!

All Articles