Contractless-GUI-Wallet/frontend/js/marketing_form.js

189 lines
6.4 KiB
JavaScript

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