Exemplo Completo
HTML
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exemplo Completo FireSyncClient</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.5; margin: 15px; background-color: #f4f4f4; }
.container { background-color: #fff; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); margin-bottom: 15px; }
h1, h2, h3 { border-bottom: 1px solid #eee; padding-bottom: 5px; margin-top: 0; }
label { display: block; margin-bottom: 3px; font-weight: bold; }
input[type="text"], textarea, select { width: 95%; max-width: 500px; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 3px; font-size: 0.95em; }
textarea { min-height: 60px; font-family: monospace; }
button { padding: 10px 15px; margin-right: 8px; margin-bottom: 8px; cursor: pointer; border: none; border-radius: 3px; background-color: #007bff; color: white; font-size: 0.95em; }
button:hover { background-color: #0056b3; }
button:disabled { background-color: #cccccc; cursor: not-allowed; }
pre { background-color: #e9e9e9; border: 1px solid #ddd; padding: 10px; max-height: 250px; overflow-y: auto; white-space: pre-wrap; word-wrap: break-word; font-size: 0.9em; border-radius: 3px; }
hr { border: 0; border-top: 1px solid #eee; margin: 20px 0; }
.status-ok { color: green; font-weight: bold; }
.status-error { color: red; font-weight: bold; }
.status-warn { color: orange; font-weight: bold; }
.flex-buttons button { flex-grow: 1; margin-left: 4px; margin-right: 4px;}
.flex-buttons { display: flex; flex-wrap: wrap; margin-left: -4px; margin-right: -4px; }
</style>
</head>
<body>
<h1>Exemplo Completo FireSyncClient (v0.3.0)</h1>
<div class="container">
<h2>1. Conexão e Autenticação</h2>
<div>
<label for="jwt">Token:</label>
<textarea id="jwt" placeholder="Cole seu TOKEN aqui..."></textarea>
</div>
<div class="flex-buttons">
<button id="connectBtn">Conectar</button>
<button id="disconnectBtn" disabled>Desconectar</button>
<button id="goOfflineBtn" disabled>Ficar Offline</button>
<button id="goOnlineBtn" disabled>Ficar Online</button>
</div>
<div>
<strong>Status:</strong> <span id="connStatus">disconnected</span>
</div>
<div>
<strong>Autenticado:</strong> <span id="authStatus">false</span>
</div>
<div>
<strong>BasePath:</strong> <span id="basePath">-</span>
</div>
</div>
<div class="container" id="dataOps" style="display: none;">
<h2>2. Operações de Dados</h2>
<div>
<label for="pathInput">Caminho Relativo:</label>
<input type="text" id="pathInput" value="meusDados/item1">
</div>
<div>
<label for="valueInput">Valor (JSON) para Set/Update/Push:</label>
<textarea id="valueInput" placeholder='Ex: {"nome": "Teste", "valor": 123} ou "uma string" ou 456 ou null'>{"timestamp": 0}</textarea>
</div>
<div class="flex-buttons">
<button id="setBtn">Set</button>
<button id="updateBtn">Update</button>
<button id="getBtn">Get</button>
<button id="removeBtn">Remove</button>
<button id="pushBtn">Push com Valor</button>
<button id="pushRefBtn">Push só Ref</button>
</div>
<h3>Resultado do Último 'Get':</h3>
<pre id="getLastResult">(Nenhum get realizado)</pre>
</div>
<div class="container" id="listenerOps" style="display: none;">
<h2>3. Listener em Tempo Real (onValue)</h2>
<div>
<label for="listenerPathInput">Path Relativo para Escutar:</label>
<input type="text" id="listenerPathInput" value="meusDados/item1">
<button id="toggleListenerBtn">Iniciar Listener</button>
</div>
<h3>Dados Recebidos pelo Listener:</h3>
<pre id="onValueData">(Listener inativo)</pre>
</div>
<div class="container">
<h2>Logs da Aplicação Cliente</h2>
<pre id="log"></pre>
</div>
<script src="https://cdn.firesync.app/clients/fsc.js"></script>
<script>
// --- Referências aos Elementos DOM ---
const jwtInput = document.getElementById('jwt');
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const goOfflineBtn = document.getElementById('goOfflineBtn');
const goOnlineBtn = document.getElementById('goOnlineBtn');
const statusSpan = document.getElementById('connStatus');
const authStatusSpan = document.getElementById('authStatus');
const basePathSpan = document.getElementById('basePath');
const dataOpsDiv = document.getElementById('dataOps');
const listenerOpsDiv = document.getElementById('listenerOps');
const pathInput = document.getElementById('pathInput');
const valueInput = document.getElementById('valueInput');
const setBtn = document.getElementById('setBtn');
const updateBtn = document.getElementById('updateBtn');
const getBtn = document.getElementById('getBtn');
const removeBtn = document.getElementById('removeBtn');
const pushBtn = document.getElementById('pushBtn');
const pushRefBtn = document.getElementById('pushRefBtn');
const getLastResultPre = document.getElementById('getLastResult');
const listenerPathInput = document.getElementById('listenerPathInput');
const toggleListenerBtn = document.getElementById('toggleListenerBtn');
const onValueDataPre = document.getElementById('onValueData');
const logPre = document.getElementById('log');
let currentListenerUnsubscribe = null;
let currentListenerPath = null;
// --- Funções Auxiliares ---
function log(message) {
console.log("[App]", message); // Log no console também
const now = new Date().toLocaleTimeString();
logPre.textContent += `[${now}] ${message}\n`;
logPre.scrollTop = logPre.scrollHeight; // Auto-scroll
}
function updateUI() {
const state = fireSyncClient.getConnectionState();
const isAuth = fireSyncClient.isAuthenticated();
statusSpan.textContent = state;
statusSpan.className = (state === 'authenticated' || state === 'connected') ? 'status-ok' : (state === 'error' || state === 'disconnected') ? 'status-error' : 'status-warn';
authStatusSpan.textContent = isAuth ? 'Sim' : 'Não';
authStatusSpan.className = isAuth ? 'status-ok' : 'status-error';
basePathSpan.textContent = isAuth ? (fireSyncClient.ref().path || '(Raiz)') : '-';
const canConnect = (state === 'disconnected' || state === 'error');
const canDisconnect = !canConnect;
const canGoOnline = canConnect; // Pode tentar ir online se desconectado/erro
const canGoOffline = canDisconnect && state !== 'reconnecting'; // Pode ir offline se conectado/autenticado
connectBtn.disabled = !canConnect;
disconnectBtn.disabled = !canDisconnect;
goOfflineBtn.disabled = !canGoOffline;
goOnlineBtn.disabled = !canGoOnline;
// Habilita operações de dados apenas se autenticado
const allowDataOps = isAuth;
dataOpsDiv.style.display = allowDataOps ? 'block' : 'none';
listenerOpsDiv.style.display = allowDataOps ? 'block' : 'none';
}
// --- Event Handlers da Biblioteca ---
fireSyncClient.on('state_changed', (newState) => {
log(`>>> Evento: state_changed -> ${newState}`);
updateUI();
});
fireSyncClient.on('error', (error) => {
log(`>>> Evento: error -> ${error.message}`);
updateUI(); // Atualiza UI para refletir estado de erro, se aplicável
});
fireSyncClient.on('disconnected', (details) => {
log(`>>> Evento: disconnected -> Code: ${details.code}, Clean: ${details.wasClean}, UserReq: ${details.requestedByUser}`);
updateUI();
stopListener(); // Para listener ao desconectar
});
fireSyncClient.on('authenticated', () => {
log(`>>> Evento: authenticated`);
// O state_changed já chama updateUI, mas podemos fazer algo extra aqui se necessário
});
fireSyncClient.on('connecting', () => log(`>>> Evento: connecting`));
fireSyncClient.on('connected', () => log(`>>> Evento: connected (WebSocket Aberto)`));
fireSyncClient.on('reconnecting', () => log(`>>> Evento: reconnecting`));
// --- Event Handlers dos Botões ---
connectBtn.addEventListener('click', () => {
const token = jwtInput.value.trim();
if (!token) { alert("Por favor, insira um token JWT."); return; }
log("Configurando token...");
fireSyncClient.setAuthToken(token);
log("Conectando...");
updateUI(); // Atualiza para estado 'connecting'
fireSyncClient.connect()
.then(() => { log("Conexão e Autenticação OK!"); /* updateUI será chamado por state_changed */ })
.catch(err => { log(`Erro ao conectar: ${err.message}`); /* updateUI será chamado por state_changed */ alert(`Falha: ${err.message}`); });
});
disconnectBtn.addEventListener('click', () => { log("Solicitando desconexão..."); fireSyncClient.disconnect(); updateUI(); });
goOfflineBtn.addEventListener('click', () => { log("Solicitando ficar offline..."); fireSyncClient.goOffline(); updateUI(); });
goOnlineBtn.addEventListener('click', () => { log("Solicitando ficar online..."); fireSyncClient.goOnline().catch(err => { log(`Erro ao tentar ficar online: ${err.message}`); alert(`Falha: ${err.message}`); updateUI(); }); updateUI(); });
setBtn.addEventListener('click', () => handleDataAction('set'));
updateBtn.addEventListener('click', () => handleDataAction('update'));
getBtn.addEventListener('click', () => handleDataAction('get'));
removeBtn.addEventListener('click', () => handleDataAction('remove'));
pushBtn.addEventListener('click', () => handleDataAction('pushWithValue'));
pushRefBtn.addEventListener('click', () => handleDataAction('pushRefOnly'));
toggleListenerBtn.addEventListener('click', startStopListener);
// --- Funções de Ação de Dados ---
function handleDataAction(actionType) {
const path = pathInput.value.trim();
const valueStr = valueInput.value.trim();
let value;
if (!path && actionType !== 'pushRefOnly' && actionType !== 'pushWithValue') {
alert("Path relativo não pode ser vazio para esta ação."); return;
}
// Parseia valor se necessário para set/update/push
if (['set', 'update', 'pushWithValue'].includes(actionType)) {
if (!valueStr) { alert("Valor JSON não pode ser vazio para esta ação."); return; }
try { value = JSON.parse(valueStr); }
catch (e) { alert(`Valor JSON inválido: ${e.message}`); return; }
}
// Atualiza o timestamp se o valor for um objeto e tiver a chave "timestamp"
if (typeof value === 'object' && value !== null && value.hasOwnProperty('timestamp')) {
value.timestamp = Date.now();
valueInput.value = JSON.stringify(value); // Atualiza textarea
log("Timestamp atualizado no payload.");
}
log(`Executando ação: ${actionType} em '${path || '(raiz do push)'}'...`);
const targetRef = fireSyncClient.ref(path); // Cria referência
let promise;
switch (actionType) {
case 'set':
promise = targetRef.set(value);
break;
case 'update':
promise = targetRef.update(value); // 'value' deve ser um objeto
break;
case 'get':
promise = targetRef.get();
break;
case 'remove':
promise = targetRef.remove();
break;
case 'pushWithValue':
promise = fireSyncClient.ref(path).push(value); // Push é na ref pai
break;
case 'pushRefOnly':
const newRef = fireSyncClient.ref(path).push(); // Push é na ref pai
log(`Push (só ref) gerou path relativo: ${newRef.relativePath}, chave: ${newRef.key}`);
// Seleciona o novo path para facilitar o próximo Set/Get
pathInput.value = newRef.relativePath;
promise = Promise.resolve(newRef); // Resolve imediatamente com a ref
break;
default:
log("Ação desconhecida.");
promise = Promise.reject(new Error("Ação desconhecida"));
}
promise.then(result => {
log(`Sucesso na ação ${actionType} para '${path || '(raiz do push)'}'.`);
if (actionType === 'get') {
const snapshot = result; // 'result' é o DataSnapshot para 'get'
const resultText = `Existe: ${snapshot.exists()}\nChave: ${snapshot.key}\nValor: ${JSON.stringify(snapshot.val(), null, 2)}`;
getLastResultPre.textContent = resultText;
log(`Resultado Get:\n${resultText}`);
}
if (actionType === 'pushWithValue') {
const newRef = result; // 'result' é a DatabaseReference para push com valor
log(`Push com valor completo. Nova ref path: ${newRef.path}`);
// Seleciona o novo path para facilitar
pathInput.value = newRef.relativePath;
}
}).catch(error => {
log(`Erro na ação ${actionType} para '${path || '(raiz do push)'}': ${error.message}`);
if (actionType === 'get') {
getLastResultPre.textContent = `Erro: ${error.message}`;
}
alert(`Erro ${actionType}: ${error.message}`);
});
}
// --- Funções do Listener ---
function startStopListener() {
if (currentListenerUnsubscribe) {
// Para o listener existente
log(`Parando listener em '${currentListenerPath}'...`);
currentListenerUnsubscribe();
currentListenerUnsubscribe = null;
currentListenerPath = null;
onValueDataPre.textContent = "(Listener inativo)";
toggleListenerBtn.textContent = "Iniciar Listener";
listenerPathInput.disabled = false;
} else {
// Inicia novo listener
const path = listenerPathInput.value.trim();
if (!path) { alert("Path do listener não pode ser vazio."); return; }
log(`Iniciando listener onValue para '${path}'...`);
try {
const listenerRef = fireSyncClient.ref(path);
currentListenerUnsubscribe = listenerRef.onValue(
(snapshot) => {
const dataText = `[${new Date().toLocaleTimeString()}] Path: ${snapshot.ref.path}\nExiste: ${snapshot.exists()}\nValor: ${JSON.stringify(snapshot.val(), null, 2)}`;
onValueDataPre.textContent = dataText;
log(`Listener recebeu dados para '${path}'.`);
},
(error) => {
log(`Erro no listener para '${path}': ${error.message}`);
onValueDataPre.textContent = `Erro no listener: ${error.message}`;
// Limpa para permitir tentar novamente
currentListenerUnsubscribe = null;
currentListenerPath = null;
toggleListenerBtn.textContent = "Iniciar Listener";
listenerPathInput.disabled = false;
}
);
currentListenerPath = path; // Guarda o path que estamos escutando
toggleListenerBtn.textContent = `Parar Listener (${path})`;
listenerPathInput.disabled = true; // Desabilita input enquanto escuta
onValueDataPre.textContent = "(Aguardando dados...)";
} catch (e) {
log(`Erro ao iniciar listener para '${path}': ${e.message}`);
alert(`Erro ao iniciar listener: ${e.message}`);
}
}
}
// --- Inicialização da UI ---
updateUI(); // Define estado inicial dos botões
</script>
</body>
</html>
Como Usar Este Exemplo:
Salve: Salve o código HTML acima como
index.html
(ou outro nome).Biblioteca: Certifique-se de que o arquivo
fsc.js
esteja no caminho correto referenciado na tag<script src=
https://cdn.firesync.app/clients/fsc.js"></script>
.Testar:
Cole um TOKEN válido na caixa de texto.
Clique em "Conectar". Observe os logs e o status.
Use os botões de operação de dados (
Set
,Get
,Update
,Remove
,Push
) para interagir com os caminhos. O campoValue
aceita JSON (lembre-se de usar aspas duplas para strings e chaves dentro do JSON).Use a seção "Listener em Tempo Real" para iniciar/parar um listener
onValue
em um caminho específico e observar as atualizações.Teste os botões
Ficar Offline
eFicar Online
para ver o controle manual e a reconexão automática em ação (se você derrubar o servidor e voltar, por exemplo, após clicar em Ficar Online ou se estava online).
Este exemplo deve dar uma boa visão geral de como utilizar as funcionalidades
Atualizado