
Security News
TC39 Advances Temporal to Stage 4 Alongside Several ECMAScript Proposals
TC39’s March 2026 meeting advanced eight ECMAScript proposals, including Temporal reaching Stage 4 and securing its place in the ECMAScript 2026 specification.
@solucx/react-native-solucx-widget
Advanced tools
Widget React Native para coleta de feedback e pesquisas de satisfacao, desenvolvido pela SoluCX.
npm install @solucx/react-native-solucx-widget
Voce pode integrar o widget de duas formas:
SoluCXWidgetHost no root e dispara a exibicao com SoluCXWidget.create(...).show().SoluCXWidget diretamente no JSX, no ponto exato em que o widget deve aparecer.SoluCXWidgetHost no root do appO SoluCXWidgetHost deve ser montado uma unica vez no componente raiz da aplicacao. Ele e responsavel por renderizar o widget quando solicitado via SoluCXWidget.create(...).show().
// App.tsx
import { SoluCXWidgetHost } from '@solucx/react-native-solucx-widget';
export default function App() {
return (
<>
<NavigationContainer>
<Stack.Navigator>{/* suas telas */}</Stack.Navigator>
</NavigationContainer>
{/* Obrigatorio: monte uma vez no root */}
<SoluCXWidgetHost />
</>
);
}
Use SoluCXWidget.create() para construir e exibir o widget. As opcoes de supressao (dias de espera) sao opcionais — se nao forem passadas, o widget as busca automaticamente do painel de configuracao da jornada (configurado remotamente).
// CheckoutScreen.tsx
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
function onCheckoutComplete() {
SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setType('modal')
.setData({
journey: 'pos_venda',
user_id: 'user_123',
email: 'cliente@email.com',
name: 'Joao Silva',
})
.show();
}
Pronto! O widget vai buscar as configuracoes de supressao remotamente e decidir se deve exibir ou nao.
Quando voce quiser controlar a exibicao pelo proprio componente React, use SoluCXWidget. Nesse modo, nao e necessario montar SoluCXWidgetHost.
// CheckoutScreen.tsx
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
export function CheckoutScreen() {
return (
<SoluCXWidget
soluCXKey="SUA_CHAVE_SOLUCX"
type="inline"
data={{
journey: 'pos_venda',
user_id: 'user_123',
email: 'cliente@email.com',
name: 'Joao Silva',
}}
callbacks={{
onOpened: (userId) => {
console.log('Widget exibido para:', userId);
},
}}
/>
);
}
Este e o caso de uso mais comum. As regras de supressao (quantos dias esperar apos cada evento) sao configuradas no painel de configuracao da jornada e buscadas automaticamente pelo widget. Voce nao precisa passar nenhuma opcao.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
// Disparar apos uma compra
function afterPurchase() {
SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setType('bottom')
.setData({
journey: 'pos_venda',
user_id: 'user_123',
email: 'cliente@email.com',
name: 'Joao Silva',
store_id: 'loja_01',
})
.show();
// As opcoes de supressao serao buscadas automaticamente da API
// com base na configuracao da jornada "pos_venda"
}
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
function showSurvey() {
SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setType('modal')
.setData({
journey: 'atendimento',
user_id: 'user_456',
email: 'maria@email.com',
name: 'Maria Santos',
employee_id: 'atendente_01',
employee_name: 'Carlos',
})
.setCallbacks({
onPreOpen: (userId) => {
console.log('Preparando widget para:', userId);
},
onOpened: (userId) => {
console.log('Widget exibido para:', userId);
analytics.track('survey_shown', { userId });
},
onBlocked: (reason) => {
// Widget nao foi exibido por causa de uma regra de supressao
console.log('Widget bloqueado:', reason);
// Ex: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL"
},
onClosed: () => {
console.log('Usuario fechou o widget');
},
onCompleted: (userId) => {
console.log('Pesquisa respondida por:', userId);
analytics.track('survey_completed', { userId });
},
onPartialCompleted: (userId) => {
console.log('Pesquisa parcialmente respondida por:', userId);
},
onError: (message) => {
console.error('Erro no widget:', message);
},
})
.show();
}
Se voce precisar sobrescrever as configuracoes da jornada para um caso especifico, passe as opcoes diretamente. Quando setOptions() e chamado, o widget nao busca configuracoes da API e usa os valores fornecidos.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
function showUrgentSurvey() {
SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setType('modal')
.setData({
journey: 'nps_trimestral',
user_id: 'user_789',
email: 'pedro@email.com',
})
.setOptions({
height: 600,
maxAttemptsAfterDismiss: 5, // Máximo de 5 tentativas para essa jornada
waitDaysAfterWidgetDismiss: 90, // Esperar 90 dias após o usuário fechar
waitDaysAfterWidgetSubmit: 30, // Esperar 30 dias após resposta completa
waitDaysAfterWidgetDisplay: 7, // Esperar 7 dias após qualquer exibição
})
.setCallbacks({
onOpened: (userId) => console.log('Abriu:', userId),
onCompleted: (userId) => console.log('Respondeu:', userId),
})
.show();
}
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
const data = {
journey: 'pos_venda',
user_id: 'user_123',
email: 'cliente@email.com',
};
// Bottom: barra fixa na parte inferior (padrao)
SoluCXWidget.create('KEY').setType('bottom').setData(data).show();
// Top: barra fixa no topo
SoluCXWidget.create('KEY').setType('top').setData(data).show();
// Modal: sobreposicao centralizada que bloqueia o fundo
SoluCXWidget.create('KEY').setType('modal').setData(data).show();
// Inline: integrado ao fluxo do layout (respeita a posicao no JSX)
SoluCXWidget.create('KEY').setType('inline').setData(data).show();
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
// Fechar o widget a qualquer momento
function handleTimeout() {
SoluCXWidget.dismiss();
}
Os metodos de log sao metodos de instancia — usam os dados do builder (instanceKey do create(), journey e userId do setData()) para identificar automaticamente qual registro acessar. Nao e necessario passar esses parametros novamente.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
// Cria a instancia com os dados que identificam o registro
const widget = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
// Consultar os logs de supressao
const logs = await widget.getWidgetLogs();
console.log(logs);
// {
// attempts: 3, // quantas vezes o widget foi exibido nesta experiencia
// answeredTransactionIds: ['txn-1'], // transacoes que ja responderam e nao devem reabrir o widget
// lastExperienceId: 'pos_venda', // ultima jornada associada ao contador
// lastDisplayAttempt: 1741000000000, // ultima tentativa de exibicao bloqueada
// lastFirstAccess: 1740000000000, // primeira vez que o widget foi exibido
// lastDisplay: 1741000000000, // ultima exibicao do widget
// lastDismiss: 1741000000000, // ultima vez que o usuario fechou
// lastSubmit: 0, // ultimo envio completo
// lastPartialSubmit: 0, // ultimo envio parcial
// }
Use overrideTimestamp para simular cenarios de teste sem esperar os dias reais.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
const widget = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
// Informar primeiro acesso do usuário
await widget.overrideTimestamp('lastFirstAccess', new Date('2026-01-01T00:00:00Z'));
// Ou usar timestamp em milissegundos diretamente
await widget.overrideTimestamp('lastFirstAccess', 1700000000000);
// Resetar um campo para "nunca aconteceu" (valor 0)
await widget.overrideTimestamp('lastSubmit', 0);
// Depois de sobrescrever, pode exibir o widget normalmente
widget.show();
Campos disponiveis para overrideTimestamp:
| Campo | Descricao |
|---|---|
lastDisplayAttempt | Ultima tentativa de exibicao (widget foi bloqueado) |
lastFirstAccess | Primeira vez que o widget foi exibido para o usuario |
lastDisplay | Ultima exibicao do widget |
lastDismiss | Ultima vez que o usuario fechou o widget |
lastSubmit | Ultimo envio completo da pesquisa |
lastPartialSubmit | Ultimo envio parcial da pesquisa |
answeredTransactionIds | Lista de transaction_id que ja responderam e devem ser bloqueados localmente |
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
const widget = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
// Limpar TODOS os logs (reseta contadores e timestamps)
await widget.clearWidgetLogs();
// Apos limpar, o widget vai se comportar como se nunca tivesse sido exibido
widget.show();
Se o widget esta sendo bloqueado por uma regra de supressao e voce quer forca-lo a exibir (por exemplo, para testes), sobrescreva a data do evento que esta bloqueando:
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
const widget = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
// Verificar qual evento esta bloqueando
const logs = await widget.getWidgetLogs();
console.log('Ultimo dismiss:', new Date(logs.lastDismiss));
console.log('Tentativas:', logs.attempts);
// Resetar o dismiss para desbloquear
await widget.overrideTimestamp('lastDismiss', 0);
// Agora o widget vai exibir (se nao houver outra regra bloqueando)
widget.show();
Cada combinacao de instanceKey + journey + userId tem seus proprios logs. Widgets de jornadas diferentes nao interferem entre si.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
// Widget da jornada "pos_venda"
const widgetPosVenda = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
// Widget da jornada "atendimento"
const widgetAtendimento = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'atendimento', user_id: 'user_123' });
// Os logs sao independentes:
const logsPosVenda = await widgetPosVenda.getWidgetLogs();
const logsAtendimento = await widgetAtendimento.getWidgetLogs();
// logsPosVenda pode ter 5 tentativas enquanto logsAtendimento tem 0
// Limpar os logs de uma jornada NAO afeta a outra
await widgetPosVenda.clearWidgetLogs();
// Apenas os logs de pos_venda foram resetados
Quando nao ha user_id, email ou cpf, os logs sao compartilhados por todos os usuarios daquela jornada naquele dispositivo.
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';
// Sem userId — logs sao por instanceKey:journey (compartilhado no dispositivo)
const widget = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda' });
const logs = await widget.getWidgetLogs();
// Chave de armazenamento: "SUA_CHAVE_SOLUCX:pos_venda"
// Com userId — logs sao por instanceKey:journey:userId (isolado por usuario)
const widgetComUser = SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({ journey: 'pos_venda', user_id: 'user_123' });
const logsComUser = await widgetComUser.getWidgetLogs();
// Chave de armazenamento: "SUA_CHAVE_SOLUCX:pos_venda:user_123"
Importante: Voce nao precisa passar essas opcoes no codigo. Elas podem (e devem) ser configuradas remotamente no painel de configuracao da jornada. O widget busca automaticamente as configuracoes da API quando
setOptions()nao e chamado.
As opcoes controlam quantos dias o widget deve esperar apos cada tipo de evento antes de exibir novamente:
| Opcao | Descricao |
|---|---|
enabled | Habilita/desabilita o widget. Quando false, bloqueia imediatamente sem validar outras regras |
samplingPercentage | Porcentagem de usuarios que verao o widget (0-100). Valor 100 ou undefined = sem amostragem |
maxAttemptsAfterDismiss | Numero maximo de tentativas de exibicao por experiencia |
waitDaysAfterWidgetDisplayAttempt | Dias apos uma tentativa de exibicao bloqueada |
waitDaysAfterWidgetFirstAccess | Dias apos o primeiro acesso do usuario a jornada |
waitDaysAfterWidgetDisplay | Dias apos qualquer exibicao do widget |
waitDaysAfterWidgetDismiss | Dias apos o usuario fechar o widget |
waitDaysAfterWidgetSubmit | Dias apos o usuario responder a pesquisa |
waitDaysAfterWidgetPartialSubmit | Dias apos o usuario responder parcialmente |
Regras:
enabled: false bloqueia o widget imediatamente com BLOCKED_BY_DISABLED, sem verificar nenhuma outra regraenabled: true ou undefined = widget habilitado (comportamento padrao)samplingPercentage: sorteia se o usuario vera o widget. Ex: 50 = 50% de chance. Valor 100 ou undefined = todos veem. Valor 0 = ninguem ve0 ou undefined nos campos de dias = sem restricao (o campo nao bloqueia)onBlocked(reason)reason indica qual regra bloqueou (ex: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL")maxAttemptsAfterDismiss conta quantas vezes o widget foi exibido para aquela experiencia (jornada). Se o ID da experiencia (journey ou form_id) mudar, o contador de tentativas e resetado automaticamenteQUESTION_ANSWERED, envio completo ou envio parcial), o contador de tentativas tambem e resetado para nao bloquear exibicoes futuras por tentativas antigastransaction_id e essa transacao ja foi concluida ou parcialmente concluida antes, o widget bloqueia localmente e nao faz chamadas de opcoes nem de preflightSoluCXWidget (classe principal)| Metodo | Descricao |
|---|---|
SoluCXWidget.create(soluCXKey) | Cria uma nova instancia do widget |
.setType(type) | Define o tipo: 'bottom', 'top', 'modal', 'inline' |
.setData(data) | Define os dados do usuario/transacao |
.setOptions(options) | Define opcoes locais (opcional - se nao chamar, busca da API) |
.setCallbacks(callbacks) | Define callbacks de eventos |
.show() | Exibe o widget |
| Metodo | Descricao |
|---|---|
SoluCXWidget.dismiss() | Fecha o widget programaticamente |
Usam os dados do builder (instanceKey, journey, userId) para acessar os logs da combinacao correta.
| Metodo | Descricao |
|---|---|
.getWidgetLogs() | Retorna os logs de supressao |
.overrideTimestamp(field, date) | Sobrescreve uma data de evento (debug/teste) |
.clearWidgetLogs() | Limpa todos os logs |
Isolamento de armazenamento: Os logs sao isolados por
instanceKey:journey:userId. Um app com 2 widgets de jornadas diferentes tera dados de supressao independentes. OuserIde opcional — quando presente, os logs sao por usuario.
SoluCXWidgetHost (componente React)Componente que deve ser montado uma vez no root do app quando a integracao for feita por funcao com SoluCXWidget.create(...).show(). Nao e necessario ao usar SoluCXWidgetView diretamente.
import { SoluCXWidgetHost } from '@solucx/react-native-solucx-widget';
// No root do app
<SoluCXWidgetHost />
SoluCXWidgetView (componente React)Componente para uso direto no JSX. Pode ser usado no lugar do fluxo por funcao quando fizer mais sentido controlar a renderizacao pelo componente.
import { SoluCXWidgetView } from '@solucx/react-native-solucx-widget';
<SoluCXWidgetView
soluCXKey="SUA_CHAVE_SOLUCX"
type="inline"
data={{ journey: 'pos_venda', user_id: 'user_123' }}
/>
Props:
| Prop | Descricao |
|---|---|
soluCXKey | Chave de instancia do widget |
type | Tipo do widget: 'bottom', 'top', 'modal', 'inline' |
data | Dados do usuario/transacao |
options | Opcoes locais de configuracao e supressao |
callbacks | Callbacks de ciclo de vida e eventos |
interface WidgetData {
journey?: string; // Nome da jornada (obrigatorio na pratica)
user_id?: string; // ID do usuario
email?: string; // Email do usuario
name?: string; // Nome do usuario
cpf?: string; // CPF
document?: string; // Documento
phone?: string; // Telefone
phone2?: string; // Telefone secundario
birth_date?: string; // Data de nascimento (YYYY-MM-DD)
transaction_id?: string; // ID da transacao
customer_id?: string; // ID do cliente
store_id?: string; // ID da loja
store_name?: string; // Nome da loja
employee_id?: string; // ID do funcionario
employee_name?: string; // Nome do funcionario
amount?: number; // Valor da transacao
score?: number; // Score
[key: `param_${string}`]: string | number | undefined; // Parametros customizados
}
interface WidgetCallbacks {
onPreOpen?: (userId: string) => void; // Antes de abrir
onOpened?: (userId: string) => void; // Widget aberto
onBlocked?: (reason: BlockReason) => void; // Widget bloqueado por regra de supressao
onClosed?: () => void; // Usuario fechou
onCompleted?: (userId: string) => void; // Pesquisa respondida
onPartialCompleted?: (userId: string) => void; // Pesquisa parcialmente respondida
onError?: (message: string) => void; // Erro no widget
onPageChanged?: (page: string) => void; // Pagina mudou
onQuestionAnswered?: () => void; // Pergunta respondida
onResize?: (height: string) => void; // Widget redimensionou
}
interface WidgetOptions {
enabled?: boolean; // Habilita/desabilita o widget (default: true). Quando false, bloqueia sem validar.
samplingPercentage?: number; // Porcentagem de usuarios que verao o widget (0-100). Default: 100 (todos).
height?: number; // Altura fixa em pontos (se nao informado, altura dinamica)
maxAttemptsAfterDismiss?: number; // Maximo de tentativas por experiencia (0 ou undefined = sem limite)
// Regras de supressao (opcionais - configuradas remotamente pelo painel da jornada):
waitDaysAfterWidgetDisplayAttempt?: number;
waitDaysAfterWidgetFirstAccess?: number;
waitDaysAfterWidgetDisplay?: number;
waitDaysAfterWidgetDismiss?: number;
waitDaysAfterWidgetSubmit?: number;
waitDaysAfterWidgetPartialSubmit?: number;
}
Valores possiveis retornados no callback onBlocked:
| Valor | Significado |
|---|---|
BLOCKED_BY_DISABLED | Widget desabilitado (enabled: false) |
BLOCKED_BY_SAMPLING | Usuario nao foi selecionado pela amostragem (samplingPercentage) |
BLOCKED_BY_MAX_ATTEMPTS | Numero maximo de tentativas atingido para esta experiencia |
BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVAL | Dentro do intervalo apos tentativa de exibicao |
BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVAL | Dentro do intervalo apos primeira visualizacao |
BLOCKED_BY_WIDGET_DISPLAY_INTERVAL | Dentro do intervalo apos exibicao |
BLOCKED_BY_WIDGET_DISMISS_INTERVAL | Dentro do intervalo apos fechamento |
BLOCKED_BY_WIDGET_SUBMIT_INTERVAL | Dentro do intervalo apos envio completo |
BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVAL | Dentro do intervalo apos envio parcial |
App chama SoluCXWidget.create('KEY').setData({...}).show()
|
v
SoluCXWidgetHost recebe a configuracao
|
v
Widget monta e executa bootstrap():
|
+-- Se setOptions() FOI chamado --> usa opcoes locais
+-- Se setOptions() NAO foi chamado --> busca opcoes da API (painel da jornada)
|
+-- shouldDisplayWidget() verifica enabled + maxAttemptsAfterDismiss + 6 regras de supressao
+-- (validacao LOCAL, antes de chamar a API, para reduzir carga no servidor):
| +-- Se transaction_id ja respondido --> onBlocked('BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED'), widget NAO exibe
| +-- Se enabled === false --> onBlocked('BLOCKED_BY_DISABLED'), widget NAO exibe
| +-- Sorteia baseado em samplingPercentage --> se nao selecionado, onBlocked('BLOCKED_BY_SAMPLING')
| +-- Se experiencia mudou --> reseta contador de tentativas
| +-- Se tentativas >= maxAttemptsAfterDismiss --> onBlocked('BLOCKED_BY_MAX_ATTEMPTS')
| +-- Para cada regra: timestamp do evento + dias de espera > agora?
| +-- Se QUALQUER regra bloqueia --> onBlocked(reason), widget NAO exibe
| +-- Se NENHUMA bloqueia --> continua
|
+-- Busca URL do formulario na API (preflight)
|
+-- Se EXIBE:
| +-- Incrementa contador de tentativas
| +-- Salva lastDisplay (e lastFirstAccess na primeira vez)
| +-- Renderiza WebView com o formulario
| +-- onOpened(userId)
|
+-- Eventos do usuario:
+-- Fecha widget --> salva lastDismiss, onClosed()
+-- Responde pergunta --> zera contador de tentativas, onQuestionAnswered()
+-- Responde pesquisa --> zera contador de tentativas, salva transaction_id respondido, salva lastSubmit, onCompleted(userId)
+-- Resposta parcial --> zera contador de tentativas, salva transaction_id respondido, salva lastPartialSubmit, onPartialCompleted(userId)
| Versao | React Native | Expo | iOS | Android |
|---|---|---|---|---|
| 1.0.x | 0.70+ | 50+ | 11+ | API 21+ |
Proprietario - SoluCX. Uso restrito a clientes licenciados.
FAQs
The React Native SDK for Solucx Widget
The npm package @solucx/react-native-solucx-widget receives a total of 159 weekly downloads. As such, @solucx/react-native-solucx-widget popularity was classified as not popular.
We found that @solucx/react-native-solucx-widget demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
TC39’s March 2026 meeting advanced eight ECMAScript proposals, including Temporal reaching Stage 4 and securing its place in the ECMAScript 2026 specification.

Research
/Security News
Since January 31, 2026, we identified at least 72 additional malicious Open VSX extensions, including transitive GlassWorm loader extensions targeting developers.

Research
Six malicious Packagist packages posing as OphimCMS themes contain trojanized jQuery that exfiltrates URLs, injects ads, and loads FUNNULL-linked redirects.