189 lines
6.4 KiB
JavaScript
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"));
|