Como as Smartcalls se tornaram o Voximplant Kit - recursos de rebranding e killer


Durante muito tempo, estávamos preparando uma atualização para o Smartcalls, um editor visual para chamadas realizadas, e agora isso aconteceu. Hoje, em detalhes, falaremos sobre alterações na interface do usuário / UX e abordaremos o modo de demonstração para mostrar como domamos o JointJS .

O que realmente mudou?


Do mais óbvio - um novo nome e URL, o que significa que o Kit Voximplant está disponível no link voximplant.com/kit apropriado . Também modificamos a página de registro , agora é assim:

imagem

Embora o conceito permaneça o mesmo, a interface do produto mudou significativamente, tornando-se mais amigável. O menu superior foi migrado para a esquerda, tornando a navegação pelos blocos mais lógica e conveniente.

imagem

Além disso, o agrupamento e a classificação de scripts e gravações de áudio, a pesquisa por números e os cartões de campanha com informações breves sobre eles, incluindo novos indicadores - a duração média de uma chamada bem-sucedida e o valor total gasto, agora estão disponíveis.

imagem

Quanto às integrações: a interface amigável chegou às configurações de correio e, nas guias Dialogflow, SIP, Global Variables, uma pesquisa e classificação de arquivos por ID e host.

imagem

Em geral, muitos novos e legais! Leia mais sobre as mudanças em nosso blog .

Mas o mais importante é o editor


Modo de demonstração (spoiler: esse é o principal recurso assassino).


Execução em tempo real de um script com destaque dos blocos envolvidos e após a execução - o resultado de uma chamada (Flow e Log), que torna os scripts de depuração ainda mais fáceis e rápidos.

imagem

Você pode assistir ao vídeo do modo de demonstração aqui ou testá-lo depois de se registrar no Kit Voximplant .

E como tudo isso é implementado, contaremos na próxima seção. Novos recursos do editor:

  • desfazer / refazer (1 na imagem abaixo);
  • teclas de atalho (2);
  • menu pop-up onde é possível alinhar os blocos e links entre eles com um clique, alterar a escala, trabalhar com o miniMap, expandir o script para tela cheia e também compartilhá-lo (copiar ou salvar como png) (3);
  • clique com o botão direito do mouse no menu de contexto;
  • copiar blocos - não apenas no mesmo script, mas também entre scripts diferentes e até (!) contas diferentes;
  • bloquear / desbloquear bloco - um bloco bloqueado pode ser movido, mas NÃO é possível editar para evitar alterações indesejadas;
  • mudança de cor - visualmente, você pode selecionar vários blocos "relacionados";
  • busca por nomes e conteúdos de blocos usados;
  • Bloco “menu interativo” - a capacidade de trocar portas (opções de resposta) em locais, simplesmente arrastando e soltando.

imagem

Nós revelamos os cartões


É hora de descobrir como a animação em bloco é implementada no código.


O editor chama nosso método de API HTTP - StartScenarios - para executar o script em nuvem. A nuvem Voximplant inicia o script e o entrega ao editor media_access_url. A partir desse momento, o editor puxa media_access_url a cada segundo, recebendo informações sobre como o script "viaja" pelos blocos em resposta - com base nesses dados, o editor destaca os blocos necessários e anima as conexões entre eles.

O histórico é um objeto JSON com os seguintes campos:

  • timestamp;
  • idSource - bloco inicial;
  • idTarget - bloco final;
  • port - port (pode haver várias saídas de 1 bloco).

imagem

Com a ajuda dessas variáveis ​​personalizadas e de serviço, o front-end entende para qual bloco ele passa durante o teste. Como ele entende isso? Quando a construção visual ocorre (um novo bloco é adicionado), ele recebe imediatamente um ID, que é usado no histórico como idSource / idTarget.

Para implementar essa funcionalidade, usamos a biblioteca JointJS, mas havia algum código auto-escrito.

Vamos começar com o método principal selectBlock (), ele funciona da seguinte maneira: examinamos a matriz do histórico de movimentos (idSource, idTarget) e, assim que encontrarmos os pontos inicial e final, procuramos a conexão entre eles:

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

Se houver uma conexão entre eles, animamos uma bola correndo ao longo da linha de comunicação:
if (link) this.setLinkAnimation(link);

O método selectBlock () é chamado após cada atualização deste.testHistory. Como vários blocos passados ​​podem chegar a esse.testHistory de uma só vez, chamamos recursivamente o selectBlock uma vez a cada 700 ms (este é o tempo aproximado gasto na animação do movimento de bloco para bloco):

setTimeout(this.selectBlock, 700);

Todo o código desse método é o seguinte. Preste atenção aos métodos selectTestBlock e getTestLink, linhas 7 e 10 - agora falaremos sobre eles separadamente:

selectBlock ( ) : void {
if ( this . historyIndex < this . testHistory . length ) {
const i = isso . 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 cru selectBlock.js hospedado com ❤ por GitHub


Desenhe uma conexão


O método getTestLink () ajuda a obter a conexão entre os blocos - é baseado em getConnectedLinks (), um método JointJS interno que recebe uma entrada de bloco e retorna uma matriz de links. Em nossa implementação, estamos procurando na matriz resultante um link com uma porta, onde a propriedade source tenha o valor portId:

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

Então, se houver um link, destaque-o:

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

Código do 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;
}
 

A animação de uma bola de corrida é implementada completamente por meio do JointJS ( veja a demonstração ).

Passamos para o bloco atual


Chamamos o método selectTestBlock () quando é necessário selecionar o bloco final e mover a tela para ele. Aqui temos as coordenadas do centro do bloco:

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

Em seguida, chame setTestCell () para colorir o bloco:

editor.tester.setTestCell(cell);

Por fim, aumentamos o zoom para o centro usando a função auto-escrita zoomToCell () (é a mais interessante, mas sobre isso no final):

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

Código do 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 colorir: encontre o elemento SVG do nosso bloco e adicione a classe CSS. É testada para fazer a cor do bloco:

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

Zoom suave


Finalmente, zoomToCell ()! O JointJS possui um método interno para mover a tela ao longo dos eixos X e Y, inicialmente eles queriam pegá-la. No entanto, esse método usa transform como um atributo da tag SVG; ele não suporta animação suave no navegador Firefox + e usa apenas a CPU.

Fizemos um pequeno hack - escrevemos nossa função zoomToCell (), que, em essência, faz a mesma coisa, mas lança a transformação como CSS embutido, isso nos permite renderizar usando a GPU (porque o WebGL está conectado ao processo). Assim, o problema de compatibilidade entre navegadores foi resolvido.

Nossa função não apenas move a tela em XY, mas também nos permite dimensionar (zoom) simultaneamente através do uso da matriz de transformação.

A propriedade will-change da classe .animate-viewport informa ao navegador que o elemento será alterado e as otimizações devem ser aplicadas, incluindo o uso da GPU, e a propriedade de transição define a suavidade de mover a tela para o bloco:

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

Todo o código do nosso método está abaixo:

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

Como se viu, às vezes até os mais avançados precisam terminar com um arquivo :) Esperamos que você tenha gostado de cavar o interior da baleia (por mais assustador que pareça). Desejamos a você desenvolvimento bem-sucedido com o Kit Voximplant e muito mais!

All Articles