aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/(app)/sent
Commit message (Expand)AuthorAgeFilesLines
* admin routeAdam Soltys2023-03-161-1/+1
* translations updateAdam Soltys2023-03-131-1/+1
* fix param nameAdam Soltys2023-03-111-1/+0
* prompt for tipAdam Soltys2023-03-111-1/+1
* prompt for tip on internal paymentsAdam Soltys2023-03-111-0/+1
* prettierAdam Soltys2023-02-271-1/+1
* show tips for senderAdam Soltys2023-02-231-7/+19
* potsAdam Soltys2023-01-182-4/+4
* payments migrationAdam Soltys2023-01-132-4/+10
* transaction list stylesAdam Soltys2022-12-151-1/+1
* prettierAdam Soltys2022-12-151-3/+1
* show users involved in voucher creation/redemptionAdam Soltys2022-12-151-0/+4
* style tweaksAdam Soltys2022-11-111-5/+4
* invoice requestsAdam Soltys2022-11-031-2/+2
* move to app layoutAdam Soltys2022-11-021-0/+34
/a> 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
import * as ecc from 'tiny-secp256k1';
import { BIP32Factory } from 'bip32';
import { browser } from '$app/environment';
import { toast } from '@zerodevx/svelte-toast';
import { goto } from '$app/navigation';
import { PUBLIC_COINOS_URL } from '$env/static/public';

export function scroll(section) {
	section.scrollIntoView({ behavior: 'smooth' });
}

const base = browser ? '' : PUBLIC_COINOS_URL;

export const punk = (k) => Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + '.webp';

export const g = (url, fetch, headers) =>
	fetch(base + url, { headers })
		.then((r) => r.text())
		.then((body) => {
			try {
				return JSON.parse(body);
			} catch (e) {
				throw new Error(body);
			}
		});

export const get = (url, headers = {}) =>
	fetch(base + url, { headers })
		.then(async (r) => {
			if (r.ok) return r.text();
			throw new Error(await r.text());
		})
		.then((body) => {
			try {
				return JSON.parse(body);
			} catch (e) {
				return body;
			}
		});

export const post = (url, body, headers) => {
	headers = { ...headers, 'content-type': 'application/json', accept: 'application/json' };
	return fetch(base + url, { method: 'POST', body: JSON.stringify(body), headers })
		.then(async (r) => {
			if (r.ok) return r.text();
			let text = await r.text();

			let message;
			try {
				({ message } = JSON.parse(text));
			} catch (e) {}

			if (message) throw new Error(message);
			throw new Error(text);
		})
		.then((body) => {
			try {
				body = JSON.parse(body);
			} catch (e) {
				throw new Error(body);
			}

			if (body.error instanceof Error) throw body.error;
			if (body instanceof Error) throw body;
			if (body.name === 'Error') throw new Error(body.message);

			return body;
		});
};

export const copy = (text) => {
	navigator.clipboard.writeText(text);
	success('Copied!');
};

export function reverseFormat(val, locale) {
	let parts = new Intl.NumberFormat(locale).formatToParts(1111.1);
	let group = parts.find((part) => part.type === 'group').value;
	let decimal = parts.find((part) => part.type === 'decimal').value;
	let reversedVal = val.replace(new RegExp('\\' + group, 'g'), '');
	reversedVal = reversedVal.replace(new RegExp('\\' + decimal, 'g'), '.');
	return Number.isNaN(reversedVal) ? 0 : +reversedVal;
}

export let protectedRoutes = [
	/customers/,
	/dashboard/,
	/settings/,
	/payments/,
	/\/ln\//,
	/request/
];

export const success = (m) => {
	toast.pop();
	toast.push(m, {
		theme: {
			'--toastBarBackground': '#F5F7FA'
		}
	});
};

export const warning = (m) => {
	toast.pop();
	toast.push(m, {
		theme: {
			'--toastBarBackground': '#FFCE22'
		}
	});
};
export const failure = (m) => {
	toast.pop();
	toast.push(m, {
		theme: {
			'--toastBarBackground': '#E93535'
		}
	});
};
export const info = (m) => {
	toast.pop();
	toast.push(m, {
		theme: {
			'--toastBarBackground': 'white'
		}
	});
};

export const login = async (user, cookies) => {
	let maxAge = 30 * 24 * 60 * 60;

	let res = await fetch(base + '/login', {
		method: 'POST',
		body: JSON.stringify(user),
		headers: { 'content-type': 'application/json', accept: 'application/json' }
	});
	if (res.status === 401) {
		let text = await res.text();
		if (text.startsWith('2fa')) throw new Error('2fa');
	}

	let { token } = await res.json();
	if (!token) throw new Error('Login failed');

	let expires = new Date();
	expires.setSeconds(expires.getSeconds() + maxAge);

	cookies.set('username', user.username, { path: '/', expires });
	cookies.set('token', token, { path: '/', expires });
};

export const auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` });

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

export const s = (s) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(s);
export const sat = (s) => {
	s = Math.abs(s);
	let p = Math.floor(Math.log(s) / Math.LN10 + 0.000000001);
	let d = Math.floor((p + 1) / 3);

	return (
		'⚡️' +
		(parseInt(s) > 0
			? new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 }).format(s / Math.pow(1000, d)) +
			  ['', 'K', 'M', 'G'][d]
			: 0
		).toString()
	);
};

export const sats = 100000000;

export const back = () => browser && history.go(-1);
export const focus = (el) => setTimeout(() => el.focus());

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

export const stretch = async (password, salt) =>
	crypto.subtle.deriveKey(
		{
			name: 'PBKDF2',
			salt,
			iterations: 100000,
			hash: 'SHA-256'
		},
		await crypto.subtle.importKey(
			'raw',
			new TextEncoder().encode(password),
			{ name: 'PBKDF2' },
			false,
			['deriveBits', 'deriveKey']
		),
		{ name: 'AES-GCM', length: 256 },
		true,
		['encrypt', 'decrypt']
	);

export const bip32 = await BIP32Factory(ecc);