facebook / modules /dashboard.js
Althnayi's picture
Upload 27 files
2a196ac verified
Raw
History Blame Contribute Delete
71.2 kB
// ============================================================================
// File: modules/dashboard.js
// ============================================================================
(global => {
'use strict';
// State structure representing unified sync state across tabs
const DashboardState = {
userPoints: 100000.00, // Initialized as float to represent USD
usedSerials: [],
pointsTransactions: [],
balanceSignature: "",
fbShares: 25,
fbSharesSignature: "",
waShares: 25,
waSharesSignature: "",
currentAccountId: null,
userXP: 0,
userLevel: 1,
lifetimeFb: 0,
lifetimeWa: 0,
lifetimeTasks: 0,
activeSubscription: 'Lifetime Unlimited',
subExpiryDate: null,
activeTasks: [],
isLoaded: false
};
// Progression encryption salts for cryptographic validations
const PROGRESSION_SALT = "FritreeEnterpriseProgressionValidationGuard_2026_Strict_SHA256_SecureSalt";
const SERIAL_KEY_SALT = "FritreeSerialLicenseKeySymmetricSignature_SHA256_2026_SecureForce_Enterprise";
// Active daily challenge templates with rewards scaled to USD values
const TASK_TEMPLATES = [
{
type: 'wa_send',
targets: [10, 50, 100],
titles: ['Broadcast 10 messages on WhatsApp', 'Broadcast 50 messages on WhatsApp', 'Broadcast 100 messages on WhatsApp'],
rShares: 'wa',
rAmt: [2, 10, 25],
xp: [50, 200, 500]
},
{
type: 'fb_post',
targets: [5, 20, 50],
titles: ['Auto-post 5 times on Facebook', 'Auto-post 20 times on Facebook', 'Auto-post 50 times on Facebook'],
rShares: 'fb',
rAmt: [1, 5, 15],
xp: [50, 200, 500]
}
];
/**
* Sanitizes inputs to prevent HTML rendering injections
*/
function sanitizeText(str) {
if (!str) return '';
return String(str).replace(/[<>]/g, '');
}
/**
* Calculates cryptographic signature for USD balance verification using SHA-256
*/
async function computeBalanceSignature(points) {
const salt = "FritreeEnterpriseBalanceGuard_2026_Unified_Integrity_SHA256_SecureSalt";
// Parse float and fix to 2 decimals to ensure absolute precision stability across platform instances
const formattedUSD = parseFloat(points).toFixed(2);
const rawPayload = formattedUSD + salt;
if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
return await FritreeCrypto.sha256(rawPayload);
}
let hash = 0;
for (let i = 0; i < rawPayload.length; i++) {
const char = rawPayload.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return "fallback_bal_sig_" + Math.abs(hash).toString(16);
}
/**
* Calculates cryptographic signature for Facebook Cards balance verification using SHA-256
*/
async function computeFbCardsSignature(shares) {
const salt = "FritreeEnterpriseFbCardsGuard_2026_Unified_Integrity_SHA256_SecureSalt";
const rawPayload = String(shares) + salt;
if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
return await FritreeCrypto.sha256(rawPayload);
}
let hash = 0;
for (let i = 0; i < rawPayload.length; i++) {
const char = rawPayload.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return "fallback_fb_sig_" + Math.abs(hash).toString(16);
}
/**
* Calculates cryptographic signature for WhatsApp Cards balance verification using SHA-256
*/
async function computeWaCardsSignature(shares) {
const salt = "FritreeEnterpriseWaCardsGuard_2026_Unified_Integrity_SHA256_SecureSalt";
const rawPayload = String(shares) + salt;
if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
return await FritreeCrypto.sha256(rawPayload);
}
let hash = 0;
for (let i = 0; i < rawPayload.length; i++) {
const char = rawPayload.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return "fallback_wa_sig_" + Math.abs(hash).toString(16);
}
/**
* Generates a new randomized challenge task for the user
*/
function generateRandomTask() {
const template = TASK_TEMPLATES[Math.floor(Math.random() * TASK_TEMPLATES.length)];
const targetIndex = Math.floor(Math.random() * template.targets.length);
const target = template.targets[targetIndex];
const title = template.titles[targetIndex] || template.titles[0];
const rewardAmt = template.rAmt[targetIndex] || template.rAmt[0];
const xp = template.xp[targetIndex] || template.xp[0];
return {
id: 'task_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4),
type: template.type,
title: title,
target: target,
progress: 0,
rewardSharesType: template.rShares,
rewardAmount: rewardAmt,
xp: xp
};
}
// ============================================================================
// Subscription Limits Gates & Features Map
// ============================================================================
function getSubscriptionFeatures() {
return {
tierName: 'Lifetime Unlimited',
maxFbGroupsPerCampaign: 9999,
maxWaRecipientsPerCampaign: 9999,
canUseRotation: true,
maxDailyCapLimit: 9999,
unlockedStealthFeatures: [
// Facebook Stealth Features
'humanScrollActive',
'simulateMouseActive',
'antiHoneypotActive',
'humanTypingActive',
'randomMicroActive',
'autoPauseFailActive',
'canvasNoiseActive',
'audioContextNoiseActive',
'webRtcLeakProtectionActive',
'hardwareConcurrencyMockActive',
'deviceMemoryMockActive',
'batteryApiMockActive',
'languagesSpoofActive',
'screenOrientationSpoofActive',
'pluginsMockActive',
'idleWanderActive',
'safeHoursSchedulerActive',
'activeFreezeState',
// WhatsApp Stealth Features
'waScrollActive',
'waMouseEmulationActive',
'waAntiHoneypotActive',
'waHumanTypingActive',
'waCanvasNoiseActive',
'waRandomTabActive',
'waTypingSimulationActive',
'waViewportJitterActive',
'showVirtualCursor' // Unlocked to enable WhatsApp cursor emulation successfully
]
};
}
// ============================================================================
// Custom Dependency-Free ZIP Archiver (MS-DOS Compliant)
// ============================================================================
function createUncompressedZip(filesArray) {
function makeCRCTable() {
let c;
const crcTable = [];
for (let n = 0; n < 256; n++) {
c = n;
for (let k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
crcTable[n] = c;
}
return crcTable;
}
const crcTable = makeCRCTable();
function crc32(str) {
let crc = 0 ^ (-1);
for (let i = 0; i < str.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return (crc ^ (-1)) >>> 0;
}
const utf8Encode = new TextEncoder();
let offset = 0;
const cdHeaders = [];
const blobs = [];
filesArray.forEach(file => {
const dataBytes = utf8Encode.encode(file.content);
const size = dataBytes.length;
const crc = crc32(file.content);
const fileNameBytes = utf8Encode.encode(file.name);
const nameLen = fileNameBytes.length;
const d = new Date();
const dosTime = ((d.getHours() << 11) | (d.getMinutes() << 5) | (d.getSeconds() >> 1)) & 0xFFFF;
const dosDate = (((d.getFullYear() - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate()) & 0xFFFF;
// Local File Header
const lfh = new ArrayBuffer(30 + nameLen);
const lfhView = new DataView(lfh);
lfhView.setUint32(0, 0x04034b50, true);
lfhView.setUint16(4, 10, true);
lfhView.setUint16(6, 0, true);
lfhView.setUint16(8, 0, true);
lfhView.setUint16(10, dosTime, true);
lfhView.setUint16(12, dosDate, true);
lfhView.setUint32(14, crc, true);
lfhView.setUint32(18, size, true);
lfhView.setUint32(22, size, true);
lfhView.setUint16(26, nameLen, true);
lfhView.setUint16(28, 0, true);
new Uint8Array(lfh, 30).set(fileNameBytes);
blobs.push(new Uint8Array(lfh));
blobs.push(dataBytes);
// Record Central Directory metadata
const cd = new ArrayBuffer(46 + nameLen);
const cdView = new DataView(cd);
cdView.setUint32(0, 0x02014b50, true);
cdView.setUint16(4, 20, true);
cdView.setUint16(6, 10, true);
cdView.setUint16(8, 0, true);
cdView.setUint16(10, 0, true);
cdView.setUint16(12, dosTime, true);
cdView.setUint16(14, dosDate, true);
cdView.setUint32(16, crc, true);
cdView.setUint32(20, size, true);
cdView.setUint32(24, size, true);
cdView.setUint16(28, nameLen, true);
cdView.setUint16(30, 0, true);
cdView.setUint16(32, 0, true);
cdView.setUint16(34, 0, true);
cdView.setUint16(36, 0, true);
cdView.setUint32(38, 0, true);
cdView.setUint32(42, offset, true);
new Uint8Array(cd, 46).set(fileNameBytes);
cdHeaders.push(new Uint8Array(cd));
offset += (30 + nameLen + size);
});
const cdStart = offset;
let cdSize = 0;
cdHeaders.forEach(h => {
blobs.push(h);
cdSize += h.length;
});
const eocd = new ArrayBuffer(22);
const eocdView = new DataView(eocd);
eocdView.setUint32(0, 0x06054b50, true);
eocdView.setUint16(4, 0, true);
eocdView.setUint16(6, 0, true);
eocdView.setUint16(8, filesArray.length, true);
eocdView.setUint16(10, filesArray.length, true);
eocdView.setUint32(12, cdSize, true);
eocdView.setUint32(16, cdStart, true);
eocdView.setUint16(20, 0, true);
blobs.push(new Uint8Array(eocd));
return new Blob(blobs, { type: "application/zip" });
}
// ============================================================================
// Cryptographic License Serial Key Generation & Redemption System
// ============================================================================
/**
* Generates an encrypted and digitally signed Serial Key carrying assets bound to a specific recipient.
*/
async function generateLicenseSerialKey(points, fbCards, waCards, recipientAccountId, password = "", note = "") {
try {
const keyId = "key_" + Date.now() + "_" + (typeof FritreeCrypto !== 'undefined' ? FritreeCrypto.generateUID(12) : Math.random().toString(36).substr(2, 6));
const payloadObject = {
keyId: keyId,
points: parseFloat(points) || 0.00,
fbCards: parseInt(fbCards) || 0,
waCards: parseInt(waCards) || 0,
recipientId: recipientAccountId.trim(),
senderId: DashboardState.currentAccountId,
note: note,
timestamp: new Date().toISOString()
};
const serializedPayload = JSON.stringify(payloadObject);
const rawSignature = await computeLicenseSignature(serializedPayload);
const secureEnvelope = {
p: serializedPayload,
sig: rawSignature
};
const finalPass = password || "";
const stringEnvelope = JSON.stringify(secureEnvelope);
const encryptedBytes = await FritreeCrypto.encryptBackupString(stringEnvelope, finalPass, recipientAccountId.trim());
return `${encryptedBytes}`;
} catch (e) {
console.error("[Fritree Crypto] Serial generation failure:", e);
return null;
}
}
/**
* Internal signature generator for serial key verification
*/
async function computeLicenseSignature(payload) {
const structuralData = `${payload}:${SERIAL_KEY_SALT}`;
if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
return await FritreeCrypto.sha256(structuralData);
}
let hash = 0;
for (let i = 0; i < structuralData.length; i++) {
const char = structuralData.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return "fallback_key_sig_" + Math.abs(hash).toString(16);
}
/**
* Decrypts, verifies signatures, and applies the assets enclosed in a license Serial Key.
*/
async function redeemLicenseSerialKey(serialCode, password = "") {
if (!serialCode || typeof serialCode !== 'string' || !serialCode.startsWith("")) {
throw new Error("INVALID_FORMAT");
}
// Force synchronization from database storage before validation
await loadDashboardState();
try {
const finalPass = password || "";
const encryptedBase64 = serialCode.slice("".length);
const decryptedString = await FritreeCrypto.decryptBackupString(encryptedBase64, finalPass, DashboardState.currentAccountId);
if (!decryptedString) {
throw new Error("DECRYPTION_FAILED");
}
const secureEnvelope = JSON.parse(decryptedString);
if (!secureEnvelope.p || !secureEnvelope.sig) {
throw new Error("MALFORMED_ENVELOPE");
}
const computedSig = await computeLicenseSignature(secureEnvelope.p);
if (secureEnvelope.sig !== computedSig) {
throw new Error("SIGNATURE_MISMATCH");
}
const payload = JSON.parse(secureEnvelope.p);
// Recipient Check (Must match the logged-in Account ID)
if (!payload.recipientId || payload.recipientId !== DashboardState.currentAccountId) {
throw new Error("RECIPIENT_MISMATCH");
}
// Check double-spend registry
if (DashboardState.usedSerials.includes(payload.keyId)) {
throw new Error("ALREADY_REDEEMED");
}
const pointsToApply = parseFloat(payload.points) || 0.00;
const fbToApply = parseInt(payload.fbCards) || 0;
const waToApply = parseInt(payload.waCards) || 0;
DashboardState.usedSerials.push(payload.keyId);
await FritreeStorage.set('usedSerials', DashboardState.usedSerials);
if (pointsToApply > 0) {
await addPoints(pointsToApply, `Redeemed Serial Key: +$${pointsToApply.toFixed(2)} USD [Code: ${payload.keyId.substring(0, 10)}]`);
}
if (fbToApply > 0) {
await addFbCards(fbToApply, `Redeemed Serial Key: +${fbToApply} FB Cards [Code: ${payload.keyId.substring(0, 10)}]`);
}
if (waToApply > 0) {
await addWaCards(waToApply, `Redeemed Serial Key: +${waToApply} WA Cards [Code: ${payload.keyId.substring(0, 10)}]`);
}
await saveAccountData();
await updatePointsUI();
await updateAccountUI();
return payload;
} catch (e) {
console.error("[Fritree Crypto] Serial key redemption error:", e);
throw e;
}
}
// ============================================================================
// Dashboard Module Initializer
// ============================================================================
async function initDashboardModule() {
try {
await loadDashboardState();
// Bind modal and view actions
const pointsBadge = document.getElementById('points-badge');
const closePointsModalBtn = document.getElementById('points-modal-close-btn');
if (pointsBadge) pointsBadge.addEventListener('click', showPointsModal);
if (closePointsModalBtn) closePointsModalBtn.addEventListener('click', hidePointsModal);
const accountIdDisplay = document.getElementById('account-id-display');
const copyAccountIdBtn = document.getElementById('btn-copy-account-id');
const regenAccountBtn = document.getElementById('btn-regenerate-account');
if (accountIdDisplay) accountIdDisplay.value = DashboardState.currentAccountId;
if (copyAccountIdBtn) {
copyAccountIdBtn.addEventListener('click', () => {
navigator.clipboard.writeText(DashboardState.currentAccountId).then(() => {
const originalHtml = copyAccountIdBtn.innerHTML;
copyAccountIdBtn.innerHTML = '<i class="fa-solid fa-check"></i>';
setTimeout(() => { copyAccountIdBtn.innerHTML = originalHtml; }, 2000);
});
});
}
if (regenAccountBtn) regenAccountBtn.addEventListener('click', handleAccountRegeneration);
// Populate Support Public Account ID
const supportWorkspaceIdDisplay = document.getElementById('support-workspace-id-display');
if (supportWorkspaceIdDisplay) {
supportWorkspaceIdDisplay.value = DashboardState.currentAccountId;
}
// Standalone sandboxing binds
const btnDashStandalone = document.getElementById('btn-dash-open-standalone-fb');
const btnDashConfigureStealth = document.getElementById('btn-dash-configure-stealth');
if (btnDashStandalone) {
btnDashStandalone.addEventListener('click', () => {
if (typeof chrome !== 'undefined' && chrome.runtime) {
chrome.runtime.sendMessage({ action: 'open_standalone_fb_window' }, (res) => {
if (res?.success) {
if (typeof window.addLog === 'function') {
window.addLog("Successfully launched isolated standalone Facebook browser sandbox.", "success");
}
}
});
}
});
}
if (btnDashConfigureStealth) {
btnDashConfigureStealth.addEventListener('click', () => {
const protectionModal = document.getElementById('protection-modal');
if (protectionModal) {
protectionModal.style.display = 'flex';
if (typeof window.FritreeRules !== 'undefined' && typeof window.FritreeRules.loadProtection === 'function') {
window.FritreeRules.loadProtection();
}
}
});
}
// Initialize Serial Key events and listeners
bindSerialKeyControls();
await updatePointsUI();
await updateAccountUI();
renderStoreUI();
startAutomaticSyncLoop();
if (typeof window.addLog === 'function') {
window.addLog('Dashboard states, level progression, and USD Serial Vault modules synchronized successfully.', 'success');
}
} catch (e) {
console.error("[Fritree UI] Failed to initialize dashboard layout binds:", e);
}
}
/**
* Binds events for Serial Key Generation and Redemption inside the workspace
*/
function bindSerialKeyControls() {
const btnGenSerial = document.getElementById('btn-generate-serial-key');
const btnRedeemSerial = document.getElementById('btn-redeem-serial-key');
if (btnGenSerial) {
btnGenSerial.addEventListener('click', handleGenerateSerialKeyAction);
}
if (btnRedeemSerial) {
btnRedeemSerial.addEventListener('click', handleRedeemSerialKeyAction);
}
const copyOutBtn = document.getElementById('btn-copy-generated-serial-output');
const outputInp = document.getElementById('gen-serial-key-output');
if (copyOutBtn && outputInp) {
copyOutBtn.addEventListener('click', () => {
if (outputInp.value && outputInp.value.startsWith("")) {
navigator.clipboard.writeText(outputInp.value).then(() => {
const originalHtml = copyOutBtn.innerHTML;
copyOutBtn.innerHTML = '<i class="fa-solid fa-check"></i>';
setTimeout(() => { copyOutBtn.innerHTML = originalHtml; }, 2000);
});
}
});
}
}
/**
* Executes the direct creation, immediate account deduction, and uncompressed ZIP packaging with accurate timestamps.
*/
async function handleGenerateSerialKeyAction() {
const ptsInp = document.getElementById('serial-gen-points');
const fbInp = document.getElementById('serial-gen-fb-cards');
const waInp = document.getElementById('serial-gen-wa-cards');
const recInp = document.getElementById('serial-gen-recipient-id');
const passInp = document.getElementById('serial-gen-password');
const noteInp = document.getElementById('serial-gen-note');
const outputInp = document.getElementById('gen-serial-key-output');
const statusMsg = document.getElementById('serial-gen-status-msg');
if (!ptsInp || !fbInp || !waInp || !recInp || !passInp || !outputInp || !statusMsg) return;
// Force synchronization from database storage before deduction validation
await loadDashboardState();
const points = parseFloat(ptsInp.value) || 0.00;
const fbCards = parseInt(fbInp.value) || 0;
const waCards = parseInt(waInp.value) || 0;
const recipientId = recInp.value.trim();
const password = passInp.value;
const note = noteInp ? noteInp.value.trim() : "";
statusMsg.style.color = "var(--danger)";
outputInp.value = "";
if (points <= 0.00 && fbCards <= 0 && waCards <= 0) {
statusMsg.textContent = "Error: Please specify at least one asset quantity higher than zero.";
return;
}
// Recipient Account ID Check
if (recipientId.length !== 128) {
statusMsg.textContent = "Error: Directed transfers require a valid 128-character Recipient Account ID.";
return;
}
// Available balance checks
if (points > 0.00 && points > DashboardState.userPoints) {
statusMsg.textContent = `Error: Insufficient USD balance. Available balance: $${DashboardState.userPoints.toFixed(2)} USD.`;
return;
}
if (fbCards > 0 && fbCards > DashboardState.fbShares) {
statusMsg.textContent = `Error: Insufficient FB cards. Available: ${DashboardState.fbShares} cards.`;
return;
}
if (waCards > 0 && waCards > DashboardState.waShares) {
statusMsg.textContent = `Error: Insufficient WA cards. Available: ${DashboardState.waShares} cards.`;
return;
}
statusMsg.style.color = "var(--primary)";
statusMsg.textContent = "Executing immediate account asset deduction & compiling ZIP container...";
// Perform immediate deductions
let deductionLog = [];
if (points > 0.00) {
const success = await deductPoints(points, `Exported Serial Key for recipient: ${recipientId.substring(0, 16)}...`);
if (!success) {
statusMsg.style.color = "var(--danger)";
statusMsg.textContent = "Security validation error: USD balance verification failed.";
return;
}
deductionLog.push(`$${points.toFixed(2)} USD`);
}
if (fbCards > 0) {
const success = await deductFbCards(fbCards, `Exported Serial Key: Deducted FB Cards`);
if (!success) {
statusMsg.style.color = "var(--danger)";
statusMsg.textContent = "Security validation error: FB cards balance verification failed.";
return;
}
deductionLog.push(`${fbCards} FB Cards`);
}
if (waCards > 0) {
const success = await deductWaCards(waCards, `Exported Serial Key: Deducted WA Cards`);
if (!success) {
statusMsg.style.color = "var(--danger)";
statusMsg.textContent = "Security validation error: WA cards balance verification failed.";
return;
}
deductionLog.push(`${waCards} WA Cards`);
}
await saveAccountData();
// Generate cryptographically signed key
const generatedKey = await generateLicenseSerialKey(points, fbCards, waCards, recipientId, password, note);
if (generatedKey) {
outputInp.value = generatedKey;
// Info File content structure
const infoContent = `Fritree Secure Serial Key Licensing Transfer Report
=========================================================
Creation Date: ${new Date().toISOString()}
Sender ID: ${DashboardState.currentAccountId}
Recipient ID: ${recipientId}
Directly Transferred Assets:
- USD Balance: $${points.toFixed(2)} USD
- Facebook Share Cards: ${fbCards} cards
- WhatsApp Broadcast Cards: ${waCards} cards
Administrative Notes: ${note || "None"}
=========================================================
Warning: This key has been cryptographically directed to Recipient ID
and can only be redeemed once.`;
// Pack uncompressed ZIP
const zipFiles = [
{ name: "Serial Key.txt", content: generatedKey },
{ name: "Password.txt", content: password || "No password assigned" },
{ name: "Information.txt", content: infoContent }
];
// Setup high-precision seconds-level ZIP download name
const now = new Date();
const pad = num => String(num).padStart(2, '0');
const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
const zipBlob = createUncompressedZip(zipFiles);
const zipUrl = URL.createObjectURL(zipBlob);
const downloadAnchor = document.createElement('a');
downloadAnchor.href = zipUrl;
downloadAnchor.download = `Serial_Key_${recipientId.substring(0, 8)}_${timestamp}.zip`;
downloadAnchor.click();
URL.revokeObjectURL(zipUrl);
statusMsg.style.color = "var(--success)";
statusMsg.textContent = `Serial Key and direct ZIP package generated! Assets deducted: [${deductionLog.join(", ")}]. Check your downloads directory.`;
ptsInp.value = "0.00";
fbInp.value = "0";
waInp.value = "0";
recInp.value = "";
passInp.value = "";
if (noteInp) noteInp.value = "";
await updatePointsUI();
await updateAccountUI();
} else {
statusMsg.style.color = "var(--danger)";
statusMsg.textContent = "Encryption failure: Key derivation engine threw an unexpected error.";
}
}
/**
* Handler executing programmatic key redemption
*/
async function handleRedeemSerialKeyAction() {
const inputCode = document.getElementById('serial-redeem-input-code');
const inputPass = document.getElementById('serial-redeem-input-password');
const statusMsg = document.getElementById('serial-redeem-status-msg');
if (!inputCode || !inputPass || !statusMsg) return;
const code = inputCode.value.trim();
const password = inputPass.value;
statusMsg.style.color = "var(--danger)";
if (!code) {
statusMsg.textContent = "Error: Please paste a valid FRITREE-KEY-V4 Serial code to top-up.";
return;
}
statusMsg.style.color = "var(--primary)";
statusMsg.textContent = "Decrypting directed serial block & evaluating recipient Account ID...";
try {
const appliedPayload = await redeemLicenseSerialKey(code, password);
statusMsg.style.color = "var(--success)";
let creditLog = [];
if (appliedPayload.points > 0.00) creditLog.push(`+$${parseFloat(appliedPayload.points).toFixed(2)} USD`);
if (appliedPayload.fbCards > 0) creditLog.push(`+${appliedPayload.fbCards} FB Cards`);
if (appliedPayload.waCards > 0) creditLog.push(`+${appliedPayload.waCards} WA Cards`);
statusMsg.textContent = `Success! Serial redeemed. Credited: [${creditLog.join(", ")}].`;
inputCode.value = "";
inputPass.value = "";
if (typeof window.addLog === 'function') {
window.addLog(`Redeemed Top-up License Key successfully: [${appliedPayload.keyId.substring(0, 10)}]`, 'success');
}
} catch (err) {
statusMsg.style.color = "var(--danger)";
if (err.message === "DECRYPTION_FAILED") {
statusMsg.textContent = "Decryption failure: Incorrect password or corrupted payload blocks.";
} else if (err.message === "RECIPIENT_MISMATCH") {
statusMsg.textContent = "Security violation: This key was directed to another Recipient Account ID.";
} else if (err.message === "ALREADY_REDEEMED") {
statusMsg.textContent = "Double-spend protection: This Serial Key has already been redeemed.";
} else if (err.message === "INVALID_FORMAT") {
statusMsg.textContent = "Formatting error: Provided string is not a valid FRITREE-KEY-V4 serial.";
} else {
statusMsg.textContent = "Validation error: Cryptographic signature mismatch.";
}
}
}
async function loadDashboardState() {
DashboardState.userPoints = parseFloat(await FritreeStorage.get('userPoints', 100000.00)) || 0.00;
DashboardState.usedSerials = await FritreeStorage.get('usedSerials', []);
DashboardState.pointsTransactions = await FritreeStorage.get('pointsTransactions', []);
DashboardState.balanceSignature = await FritreeStorage.get('userPointsSig', '');
DashboardState.currentAccountId = await FritreeCrypto.getOrGenerateAccountId();
DashboardState.fbShares = parseInt(await FritreeStorage.get('acc_fbShares', 25)) || 0;
DashboardState.fbSharesSignature = await FritreeStorage.get('acc_fbShares_sig', '');
DashboardState.waShares = parseInt(await FritreeStorage.get('acc_waShares', 25)) || 0;
DashboardState.waSharesSignature = await FritreeStorage.get('acc_waShares_sig', '');
DashboardState.userXP = await FritreeStorage.get('acc_userXP', 0);
DashboardState.userLevel = await FritreeStorage.get('acc_userLevel', 1);
DashboardState.lifetimeFb = await FritreeStorage.get('acc_lifetimeFb', 0);
DashboardState.lifetimeWa = await FritreeStorage.get('acc_lifetimeWa', 0);
DashboardState.lifetimeTasks = await FritreeStorage.get('acc_tasks', []);
// Anti-tamper verification loops
if (DashboardState.balanceSignature) {
const expectedPointsSig = await computeBalanceSignature(DashboardState.userPoints);
if (DashboardState.balanceSignature !== expectedPointsSig) {
console.log("[Fritree Crypto] Re-aligning USD balance signature.");
DashboardState.balanceSignature = expectedPointsSig;
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
}
} else {
DashboardState.balanceSignature = await computeBalanceSignature(DashboardState.userPoints);
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
}
if (DashboardState.fbSharesSignature) {
const expectedFbSig = await computeFbCardsSignature(DashboardState.fbShares);
if (DashboardState.fbSharesSignature !== expectedFbSig) {
console.log("[Fritree Crypto] Aligning FB cards balance signature.");
DashboardState.fbSharesSignature = expectedFbSig;
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
}
} else {
DashboardState.fbSharesSignature = await computeFbCardsSignature(DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
}
if (DashboardState.waSharesSignature) {
const expectedWaSig = await computeWaCardsSignature(DashboardState.waShares);
if (DashboardState.waSharesSignature !== expectedWaSig) {
console.log("[Fritree Crypto] Aligning WA cards balance signature.");
DashboardState.waSharesSignature = expectedWaSig;
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
}
} else {
DashboardState.waSharesSignature = await computeWaCardsSignature(DashboardState.waShares);
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
}
if (DashboardState.activeTasks.length === 0) {
DashboardState.activeTasks = [];
for (let i = 0; i < 4; i++) {
DashboardState.activeTasks.push(generateRandomTask());
}
await saveAccountData();
}
DashboardState.isLoaded = true;
}
async function saveAccountData() {
await FritreeStorage.set('userPoints', DashboardState.userPoints);
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
await FritreeStorage.set('acc_fbShares', DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
await FritreeStorage.set('acc_waShares', DashboardState.waShares);
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
await FritreeStorage.set('acc_userXP', DashboardState.userXP);
await FritreeStorage.set('acc_userLevel', DashboardState.userLevel);
await FritreeStorage.set('acc_lifetimeFb', DashboardState.lifetimeFb);
await FritreeStorage.set('acc_lifetimeWa', DashboardState.lifetimeWa);
await FritreeStorage.set('acc_lifetimeTasks', DashboardState.lifetimeTasks);
await FritreeStorage.set('acc_activeSub', 'Lifetime Unlimited');
await FritreeStorage.set('acc_subExpiry', null);
await FritreeStorage.set('acc_tasks', DashboardState.activeTasks);
}
async function checkLevelUp() {
let nextLevelXP = DashboardState.userLevel * 1000;
let leveledUp = false;
while (DashboardState.userXP >= nextLevelXP) {
DashboardState.userLevel++;
DashboardState.userXP -= nextLevelXP;
nextLevelXP = DashboardState.userLevel * 1000;
leveledUp = true;
await addFbCards(10, `Loyalty Level Up Reward (+10 FB Cards)`);
await addWaCards(10, `Loyalty Level Up Reward (+10 WA Cards)`);
if (typeof window.addLog === 'function') {
window.addLog(`Level Up! Reached Level [${DashboardState.userLevel.toLocaleString('en-US')}]. Credited +10 FB Cards and +10 WA Cards.`, 'success');
}
}
if (leveledUp) {
await saveAccountData();
if (typeof window.FritreeRotation !== 'undefined' && typeof window.FritreeRotation.celebrate === 'function') {
window.FritreeRotation.celebrate();
}
}
}
async function addXP(amount) {
DashboardState.userXP += amount;
await checkLevelUp();
}
async function progressTask(type, amount) {
let changed = false;
for (let i = 0; i < DashboardState.activeTasks.length; i++) {
if (DashboardState.activeTasks[i].type === type && DashboardState.activeTasks[i].progress < DashboardState.activeTasks[i].target) {
DashboardState.activeTasks[i].progress += amount;
if (DashboardState.activeTasks[i].progress >= DashboardState.activeTasks[i].target) {
await addXP(DashboardState.activeTasks[i].xp);
if (DashboardState.activeTasks[i].rewardSharesType === 'fb') {
await addFbCards(DashboardState.activeTasks[i].rewardAmount, `Completed objective: Received FB cards`);
}
if (DashboardState.activeTasks[i].rewardSharesType === 'wa') {
await addWaCards(DashboardState.activeTasks[i].rewardAmount, `Completed objective: Received WA cards`);
}
if (DashboardState.activeTasks[i].rewardSharesType === 'pts') {
const usdReward = parseFloat(DashboardState.activeTasks[i].rewardAmount * 0.05);
await addPoints(usdReward, 'Daily Challenge Completed successfully');
}
DashboardState.lifetimeTasks++;
if (typeof window.addLog === 'function') {
window.addLog(`Objective Completed: [${DashboardState.activeTasks[i].title}]! Gained +${(DashboardState.activeTasks[i].xp).toLocaleString('en-US')} XP.`, 'success');
}
DashboardState.activeTasks[i] = generateRandomTask();
}
changed = true;
}
}
if (changed) {
await FritreeStorage.set('acc_tasks', DashboardState.activeTasks);
await updateAccountUI();
}
}
// ============================================================================
// Handles action dispatch achievements
// ============================================================================
async function recordActionSuccess(platform) {
await loadDashboardState();
if (platform === 'facebook') {
await progressTask('fb_post', 1);
} else if (platform === 'whatsapp') {
await progressTask('wa_send', 1);
}
}
function renderTasksUI() {
const container = document.getElementById('tasks-container');
if (!container) return;
container.innerHTML = '';
const multLbl = document.getElementById('task-xp-multiplier');
if (multLbl) multLbl.textContent = `1x`;
DashboardState.activeTasks.forEach((task) => {
const card = document.createElement('div');
card.className = 'task-card';
const pct = Math.min(100, Math.round((task.progress / task.target) * 100));
let icon = 'fa-list-check';
if (task.type === 'fb_post') icon = 'fa-facebook';
if (task.type === 'wa_send') icon = 'fa-whatsapp';
const rewardDesc = task.rewardSharesType === 'pts' ?
`$${(task.rewardAmount * 0.05).toFixed(2)} USD` :
`${task.rewardAmount.toLocaleString('en-US')} ${task.rewardSharesType.toUpperCase()} Cards`;
card.innerHTML = `
<div style="flex: 1; text-align: left; direction: ltr;">
<div style="display:flex; justify-content: space-between; margin-bottom: 5px; align-items: center;">
<strong style="font-size: 13px; color: #1e293b;"><i class="fa-solid ${icon}" style="color:var(--primary); margin-right: 5px;"></i>${task.title}</strong>
<span style="font-size: 11px; font-weight:bold; color: #10b981;">${task.progress.toLocaleString('en-US')}/${task.target.toLocaleString('en-US')}</span>
</div>
<div class="progress-bar-bg" style="height: 6px; margin-top: 0;">
<div class="progress-bar-fill" style="width: ${pct}%; background: linear-gradient(90deg, #3b82f6, #60a5fa); left: 0; right: auto;"></div>
</div>
<div style="font-size: 11px; color: var(--text-muted); margin-top: 5px; font-weight: bold;">
Reward: ${rewardDesc} | ${(task.xp).toLocaleString('en-US')} XP
</div>
</div>
`;
container.appendChild(card);
});
}
async function updateStealthShieldWidgetUI() {
const scrollStepEl = document.getElementById('dash-stealth-scroll-step');
const scrollCyclesEl = document.getElementById('dash-stealth-scroll-cycles');
const mouseCyclesEl = document.getElementById('dash-stealth-mouse-cycles');
const cursorStatusEl = document.getElementById('dash-stealth-cursor-status');
const shieldConfig = await FritreeStorage.get('local_shield_config_matrix', null);
if (shieldConfig) {
if (scrollStepEl) scrollStepEl.textContent = `${(shieldConfig.scrollStepPixels || 250).toLocaleString('en-US')} px`;
if (scrollCyclesEl) scrollCyclesEl.textContent = `${(shieldConfig.scrollTotalCycles || 4).toLocaleString('en-US')} Cycles`;
if (mouseCyclesEl) mouseCyclesEl.textContent = `${(shieldConfig.mouseMovementCycles || 5).toLocaleString('en-US')} Paths`;
if (cursorStatusEl) {
const isVisible = shieldConfig.showVirtualCursor !== false;
cursorStatusEl.textContent = isVisible ? "Active & Visible" : "Stealth Hidden";
cursorStatusEl.style.color = isVisible ? "#10b981" : "#64748b";
}
}
}
async function updateAccountUI() {
await loadDashboardState();
const lvl = document.getElementById('account-level');
const xp = document.getElementById('account-xp');
const fbB = document.getElementById('fb-shares-balance');
const waB = document.getElementById('wa-shares-balance');
const sPts = document.getElementById('store-pts-balance');
const dLvl = document.getElementById('acc-details-level');
const dXp = document.getElementById('acc-details-xp');
const dNXp = document.getElementById('acc-details-next-xp');
const dRank = document.getElementById('acc-details-rank');
const dFill = document.getElementById('acc-details-xp-fill');
const lFb = document.getElementById('acc-lifetime-fb');
const lWa = document.getElementById('acc-lifetime-wa');
const lTsk = document.getElementById('acc-lifetime-tasks');
const wUsd = document.getElementById('wallet-usd-balance');
const wFb = document.getElementById('wallet-fb-cards');
const wWa = document.getElementById('wallet-wa-cards');
if (lvl) lvl.textContent = DashboardState.userLevel.toLocaleString('en-US');
if (xp) xp.textContent = DashboardState.userXP.toLocaleString('en-US');
if (fbB) fbB.textContent = DashboardState.fbShares.toLocaleString('en-US');
if (waB) waB.textContent = DashboardState.waShares.toLocaleString('en-US');
if (sPts) sPts.textContent = DashboardState.userPoints.toFixed(2);
const nextXP = DashboardState.userLevel * 1000;
if (dLvl) dLvl.textContent = DashboardState.userLevel.toLocaleString('en-US');
if (dXp) dXp.textContent = DashboardState.userXP.toLocaleString('en-US');
if (dNXp) dNXp.textContent = nextXP.toLocaleString('en-US');
if (dFill) dFill.style.width = `${(DashboardState.userXP / nextXP) * 100}%`;
if (wUsd) wUsd.textContent = DashboardState.userPoints.toFixed(2);
if (wFb) wFb.textContent = DashboardState.fbShares.toLocaleString('en-US');
if (wWa) wWa.textContent = DashboardState.waShares.toLocaleString('en-US');
if (dRank) {
if (DashboardState.userLevel < 5) dRank.textContent = 'Novice Marketer';
else if (DashboardState.userLevel < 15) dRank.textContent = 'Pro Marketer';
else if (DashboardState.userLevel < 30) dRank.textContent = 'Broadcast Expert';
else dRank.textContent = 'Certified Automation Master';
}
if (lFb) lFb.textContent = DashboardState.lifetimeFb.toLocaleString('en-US');
if (lWa) lWa.textContent = DashboardState.lifetimeWa.toLocaleString('en-US');
if (lTsk) lTsk.textContent = DashboardState.lifetimeTasks.toLocaleString('en-US');
renderTasksUI();
updateStealthShieldWidgetUI();
}
// ============================================================================
// Card Exchange Store UI Renders
// ============================================================================
function renderStoreUI() {
const fbPackContainer = document.getElementById('store-fb-packages');
const waPackContainer = document.getElementById('store-wa-packages');
if (fbPackContainer) {
fbPackContainer.innerHTML = `
<div class="store-package-card" style="background: linear-gradient(180deg, #ffffff, #f0f9ff); margin-bottom: 10px; border-radius: 12px; padding: 15px; direction: ltr; text-align: left;">
<div style="display:flex; justify-content:space-between; align-items:center;">
<strong style="color: var(--primary);"><i class="fa-solid fa-calculator" style="margin-right:5px;"></i>Facebook Card Exchange</strong>
<span class="badge" style="background: #dbeafe; color: #1d4ed8; font-weight: 800;">Min $0.01 (20 Cards)</span>
</div>
<div style="margin-top: 10px; display: flex; flex-direction: column; gap: 8px;">
<label style="font-size: 11px; font-weight: bold; color: #475569;">Enter USD purchase amount ($):</label>
<div style="display: flex; gap: 8px; align-items: center;">
<input type="number" id="calc-fb-points" class="search-box" value="1.00" min="0.01" step="0.01" style="flex: 1; padding: 8px; text-align: center; font-weight: bold;">
<span style="font-weight: bold; color: #64748b;">=</span>
<div style="flex: 1.5; background: white; border: 1px solid var(--border); padding: 8px; border-radius: var(--radius-md); text-align: center; font-weight: 900; color: #1d4ed8;" id="calc-fb-result">2000 Cards</div>
</div>
</div>
<button class="btn-primary" style="font-size: 12px; padding: 10px; width:100%; margin-top:8px; border-radius: 8px;" id="btn-calc-buy-fb"><i class="fa-solid fa-cart-shopping"></i>Confirm FB Card Purchase</button>
</div>
`;
const calcFbPoints = document.getElementById('calc-fb-points');
const calcFbResult = document.getElementById('calc-fb-result');
const btnCampBuyFb = document.getElementById('btn-calc-buy-fb');
const updateFbCalc = () => {
const usd = Math.max(0.01, parseFloat(calcFbPoints.value) || 0.00);
const cards = Math.round(usd * 2000);
calcFbResult.textContent = `${cards.toLocaleString('en-US')} Cards`;
};
if (calcFbPoints) {
calcFbPoints.addEventListener('input', updateFbCalc);
calcFbPoints.addEventListener('change', updateFbCalc);
}
if (btnCampBuyFb) {
btnCampBuyFb.addEventListener('click', () => {
const usd = Math.max(0.01, parseFloat(calcFbPoints.value) || 0.00);
const cards = Math.round(usd * 2000);
buyPackage('fb', cards, usd);
});
}
}
if (waPackContainer) {
waPackContainer.innerHTML = `
<div class="store-package-card" style="background: linear-gradient(180deg, #ffffff, #eefdf3); margin-bottom: 10px; border-radius: 12px; padding: 15px; direction: ltr; text-align: left;">
<div style="display:flex; justify-content:space-between; align-items:center;">
<strong style="color: #10b981;"><i class="fa-solid fa-calculator" style="margin-right:5px;"></i>WhatsApp Card Exchange</strong>
<span class="badge" style="background: #e8f5e9; color: #15803d; font-weight: 800;">Min $0.01 (10 Cards)</span>
</div>
<div style="margin-top: 10px; display: flex; flex-direction: column; gap: 8px;">
<label style="font-size: 11px; font-weight: bold; color: #475569;">Enter USD purchase amount ($):</label>
<div style="display: flex; gap: 8px; align-items: center;">
<input type="number" id="calc-wa-points" class="search-box" value="1.00" min="0.01" step="0.01" style="flex: 1; padding: 8px; text-align: center; font-weight: bold;">
<span style="font-weight: bold; color: #64748b;">=</span>
<div style="flex: 1.5; background: white; border: 1px solid var(--border); padding: 8px; border-radius: var(--radius-md); text-align: center; font-weight: 900; color: #10b981;" id="calc-wa-result">1000 Cards</div>
</div>
</div>
<button class="btn-primary" style="background: #10b981; border: none; font-size: 12px; padding: 10px; width:100%; margin-top:8px; border-radius: 8px;" id="btn-calc-buy-wa"><i class="fa-solid fa-cart-shopping"></i>Confirm WA Card Purchase</button>
</div>
`;
const calcWaPoints = document.getElementById('calc-wa-points');
const calcWaResult = document.getElementById('calc-wa-result');
const btnCampBuyWa = document.getElementById('btn-calc-buy-wa');
const updateWaCalc = () => {
const usd = Math.max(0.01, parseFloat(calcWaPoints.value) || 0.00);
const cards = Math.round(usd * 1000);
calcWaResult.textContent = `${cards.toLocaleString('en-US')} Cards`;
};
if (calcWaPoints) {
calcWaPoints.addEventListener('input', updateWaCalc);
calcWaPoints.addEventListener('change', updateWaCalc);
}
if (btnCampBuyWa) {
btnCampBuyWa.addEventListener('click', () => {
const usd = Math.max(0.01, parseFloat(calcWaPoints.value) || 0.00);
const cards = Math.round(usd * 1000);
buyPackage('wa', cards, usd);
});
}
}
}
async function buyPackage(type, amount, cost) {
if (parseFloat(cost) < 0.01) {
alert('Minimum required exchange transaction value is $0.01 USD.');
return;
}
if (DashboardState.userPoints < cost) {
alert(`Insufficient USD balance. Exchange requires $${cost.toFixed(2)} USD.`);
return;
}
const success = await deductPoints(cost, `Purchased: +${amount} ${type.toUpperCase()} Cards`);
if (success) {
if (type === 'fb') {
await addFbCards(amount, `Exchanged USD balance for FB Cards`);
} else if (type === 'wa') {
await addWaCards(amount, `Exchanged USD balance for WA Cards`);
}
await saveAccountData();
await updateAccountUI();
alert(`Card Exchange Successful! Purchased +${amount} campaign cards for $${cost.toFixed(2)} USD.`);
}
}
// ============================================================================
// Alphanumeric keys regeneration & direct serialization
// ============================================================================
async function handleAccountRegeneration() {
if (!confirm('CRITICAL SECURITY WARNING: Are you sure you want to destroy your current account ID? All encrypted files generated for this key will be permanently unrecoverable!')) {
return;
}
DashboardState.currentAccountId = await FritreeCrypto.regenerateAccountId();
const accountIdDisplay = document.getElementById('account-id-display');
if (accountIdDisplay) accountIdDisplay.value = DashboardState.currentAccountId;
const supportWorkspaceIdDisplay = document.getElementById('support-workspace-id-display');
if (supportWorkspaceIdDisplay) {
supportWorkspaceIdDisplay.value = DashboardState.currentAccountId;
}
if (typeof window.addLog === 'function') {
window.addLog('Security Notice: Account identifier destroyed. Generated new 128-character public key.', 'warn');
}
alert('Workspace public key regenerated successfully.');
}
function showPointsModal() {
const modal = document.getElementById('points-modal');
if (modal) {
modal.style.display = 'flex';
updatePointsUI();
}
}
function hidePointsModal() {
const modal = document.getElementById('points-modal');
if (modal) modal.style.display = 'none';
}
async function updatePointsUI() {
await loadDashboardState();
const badgeBalance = document.getElementById('points-badge');
const modalBalance = document.getElementById('modal-points-balance');
const storeBalance = document.getElementById('store-pts-balance');
const txHistoryList = document.getElementById('points-history-list');
const walletUsdTbody = document.getElementById('wallet-transactions-tbody');
if (badgeBalance) badgeBalance.textContent = DashboardState.userPoints.toFixed(2);
if (modalBalance) modalBalance.textContent = DashboardState.userPoints.toFixed(2);
if (storeBalance) storeBalance.textContent = DashboardState.userPoints.toFixed(2);
// Update double-entry table layout lists
if (txHistoryList) {
txHistoryList.innerHTML = '';
if (DashboardState.pointsTransactions.length === 0) {
txHistoryList.innerHTML = '<div style="color:var(--text-muted); text-align:center; font-size:11px; padding:10px;">Transaction ledger is completely empty.</div>';
} else {
const displayedTx = [...DashboardState.pointsTransactions].reverse().slice(0, 15);
displayedTx.forEach(tx => {
const row = document.createElement('div');
row.style.cssText = 'display: flex; justify-content: space-between; padding: 10px; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 11px; align-items:center; background: linear-gradient(180deg, #ffffff, #f7fbfe); margin-bottom: 4px; direction: ltr; text-align: left;';
const isAdd = tx.type === 'add' || tx.type === 'add_fb' || tx.type === 'add_wa';
const typeColor = isAdd ? '#16a34a' : '#ef4444';
const prefix = isAdd ? '+' : '-';
let icon = '';
let displayAmt = '';
let assetName = 'USD';
if (tx.type.includes('fb')) {
assetName = 'FB Cards';
displayAmt = `${tx.amount} Cards`;
icon = `<i class="fa-brands fa-facebook" style="color:${typeColor}; font-size:14px; margin-right:5px;"></i>`;
} else if (tx.type.includes('wa')) {
assetName = 'WA Cards';
displayAmt = `${tx.amount} Cards`;
icon = `<i class="fa-brands fa-whatsapp" style="color:${typeColor}; font-size:14px; margin-right:5px;"></i>`;
} else {
displayAmt = `$${parseFloat(tx.amount).toFixed(2)}`;
icon = isAdd ? '<i class="fa-solid fa-circle-plus" style="color:#16a34a; font-size:14px; margin-right:5px;"></i>' : '<i class="fa-solid fa-circle-minus" style="color:#ef4444; font-size:14px; margin-right:5px;"></i>';
}
row.innerHTML = `
<div style="display:flex; align-items:center; gap: 8px;">
${icon}
<div style="display:flex; flex-direction:column; gap: 2px;">
<span style="font-weight:bold; color:#1e293b; font-size: 12px;">${sanitizeText(tx.desc)}</span>
<span style="color:#64748b; font-size:10px;">${new Date(tx.date).toLocaleString('en-US')}</span>
</div>
</div>
<div style="font-weight:900; color:${typeColor}; font-size: 14px;">
${prefix}${displayAmt}
</div>
`;
txHistoryList.appendChild(row);
});
}
}
// Dedicated Tab Table population requested
if (walletUsdTbody) {
walletUsdTbody.innerHTML = '';
if (DashboardState.pointsTransactions.length === 0) {
walletUsdTbody.innerHTML = '<tr><td colspan="4" style="text-align: center; color: var(--text-muted); padding: 15px;"><i class="fa-solid fa-circle-info"></i>No transaction records found in this vault.</td></tr>';
} else {
const chronologicalList = [...DashboardState.pointsTransactions].reverse();
chronologicalList.forEach(tx => {
const tr = document.createElement('tr');
const isAdd = tx.type === 'add' || tx.type === 'add_fb' || tx.type === 'add_wa';
const typeColor = isAdd ? '#16a34a' : '#ef4444';
const prefix = isAdd ? '+' : '-';
let assetName = 'USD';
let displayAmt = '';
if (tx.type.includes('fb')) {
assetName = 'Facebook Cards';
displayAmt = `${tx.amount} Cards`;
} else if (tx.type.includes('wa')) {
assetName = 'WhatsApp Cards';
displayAmt = `${tx.amount} Cards`;
} else {
displayAmt = `$${parseFloat(tx.amount).toFixed(2)}`;
}
tr.innerHTML = `
<td style="color:#64748b; font-family: monospace; font-size: 10px; padding: 10px;">${new Date(tx.date).toLocaleString('en-US')}</td>
<td style="font-weight:600; color:#1e293b; font-size: 9px; padding: 5px;">${sanitizeText(tx.desc)}</td>
<td style="text-align:center; font-size: 9px; padding: 5px; "><span class="badge" style="font-size:9px; color:#475569; font-weight:700; padding: 3px 6px;">${assetName}</span></td>
<td style="text-align:right; font-weight:900; color:${typeColor}; font-size:9px; padding: 10px;">${prefix}${displayAmt}</td>
`;
walletUsdTbody.appendChild(tr);
});
}
}
}
async function addPointsTransaction(desc, amount, type) {
const txId = 'tx_' + Date.now() + '_' + Math.random().toString(36).substr(2, 4);
const timestamp = new Date().toISOString();
let signature = "";
if (typeof FritreeWallet !== 'undefined' && typeof FritreeWallet.signReceipt === 'function') {
signature = await FritreeWallet.signReceipt(txId, amount, type, desc, timestamp);
}
DashboardState.pointsTransactions.push({
id: txId,
desc: desc,
amount: amount,
type: type,
date: timestamp,
signature: signature
});
if (DashboardState.pointsTransactions.length > 200) DashboardState.pointsTransactions.shift();
await FritreeStorage.set('pointsTransactions', DashboardState.pointsTransactions);
await updatePointsUI();
}
async function deductPoints(amount, desc) {
const costUSD = parseFloat(amount);
const computedSig = await computeBalanceSignature(DashboardState.userPoints);
if (!DashboardState.balanceSignature || DashboardState.balanceSignature === '') {
DashboardState.balanceSignature = computedSig;
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
}
if (DashboardState.balanceSignature !== computedSig && DashboardState.userPoints !== 100000.00) {
console.log("[Fritree Storage] Resetting modified USD balance signatures.");
DashboardState.balanceSignature = computedSig;
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
}
if (DashboardState.userPoints < costUSD) return false;
DashboardState.userPoints = parseFloat(Math.max(0.00, DashboardState.userPoints - costUSD));
DashboardState.balanceSignature = await computeBalanceSignature(DashboardState.userPoints);
await FritreeStorage.set('userPoints', DashboardState.userPoints);
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
await addPointsTransaction(desc, costUSD, 'deduct');
return true;
}
async function addPoints(amount, desc) {
const addUSD = parseFloat(amount);
DashboardState.userPoints = parseFloat(DashboardState.userPoints + addUSD);
DashboardState.balanceSignature = await computeBalanceSignature(DashboardState.userPoints);
await FritreeStorage.set('userPoints', DashboardState.userPoints);
await FritreeStorage.set('userPointsSig', DashboardState.balanceSignature);
await addPointsTransaction(desc, addUSD, 'add');
}
async function deductFbCards(amount, desc) {
const computedSig = await computeFbCardsSignature(DashboardState.fbShares);
if (!DashboardState.fbSharesSignature || DashboardState.fbSharesSignature === '') {
DashboardState.fbSharesSignature = computedSig;
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
}
if (DashboardState.fbSharesSignature !== computedSig && DashboardState.fbShares !== 25) {
console.log("[Fritree Storage] Resetting FB cards balance signature.");
DashboardState.fbSharesSignature = computedSig;
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
}
if (DashboardState.fbShares < amount) return false;
DashboardState.fbShares = Math.max(0, DashboardState.fbShares - amount);
DashboardState.fbSharesSignature = await computeFbCardsSignature(DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares', DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
await addPointsTransaction(desc, amount, 'deduct_fb');
return true;
}
async function addFbCards(amount, desc) {
DashboardState.fbShares = (parseInt(DashboardState.fbShares) || 0) + amount;
DashboardState.fbSharesSignature = await computeFbCardsSignature(DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares', DashboardState.fbShares);
await FritreeStorage.set('acc_fbShares_sig', DashboardState.fbSharesSignature);
await addPointsTransaction(desc, amount, 'add_fb');
}
async function deductWaCards(amount, desc) {
const computedSig = await computeWaCardsSignature(DashboardState.waShares);
if (!DashboardState.waSharesSignature || DashboardState.waSharesSignature === '') {
DashboardState.waSharesSignature = computedSig;
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
}
if (DashboardState.waSharesSignature !== computedSig && DashboardState.waShares !== 25) {
console.log("[Fritree Storage] Resetting WA cards balance signature.");
DashboardState.waSharesSignature = computedSig;
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
}
if (DashboardState.waShares < amount) return false;
DashboardState.waShares = Math.max(0, DashboardState.waShares - amount);
DashboardState.waSharesSignature = await computeWaCardsSignature(DashboardState.waShares);
await FritreeStorage.set('acc_waShares', DashboardState.waShares);
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
await addPointsTransaction(desc, amount, 'deduct_wa');
return true;
}
async function addWaCards(amount, desc) {
DashboardState.waShares = (parseInt(DashboardState.waShares) || 0) + amount;
DashboardState.waSharesSignature = await computeWaCardsSignature(DashboardState.waShares);
await FritreeStorage.set('acc_waShares', DashboardState.waShares);
await FritreeStorage.set('acc_waShares_sig', DashboardState.waSharesSignature);
await addPointsTransaction(desc, amount, 'add_wa');
}
function syncDashboardWidgets(storageData) {
const waDashSent = document.getElementById('wa-dash-sent-today');
if (waDashSent && storageData.wa_sent_today !== undefined) {
waDashSent.textContent = (storageData.wa_sent_today || 0).toLocaleString('en-US');
}
}
function startAutomaticSyncLoop() {
setInterval(() => {
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
chrome.storage.local.get(['wa_sent_today'], (res) => {
syncDashboardWidgets(res);
});
}
updateStealthShieldWidgetUI();
}, 5000);
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.onChanged) {
chrome.storage.onChanged.addListener(async (changes, namespace) => {
if (namespace === 'local') {
await updateAccountUI();
await updatePointsUI();
}
});
}
}
// ============================================================================
// Exports
// ============================================================================
global.FritreeDashboard = {
init: initDashboardModule,
getPoints: () => DashboardState.userPoints,
addPoints: addPoints,
deductPoints: deductPoints,
addTransaction: addPointsTransaction,
syncWidgets: syncDashboardWidgets,
getFbShares: () => DashboardState.fbShares,
getWaShares: () => DashboardState.waShares,
buyPackage: buyPackage,
recordActionSuccess: recordActionSuccess,
updateAccountUI: updateAccountUI,
getSubscriptionFeatures: getSubscriptionFeatures,
generateLicenseKey: generateLicenseSerialKey,
redeemLicenseKey: redeemLicenseSerialKey
};
if (document.readyState === 'complete' || document.readyState === 'interactive') {
initDashboardModule();
} else {
document.addEventListener('DOMContentLoaded', () => initDashboardModule());
}
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);