const MARKETING_FEE_ATOMIC = 100000000n; function setMarketingMessage(message, type = "") { setScopedTransactionMessage("Marketing", marketingMessage, message, type); } function decimalToCents(value) { const text = String(value || "").trim(); if (!/^\d+(\.\d{0,2})?$/.test(text)) { return null; } const [whole, fraction = ""] = text.split("."); const cents = Number.parseInt(whole || "0", 10) * 100 + Number.parseInt(fraction.padEnd(2, "0") || "0", 10); return Number.isInteger(cents) && cents >= 0 && cents <= 65535 ? cents : null; } function updateMarketingDerivedFields() { if (marketingFeeInput) { marketingFeeInput.value = atomicToDecimal(MARKETING_FEE_ATOMIC); } if (marketingAdvertiser) { marketingAdvertiser.textContent = activeWalletAddressShort || activeWalletAddressHex || "--"; } if (marketingFeeLabel) { marketingFeeLabel.textContent = `${atomicToDecimal(MARKETING_FEE_ATOMIC)} ${activeBaseCoin()}`; } } function marketingCountValue(input) { const value = Number.parseInt(input?.value || "0", 10); return Number.isInteger(value) && value >= 0 && value <= 255 ? value : null; } function validateMarketingForm() { if (!walletLoaded) { return "Load a wallet before creating a marketing transaction."; } if (!registrationRegistered) { return "Address registration is required before creating a marketing transaction."; } const campaign = String(marketingCampaignInput?.value || "").trim(); if (!/^\d+$/.test(campaign)) { return "Campaign ID must be a whole number."; } const keyword = String(marketingKeywordInput?.value || "").trim(); if (!keyword) { return "Keyword is required."; } if (!/^[\x20-\x7E]+$/.test(keyword) || keyword.length > 40) { return "Keyword must be ASCII and no longer than 40 characters."; } const displayed = String(marketingDisplayedInput?.value || "").trim(); if (!displayed) { return "Displayed location is required."; } if (!/^[\x20-\x7E]+$/.test(displayed) || displayed.length > 100) { return "Displayed location must be ASCII and no longer than 100 characters."; } if (marketingCountValue(marketingImpressionInput) === null) { return "Impressions must be between 0 and 255."; } if (marketingCountValue(marketingClickInput) === null) { return "Clicks must be between 0 and 255."; } if (decimalToCents(marketingImpressionValueInput?.value) === null) { return "Impression value must be between 0.00 and 655.35."; } if (decimalToCents(marketingClickValueInput?.value) === null) { return "Click value must be between 0.00 and 655.35."; } if (baseCoinBalanceAtomic() < MARKETING_FEE_ATOMIC) { return `Marketing transactions require ${atomicToDecimal(MARKETING_FEE_ATOMIC)} ${activeBaseCoin()} for the fee.`; } return ""; } function marketingPayload() { return { campaign: String(marketingCampaignInput.value || "0").trim(), adType: marketingAdTypeSelect.value, keyword: String(marketingKeywordInput.value || "").trim(), displayed: String(marketingDisplayedInput.value || "").trim(), impression: marketingCountValue(marketingImpressionInput), click: marketingCountValue(marketingClickInput), impressionValue: decimalToCents(marketingImpressionValueInput.value), clickValue: decimalToCents(marketingClickValueInput.value), txfee: String(MARKETING_FEE_ATOMIC) }; } function clearMarketingForm() { marketingCampaignInput.value = ""; marketingAdTypeSelect.value = "banner"; marketingKeywordInput.value = ""; marketingDisplayedInput.value = ""; marketingImpressionInput.value = "0"; marketingClickInput.value = "0"; marketingImpressionValueInput.value = ""; marketingClickValueInput.value = ""; updateMarketingDerivedFields(); } function setMarketingButtonsDisabled(disabled) { document.querySelector("[data-marketing-save]")?.toggleAttribute("disabled", disabled); document.querySelector("[data-marketing-broadcast]")?.toggleAttribute("disabled", disabled); } async function handleMarketingAction(action) { const validationError = validateMarketingForm(); if (validationError) { setMarketingMessage(validationError, "error"); return; } const invoke = window.__TAURI__?.core?.invoke; if (!invoke) { setMarketingMessage("Wallet bridge is not available.", "error"); return; } const walletKey = await requestSigningWalletKey(); if (guiRuntimeSettings.require_key_before_signing !== false && !walletKey) { setMarketingMessage("Signing cancelled."); return; } setMarketingButtonsDisabled(true); try { if (action === "save") { setMarketingMessage("Saving marketing transaction..."); const review = await invoke("save_marketing_transaction", { ...marketingPayload(), walletKey }); clearMarketingForm(); setMarketingMessage(`Marketing transaction saved: ${review.file_name}`, "success"); if (typeof refreshUnbroadcastTransactions === "function") { refreshUnbroadcastTransactions(); } } else { const broadcastNode = activeBroadcastNode(); if (!broadcastNode) { throw new Error("Select a broadcast node before broadcasting."); } setMarketingMessage("Broadcasting marketing transaction..."); const result = await invoke("broadcast_marketing_transaction", { ...marketingPayload(), broadcastNode, walletKey }); clearMarketingForm(); setMarketingMessage(result.response || "Marketing transaction broadcast.", "success"); } } catch (error) { setMarketingMessage(String(error), "error"); } finally { setMarketingButtonsDisabled(false); } } function prepareMarketingForm() { updateMarketingDerivedFields(); setMarketingMessage(""); } function resetMarketingSession() { clearMarketingForm(); setMarketingMessage(""); } [ marketingCampaignInput, marketingAdTypeSelect, marketingKeywordInput, marketingDisplayedInput, marketingImpressionInput, marketingClickInput, marketingImpressionValueInput, marketingClickValueInput ].forEach((element) => { element?.addEventListener("input", () => setMarketingMessage("")); element?.addEventListener("change", () => setMarketingMessage("")); }); document .querySelector("[data-marketing-save]") ?.addEventListener("click", () => handleMarketingAction("save")); document .querySelector("[data-marketing-broadcast]") ?.addEventListener("click", () => handleMarketingAction("broadcast"));