aboutsummaryrefslogtreecommitdiffstats
path: root/nwcpush/server.js
diff options
context:
space:
mode:
Diffstat (limited to 'nwcpush/server.js')
0 files changed, 0 insertions, 0 deletions
='#n2'>2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
import config from "$config";
import { g } from "$lib/db";
import locales from "$lib/locales/index";

const { URL } = process.env;

export const fail = (msg) => {
  throw new Error(msg);
};

export const nada = () => {};

export const sleep = (n) => new Promise((r) => setTimeout(r, n));
export const wait = async (f, s = 300, n = 50) => {
  let i = 0;
  while (!(await f()) && i < s) (await sleep(n)) && i++;
  if (i >= s) fail("timeout");
  return f();
};

export const prod = process.env.NODE_ENV === "production";

export const bail = (res, msg) => res.code(500).send(msg);

export const SATS = 100000000;
export const sats = (n) => Math.round(n * SATS);
export const btc = (n) => parseFloat((n / SATS).toFixed(8));
export const fiat = (n, r) => (n * r) / SATS;

export const uniq = (a, k) => [...new Map(a.map((x) => [k(x), x])).values()];
export const pick = (O, K) =>
  K.reduce((o, k) => (typeof O[k] !== "undefined" && (o[k] = O[k]), o), {});

export const bip21 = (address, { amount, memo, tip, type }) => {
  if (!(amount || memo)) return address;

  const network = { liquid: "liquidnetwork", bitcoin: "bitcoin" }[type];
  const url = new URLSearchParams();

  if (amount) {
    url.append("amount", btc(amount + tip).toFixed(8));
    if (type === "liquid") url.append("assetid", config.liquid.btc);
  }

  if (memo) url.append("message", memo);

  return `${network}:${address}?${url.toString()}`;
};

export const getUser = async (username, fields = undefined) => {
  if (username === "undefined") fail("invalid user");
  const k = username?.replace(/\s/g, "").toLowerCase();
  let user = await g(`user:${k}`);
  if (typeof user === "string") user = await g(`user:${user}`);

  return fields && user ? pick(user, fields) : user;
};

export const getInvoice = async (hash) => {
  let iid = await g(`invoice:${hash}`);
  if (iid?.id) iid = iid.id;
  else if (iid?.hash) iid = iid.hash;
  return await g(`invoice:${iid}`);
};

export const getPayment = async (id) => {
  let p = await g(`payment:${id}`);
  if (typeof p === "string") p = await g(`payment:${p}`);
  return p;
};

export const f = (s, currency) =>
  new Intl.NumberFormat("en-US", {
    style: "currency",
    currency,
  })
    .format(s)
    .replace("CA", "");

export function formatReceipt(items, currency) {
  function wrapText(text, maxWidth) {
    const words = text.split(" ");
    const lines = [];
    let currentLine = "";
    for (const word of words) {
      if (currentLine.length + word.length + 1 > maxWidth) {
        lines.push(currentLine);
        currentLine = word;
      } else {
        currentLine += (currentLine.length > 0 ? " " : "") + word;
      }
    }
    if (currentLine) lines.push(currentLine);
    return lines;
  }

  function calculateColumnWidths(items) {
    let maxQuantityLength = 0;
    let maxPriceLength = 0;

    for (const item of items) {
      const quantityLength = String(item.quantity).length;
      if (quantityLength > maxQuantityLength) {
        maxQuantityLength = quantityLength;
      }

      const priceLength = f(item.price * item.quantity, currency).length;
      if (priceLength > maxPriceLength) {
        maxPriceLength = priceLength;
      }
    }

    // Add padding to the widths for aesthetic spacing
    return {
      quantityColumnWidth: maxQuantityLength + 1, // Space after quantity
      priceColumnWidth: maxPriceLength + 1, // Space before price
    };
  }
  const maxLineWidth = 32;
  const { quantityColumnWidth, priceColumnWidth } =
    calculateColumnWidths(items);
  const nameColumnWidth = maxLineWidth - quantityColumnWidth - priceColumnWidth;

  return items
    .map((item) => {
      const quantityStr = String(item.quantity).padEnd(quantityColumnWidth);
      const priceStr = f(item.price * item.quantity, currency).padStart(
        priceColumnWidth,
      );
      const nameLines = wrapText(item.name, nameColumnWidth);

      // Construct the full line(s) with the first line including the price
      const fullLines = [
        `${quantityStr}${nameLines[0].padEnd(nameColumnWidth)}${priceStr}`,
      ];
      // Add any additional name lines, properly indented
      for (let i = 1; i < nameLines.length; i++) {
        fullLines.push(" ".repeat(quantityColumnWidth) + nameLines[i]);
      }

      return fullLines.join("\n");
    })
    .join("\n");
}

export const t = ({ language = "en" }) => locales[language];

export const time = (() => {
  let count = 0;
  let started = false;
  return (s = "") => {
    if (!started) {
      console.time("");
      started = true;
    }
    console.timeLog("", ++count, s);
  };
})();

export const fmt = (sats) =>
  `⚡️${new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(
    sats,
  )}`;

export const link = (id) => `${URL}/payment/${id}`;

export const fields = [
  "about",
  "anon",
  "banner",
  "banner",
  "currency",
  "display",
  "id",
  "hidepay",
  "lud16",
  "memoPrompt",
  "npub",
  "picture",
  "prompt",
  "pubkey",
  "username",
  "website",
];