aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-03-17 20:28:00 -0700
committerAdam Soltys <[email protected]>2023-03-17 20:28:00 -0700
commit93f23786af35bbe932bc849c8a301d5af1b82d58 (patch)
treed7d6d408fb34ec1892b67aa56398cffe726d0eab /src
parentc4757b998cd9be2db18ac1805557bdf175fc86c3 (diff)
parentcdc3a2677c8728af27084024daba288153c2bd52 (diff)
downloadcoinos-ui-93f23786af35bbe932bc849c8a301d5af1b82d58.tar.gz
coinos-ui-93f23786af35bbe932bc849c8a301d5af1b82d58.zip
Merge branch 'master' into hashme
Diffstat (limited to 'src')
-rw-r--r--src/app.css1
-rw-r--r--src/components/About.svelte17
-rw-r--r--src/components/AppHeader.svelte1
-rw-r--r--src/components/Code.svelte29
-rw-r--r--src/components/Footer.svelte4
-rw-r--r--src/components/LandingHeader.svelte26
-rw-r--r--src/components/Numpad.svelte2
-rw-r--r--src/components/index.js1
-rw-r--r--src/lib/samples.js23
-rw-r--r--src/lib/translations/index.js5
-rw-r--r--src/lib/translations/lang.json3
-rw-r--r--src/locales/en.json28
-rw-r--r--src/locales/es.json29
-rw-r--r--src/locales/pt.json384
-rw-r--r--src/locales/zh.json291
-rw-r--r--src/routes/(app)/+layout.server.js3
-rw-r--r--src/routes/(app)/+layout.svelte1
-rw-r--r--src/routes/(app)/[username]/(profile)/+layout.svelte10
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.server.js38
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte2
-rw-r--r--src/routes/(app)/[username]/(profile)/followers/+page.svelte5
-rw-r--r--src/routes/(app)/[username]/(profile)/follows/+page.svelte5
-rw-r--r--src/routes/(app)/[username]/dashboard/+page.svelte1
-rw-r--r--src/routes/(app)/[username]/invoice/+server.js6
-rw-r--r--src/routes/(app)/[username]/invoice/[hash]/+layout.server.js2
-rw-r--r--src/routes/(app)/[username]/invoice/[hash]/+page.svelte36
-rw-r--r--src/routes/(app)/[username]/invoice/[hash]/paid/+page.svelte6
-rw-r--r--src/routes/(app)/[username]/invoice/[hash]/tip/+page.svelte4
-rw-r--r--src/routes/(app)/[username]/messages/+page.server.js2
-rw-r--r--src/routes/(app)/[username]/messages/+page.svelte36
-rw-r--r--src/routes/(app)/[username]/payments/[...page]/+page.svelte34
-rw-r--r--src/routes/(app)/[username]/settings/[...tab]/+page.svelte19
-rw-r--r--src/routes/(app)/admin/+layout.server.js11
-rw-r--r--src/routes/(app)/admin/+layout.svelte10
-rw-r--r--src/routes/(app)/admin/+page.server.js10
-rw-r--r--src/routes/(app)/admin/+page.svelte11
-rw-r--r--src/routes/(app)/launch/thanks/[asset]/+page.server.js1
-rw-r--r--src/routes/(app)/payment/[id]/+page.svelte54
-rw-r--r--src/routes/(app)/send/[...text]/+page.svelte1
-rw-r--r--src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte10
-rw-r--r--src/routes/(app)/send/invoice/[id]/+page.server.js25
-rw-r--r--src/routes/(app)/send/invoice/[id]/+page.svelte24
-rw-r--r--src/routes/(app)/send/lightning/[payreq]/+page.server.js2
-rw-r--r--src/routes/(app)/send/user/[username]/[...amount]/+page.server.js5
-rw-r--r--src/routes/(app)/send/user/[username]/[...amount]/+page.svelte26
-rw-r--r--src/routes/(app)/sent/+page.svelte4
-rw-r--r--src/routes/(app)/support/+page.server.js15
-rw-r--r--src/routes/(app)/support/+page.svelte57
-rw-r--r--src/routes/(app)/support/+server.js9
-rw-r--r--src/routes/(static)/+layout.server.js16
-rw-r--r--src/routes/(static)/+layout.svelte6
-rw-r--r--src/routes/(static)/bounties/+page.svelte7
-rw-r--r--src/routes/(static)/docs/+page.server.js4
-rw-r--r--src/routes/(static)/docs/+page.svelte92
-rw-r--r--src/routes/+page.server.js2
55 files changed, 982 insertions, 474 deletions
diff --git a/src/app.css b/src/app.css
index 12fad732..3a722fb1 100644
--- a/src/app.css
+++ b/src/app.css
@@ -35,7 +35,6 @@ button.primary {
-webkit-appearance: none;
-moz-appearance: none;
background: transparent;
- background-image: url('/icons/down.svg');
background-repeat: no-repeat;
background-position-x: 95%;
background-position-y: 1em;
diff --git a/src/components/About.svelte b/src/components/About.svelte
index 0a0ff874..4292b9fd 100644
--- a/src/components/About.svelte
+++ b/src/components/About.svelte
@@ -12,22 +12,7 @@
{ type: 'github', url: 'https://github.com/asoltys', color: 'bg-gray-800' }
]
},
- { name: 'cole', socials: [] },
- {
- name: 'john',
- socials: []
- },
- {
- name: 'shauna',
- socials: [
- { type: 'twitter', url: 'https://twitter.com/ShaunaCollister', color: 'bg-[#1D9BF0]' },
- {
- type: 'linkedin',
- url: 'https://www.linkedin.com/in/shauna-collister-32905214',
- color: 'bg-[#0a66c2]'
- }
- ]
- }
+ { name: 'cole', socials: [] }
];
</script>
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index 74afd7b9..51c70851 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -15,7 +15,6 @@
let menuButtons;
if (user)
menuButtons = [
- { key: 'nav.profile', icon: 'profile', href: `/${user.username}` },
{ key: 'nav.settings', icon: 'settings', href: `/${user.username}/settings` },
{ key: 'nav.support', icon: 'support', href: `/support` },
{ key: 'nav.signOut', icon: 'logout', href: `/logout` }
diff --git a/src/components/Code.svelte b/src/components/Code.svelte
new file mode 100644
index 00000000..a718c5ed
--- /dev/null
+++ b/src/components/Code.svelte
@@ -0,0 +1,29 @@
+<script>
+ import { copy } from '$lib/utils';
+ import { Icon } from '$comp';
+ import samples from '$lib/samples';
+ export let sample;
+</script>
+
+<div class="bg-black text-white rounded-lg p-4 flex gap-4 whitespace-pre-wrap font-mono">
+ <div class="overflow-x-scroll max-w-full scrollbar-hide">
+ <div class="break-all min-w-[800px]">{samples[sample]}</div>
+ </div>
+ <button
+ class="ml-auto my-auto invert opacity-90"
+ on:click={() => copy(samples[sample].replace(/^\s+/gm, '').replace(/\n/g, ''))}
+ ><Icon icon="copy" style="w-10 max-w-none" /></button
+ >
+</div>
+
+<style>
+ .scrollbar-hide::-webkit-scrollbar {
+ display: none;
+ }
+
+ /* For IE, Edge and Firefox */
+ .scrollbar-hide {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+ }
+</style>
diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte
index a33716d4..7520e5d4 100644
--- a/src/components/Footer.svelte
+++ b/src/components/Footer.svelte
@@ -4,8 +4,8 @@
const followLinks = [
{ url: 'https://twitter.com/coinoswallet', titleID: 'twitter' },
- { url: 'https://github.com/coinos', titleID: 'github' },
- { url: 'https://t.me/coinoswallet', titleID: 'telegram' }
+ { url: 'https://t.me/coinoswallet', titleID: 'telegram' },
+ { url: 'https://linktr.ee/coinos.io', titleID: 'linktree' }
];
const companyLinks = [
diff --git a/src/components/LandingHeader.svelte b/src/components/LandingHeader.svelte
index f9db5ea0..ed7907c1 100644
--- a/src/components/LandingHeader.svelte
+++ b/src/components/LandingHeader.svelte
@@ -40,29 +40,29 @@
<button class="hover:opacity-80" on:click={() => scroll(about)}>{$t('about.header')}</button
>
{/if}
- {#if !user}
+ {#if user}
<button
- class="border rounded-full px-6 py-2 font-bold hover:opacity-80"
- on:click={() => goto('/register')}
- >{$t('nav.startInSeconds')}
+ class="border rounded-full px-6 py-2 font-bold"
+ on:click={() => goto(`/${user.username}`)}
+ >Home
</button>
<button
- class="bg-black text-white border rounded-full px-6 py-2 font-bold hover:opacity-80"
- on:click={() => goto('/login')}
+ class="bg-black text-white border rounded-full px-6 py-2 font-bold"
+ on:click={() => goto('/logout')}
>
- {$t('nav.signIn')}
+ {$t('nav.signOut')}
</button>
{:else}
<button
- class="border rounded-full px-6 py-2 font-bold"
- on:click={() => goto(`/${user.username}`)}
- >{$t('nav.account')}
+ class="border rounded-full px-6 py-2 font-bold hover:opacity-80"
+ on:click={() => goto('/register')}
+ >{$t('nav.startInSeconds')}
</button>
<button
- class="bg-black text-white border rounded-full px-6 py-2 font-bold"
- on:click={() => goto('/logout')}
+ class="bg-black text-white border rounded-full px-6 py-2 font-bold hover:opacity-80"
+ on:click={() => goto('/login')}
>
- {$t('nav.signOut')}
+ {$t('nav.signIn')}
</button>
{/if}
</div>
diff --git a/src/components/Numpad.svelte b/src/components/Numpad.svelte
index 47bd9375..77a4f38b 100644
--- a/src/components/Numpad.svelte
+++ b/src/components/Numpad.svelte
@@ -9,7 +9,7 @@
fiat = !amount,
submit = undefined;
- let amountFiat = 0;
+ export let amountFiat = 0;
let arrow = '<';
$: update(amount, amountFiat);
diff --git a/src/components/index.js b/src/components/index.js
index 0d1d8d1d..1853e7c9 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1,3 +1,4 @@
+export { default as Code } from './Code.svelte';
export { default as Slider } from './Slider.svelte';
export { default as Pinpad } from './Pinpad.svelte';
export { default as Balance } from './Balance.svelte';
diff --git a/src/lib/samples.js b/src/lib/samples.js
new file mode 100644
index 00000000..93b9aa24
--- /dev/null
+++ b/src/lib/samples.js
@@ -0,0 +1,23 @@
+export default {
+ register: `curl "https://coinos.io/api/register" -H "application/json" -d '{
+ "username": "adam",
+ "password": "hunter2"
+}'`,
+ login: `curl "https://coinos.io/api/login" -H "application/json" -d '{
+ "username": "adam",
+ "password": "hunter2"
+}'`,
+ invoice: `curl "https://coinos.io/api/invoice"
+ -H "application/json"
+ -H "Authorization: Bearer $token"
+ -d '{
+ "invoice": {
+ "amount": 1000,
+ "type": "lightning",
+ "webhook": "https://example.com/payment/received",
+ "secret": "hunter2"
+ },
+}'`,
+ fetchInvoice: `curl "https://coinos.io/api/invoice/bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj"
+ -H "application/json"'`
+};
diff --git a/src/lib/translations/index.js b/src/lib/translations/index.js
index 9c798530..dfd7f47f 100644
--- a/src/lib/translations/index.js
+++ b/src/lib/translations/index.js
@@ -46,6 +46,11 @@ const config = {
locale: 'de',
key: '',
loader: async () => (await import('../../locales/de.json')).default
+ },
+ {
+ locale: 'zh',
+ key: '',
+ loader: async () => (await import('../../locales/zh.json')).default
}
]
};
diff --git a/src/lib/translations/lang.json b/src/lib/translations/lang.json
index c537d4cf..c607ebd0 100644
--- a/src/lib/translations/lang.json
+++ b/src/lib/translations/lang.json
@@ -3,5 +3,6 @@
"fr": "Français",
"es": "Español",
"de": "Deutsch",
- "pt": "Português"
+ "pt": "Português",
+ "zh": "中国人"
}
diff --git a/src/locales/en.json b/src/locales/en.json
index 402f0214..28f7db2b 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -15,20 +15,10 @@
"position": "Chief Technical Officer",
"bio": "Adam is a web developer and bitcoin enthusiast who believes sound money is key to increasing global prosperity. He lives in North Vancouver with his wife Chelsea and two children."
},
- "shauna": {
- "name": "Shauna",
- "position": "Operations & Marketing",
- "bio": "Strategic & Project Management. Jack of all Trades. Can always find time to meet for coffee."
- },
"cole": {
"name": "Cole",
"position": "Certified Bitcoin Professional",
"bio": "Cole is a driven individual with a passion for freedom. He came across bitcoin in 2016 and studied it in depth. Now he wants to introduce the lightning network to as many users as possible."
- },
- "john": {
- "name": "John Barratt",
- "position": "Full Stack Web Developer",
- "bio": "When not coding you can find John out in the woods hiking all the great trails that BC has to offer."
}
},
"locations": {
@@ -45,21 +35,22 @@
"header": "FAQ",
"cost": {
"question": "What does it cost?",
- "answer": "It's free to register an account and receive payments over the lightning network. We charge a 0.1% fee to convert from lightning to bitcoin."
+ "answer": "It's free to register an account and receive payments. We charge a 0.1% conversion fee if you withdraw regular bitcoin to lightning or vice versa."
},
"compatibility": {
"question": "Do I need any special device or software?",
- "answer": "No. Coinos is web-based, so as long as you have an internet connection, you can use it on any device. Retail merchants might use it on a phone or a tablet."
+ "answer": "No. Coinos is web based, so as long as you have an internet connection and a web browser, it works anywhere. Retail merchants might use it on a phone or a tablet."
},
"safety": {
"question": "When do my funds settle?",
- "answer": "Lightning payments are settled instantly. You can withdraw your balance to another wallet or exchange right after payments are received."
+ "answer": "Lightning payments are settled instantly. You can withdraw your balance to another wallet or exchange immediately once funds are received."
}
},
"footer": {
"company": "Company",
"followUs": "Follow Us",
"github": "GitHub",
+ "linktree": "Linktree",
"jobs": "Jobs",
"support": "Support",
"telegram": "Telegram",
@@ -78,7 +69,7 @@
},
"step3": {
"header": "3. That's all!",
- "description": "Spend your coin at other merchants or save it for a rainy day, the choice is yours."
+ "description": "Spend your coin immediately or save it and watch it grow, the choice is yours."
}
},
"invoice": {
@@ -101,11 +92,11 @@
"landing": {
"header_before_icon": "The",
"header_with_icon": "easiest",
- "header_after_icon": "way to accept bitcoin in your store.",
- "subheader": "Grow your customer base and revenue by accepting bitcoin with an easy-to-use web app. No software or hardware required.",
+ "header_after_icon": "way to get started with bitcoin.",
+ "subheader": "A simple but powerful web wallet for everyone. No software or hardware required.",
"info1": {
"title": "Instant payments via Lightning",
- "description": "The moment your customer sends a payment, it’s there. No waiting for bank settlements or worrying about chargebacks. Instant settlement, in seconds. Say goodbye to 3% credit card processing fees."
+ "description": "We make it easy for merchants to receive retail payments. No waiting for bank settlements or worrying about chargebacks. Instant settlement, in seconds. Say goodbye to 3% credit card processing fees."
},
"info2": {
"title": "Simple to use",
@@ -115,7 +106,7 @@
},
"info3": {
"title": "Brand it your way!",
- "description": "Customize your point of sale look and feel, or keep it on default - it's totally up to you."
+ "description": "Customize your profile page look and feel, or keep it on default - it's totally up to you."
}
},
"login": {
@@ -194,6 +185,7 @@
"placeholder": "Paste a bitcoin address, lightning invoice or coinos username"
},
"settings": {
+ "saveSettings": "Save Settings",
"bannerImage": "Banner Image",
"profileImage": "Profile Image",
"oneTimeCode": "Enter one-time code",
diff --git a/src/locales/es.json b/src/locales/es.json
index 8e68a5f3..9f6c6e1b 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -15,31 +15,10 @@
"position": "Director de Tecnología",
"bio": "Adam es un desarrollador web y un entusiasta de bitcoin que cree que el dinero sólido es clave para aumentar la prosperidad global. Vive en la ciudad de North Vancouver con su esposa Chelsea y sus dos hijos."
},
- "secondl1ght": {
- "name": "secondl1ght",
- "position": "Desarrollador Web",
- "bio": "Desarrollador web autodidacta, se sumergió de cabeza en con el bitcoin y dejó su trabajo fiduciario para concentrarse en el desarrollo de bitcoin a tiempo completo. Contribuye en proyectos de código abierto, es un defensor de la privacidad y un amante de la libertad."
- },
-
- "karnage": {
- "name": "Karnage",
- "position": "Diseñador de Producto",
- "bio": "Mi misión es ayudar a los fundadores de startups a tener éxito. Creo productos para lograr estos objetivos. Diseño de producto con píxeles perfectos. Haz que te lo envíen."
- },
- "shauna": {
- "name": "Shauna",
- "position": "Operaciones y Mercadeo",
- "bio": "Gestión Estratégica y de Proyectos. Sé un poco de todo. Siempre habrá tiempo para compartir un buen café."
- },
"cole": {
"name": "Cole",
"position": "Profesional de Bitcoin",
"bio": "Como profesional certificado de Bitcoin, Cole es una persona motivada y apasionada por la libertad. Conoció el bitcoin en 2016 y lo estudió en profundidad. Ahora quiere presentarle la Red Lightning a tantos usuarios como sea posible."
- },
- "john": {
- "name": "John Barratt",
- "position": "Desarrollador Web Full Stack",
- "bio": "Cuando no está programando, podemos encontrar a John en el bosque recorriendo todos los grandes senderos que British Columbia tiene para ofrecer."
}
},
"locations": {
@@ -165,6 +144,7 @@
"invoice": "Factura",
"unfollow": "Dejar de seguir",
"following": "Siguiendo",
+ "followers": "Seguidores",
"follow": "Seguir",
"remove": "Eliminar",
"scanToPayBTC": "Escanear para pagar con bitcoin",
@@ -198,6 +178,9 @@
"placeholder": "Asigne una dirección de bitcoin, una factura lightning o un usuario de coinos"
},
"settings": {
+ "saveSettings": "Guardar ajustes",
+ "tipPrompt": "Solicitar propinas",
+ "tipPromptDescription": "Pregunta a las personas si quieren agregar una propina antes de pagar sus facturas",
"bannerImage": "Imagen del Banner",
"profileImage": "Imagen de perfil",
"oneTimeCode": "Ingrese el código de un solo uso",
@@ -243,7 +226,7 @@
"send": "Enviar"
}
},
- "transactions": {
+ "payments": {
"header": "Transacciones",
"AMOUNT": "CANTIDAD",
"TYPE": "TIPO",
@@ -253,7 +236,7 @@
"empty": "Aún no hay transacciones.",
"send": "Enviado",
"next": "Siguiente",
- "sendersChoice": "Sender's choice",
+ "sendersChoice": "Elección del remitente",
"sendTo": "Enviar a",
"to": "para",
"pay": "Pagar",
diff --git a/src/locales/pt.json b/src/locales/pt.json
index 117a5101..309511f9 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -1,277 +1,283 @@
{
"404": {
"404": "404",
- "pageNotFound": "Page not found."
+ "pageNotFound": "Página não encontrada."
},
"about": {
- "header": "About",
- "p1": "Coinos is an open source bitcoin payment app. Development started in Vancouver in September 2012 as a way to provide local merchants with a convenient way to accept bitcoin payments.",
- "p2": "You can download the source code to run an instance and connect it to your own bitcoin and lightning nodes. We'll help you get started being a benevolent custodian and payments provider for your community.",
- "p3": "Account transfers amongst users on our server are done off-chain so they're completely free, instant, and unlimited.",
+ "header": "Sobre",
+ "p1": "O Coinos é uma aplicação de pagamento de bitcoin de código aberto. O desenvolvimento começou em Vancouver em Setembro de 2012 como forma de fornecer aos comerciantes locais uma forma conveniente de aceitar pagamentos de bitcoin.",
+ "p2": "Você pode baixar o código fonte para executar uma instância e ligá-lo aos seus próprios nós de bitcoin e da Lightning Network. Vamos ajudá-lo a começar a ser um depositário benevolente e fornecedor de pagamentos para a sua comunidade..",
+ "p3": "As transferências de conta entre utilizadores no nosso servidor são feitas off-chain, por isso são completamente gratuitas, instantâneas e ilimitadas.",
"team": {
- "header": "The Team",
+ "header": "O time",
"adam": {
"name": "Adam Soltys",
- "position": "Chief Technical Officer",
- "bio": "Adam is a web developer and bitcoin enthusiast who believes sound money is key to increasing global prosperity. He lives in North Vancouver with his wife Chelsea and two children."
- },
- "secondl1ght": {
- "name": "secondl1ght",
- "position": "Web Developer",
- "bio": "A self-taught Web Developer, secondl1ght dove head first down the bitcoin rabbit hole and left his fiat job to focus on bitcoin development full-time. He contributes to open source projects, is a privacy advocate, and freedom lover."
- },
-
- "karnage": {
- "name": "Karnage",
- "position": "Product Designer",
- "bio": "My mission is to help startup founders succeed. I create products to achieve this goal. Pixel-perfect product design. Get it shipped."
- },
- "shauna": {
- "name": "Shauna",
- "position": "Operations & Marketing",
- "bio": "Strategic & Project Management. Jack of all Trades. Can always find time to meet for coffee."
+ "position": "Responsável Técnico Principal",
+ "bio": "Adam é um desenvolvedor web e um entusiasta do bitcoin que acredita que o dinheiro sólido é a chave para aumentar a prosperidade global. Ele vive em North Vancouver com a sua esposa Chelsea e dois filhos."
},
"cole": {
"name": "Cole",
- "position": "Bitcoin Professional",
- "bio": "As a Certified Bitcoin Professional, Cole is a driven individual with a passion for freedom. He came across bitcoin in 2016 and studied it in depth. Now he wants to introduce the lightning network to as many users as possible."
- },
- "john": {
- "name": "John Barratt",
- "position": "Full Stack Web Developer",
- "bio": "When not coding you can find John out in the woods hiking all the great trails that BC has to offer."
+ "position": "Profissional Certificado de Bitcoin",
+ "bio": "Cole é um indivíduo motivado com uma paixão pela liberdade. Encontrou-se com o bitcoin em 2016 e estudou-o em profundidade. Agora quer introduzir a rede lightning ao maior número de utilizadores possível."
}
},
"locations": {
- "header": "Locations",
- "description": "Here are some businesses that are accepting bitcoin with Coinos. We hope that yours will be next!"
+ "header": "Locais",
+ "description": "Aqui estão algumas empresas que estão aceitando bitcoin com a Coinos. Esperamos que o seu seja o próximo!"
}
},
"error": {
- "header": "Error",
- "message": "Please try again or contact support.",
- "signIn": "Please sign in to continue."
+ "header": "Erro",
+ "message": "Por favor tente novamente ou contate o suporte.",
+ "signIn": "Por favor, faça o login para continuar."
},
"faq": {
"header": "FAQ",
"cost": {
- "question": "What does it cost?",
- "answer": "It's free to register an account and receive payments over the lightning network. We charge a 0.1% fee to convert from lightning to bitcoin."
+ "question": "Quanto custa?",
+ "answer": "É grátis para registrar uma conta e receber pagamentos. Cobramos uma taxa de conversão de 0,1% se você retirar o bitcoin da rede principal para a lightning ou vice-versa.."
},
"compatibility": {
- "question": "Do I need any special device or software?",
- "answer": "No. Coinos is web-based, so as long as you have an internet connection, you can use it on any device. Retail merchants might use it on a phone or a tablet."
+ "question": "Eu preciso de algum dispositivo ou software especial?",
+ "answer": "Não. Coinos baseado na web, desde que você tenha uma conexão com a Internet e um navegador web, ele funciona em qualquer lugar. Os comerciantes de varejo podem usá-lo em um telefone, computador ou tablet."
},
"safety": {
- "question": "When do my funds settle?",
- "answer": "Lightning payments are settled instantly. You can withdraw your balance to another wallet or exchange right after payments are received."
+ "question": "Como é a liquidez?",
+ "answer": "Os pagamentos pela lightning são liquidados instantaneamente. Você pode retirar seu saldo para outra carteira ou trocar imediatamente assim que os fundos forem recebidos."
}
},
"footer": {
- "company": "Company",
- "followUs": "Follow Us",
+ "company": "Empresa",
+ "followUs": "Siga-nos",
"github": "GitHub",
- "jobs": "Jobs",
- "support": "Support",
+ "linktree": "Linktree",
+ "jobs": "Empregos",
+ "support": "Suporte",
"telegram": "Telegram",
"twitter": "Twitter",
- "news": "News"
+ "documentation": "Documentação"
},
"howItWorks": {
- "header": "How it works",
+ "header": "Como funciona",
"step1": {
- "header": "1. Create your account",
- "description": "A username and password is all you need to get started."
+ "header": "1. Crie sua conta",
+ "description": "Um nome de usuário e senha é tudo o que você precisa para começar."
},
"step2": {
- "header": "2. Ask for payments",
- "description": "The same as you normally would, but with bitcoin."
+ "header": "2. Solicite pagamentos",
+ "description": "O mesmo que você normalmente faria, mas com bitcoin."
},
"step3": {
- "header": "3. That's all!",
- "description": "Spend your coin at other merchants or save it for a rainy day, the choice is yours."
+ "header": "3. Pronto!",
+ "description": "Gaste sua moeda imediatamente ou guarde-a e veja-a crescer, a escolha é sua."
}
},
"invoice": {
- "addTip": "Add a tip",
- "addTipq": "Add a tip?",
- "done": "Done",
- "paymentSuccessful": "Payment received!",
- "invoice": "Invoice",
+ "addTip": "Adicionar gorjeta",
+ "addTipq": "Adicionar gorjeta?",
+ "done": "Feito",
+ "paymentSuccessful": "Pagamento recebido!",
+ "invoice": "Fatura",
"total": "Total",
- "tip": "Tip"
+ "tip": "Gorjeta"
},
"jobs": {
- "header": "Jobs",
- "subheader": "Current Openings",
- "pitch1": "Coinos is actively looking for developers to join our growing team (full-time or part-time, remote working OK).",
- "pitch2": "Excited about working on open source bitcoin? Then take a look at our repositories on",
+ "header": "Vagas de emprego",
+ "subheader": "Atualmente abertas",
+ "pitch1": "Coinos está procurando ativamente desenvolvedores para se juntar à nossa crescente equipe (tempo integral ou meio-período, trabalho remoto OK).",
+ "pitch2": "Entusiasmado com o trabalho em bitcoin open source? Então dê uma olhada em nossos repositórios em",
"github": "GitHub",
- "pitch3": "If you think you can make an impact, we'd love to hear from you. Send us your resume to"
+ "pitch3": "Se você acha que pode causar um impacto, gostaríamos muito de ouvir de você. Envie-nos seu currículo para"
},
"landing": {
- "header_before_icon": "The",
- "header_with_icon": "easiest",
- "header_after_icon": "way to accept bitcoin in your store.",
- "subheader": "Grow your customer base and revenue by accepting bitcoin with an easy-to-use web app. No software or hardware required.",
+ "header_before_icon": "O",
+ "header_with_icon": "jeito mais fácil",
+ "header_after_icon": "de iniciar no Bitcoin.",
+ "subheader": "Uma carteira web simples, mas poderosa para todos. Não requer software nem hardware.",
"info1": {
- "title": "Instant payments via Lightning",
- "description": "The moment your customer sends a payment, it’s there. No waiting for bank settlements or worrying about chargebacks. Instant settlement, in seconds. Say goodbye to 3% credit card processing fees."
+ "title": "Pagamentos instantâneos via Lightning",
+ "description": "Facilitamos para os comerciantes o recebimento de pagamentos de varejo. Nada de esperar por liquidações bancárias ou se preocupar com chargebacks. Liquidação instantânea, em segundos. Diga adeus a 3% de taxas de processamento de cartão de crédito.."
},
"info2": {
- "title": "Simple to use",
- "description1": "Just enter an amount in your local currency, share the QR code and you’re done.",
- "description2": "We do all the heavy lifting so you can focus on your business.",
- "description3": "You always have the option of taking custody of your funds by withdrawing instantly at any time."
+ "title": "Simples de usar",
+ "description1": "Basta inserir a quantia em sua moeda local, compartilhar o QR code e pronto..",
+ "description2": "Nós fazemos todo o trabalho pesado para que você possa se concentrar no seu negócio.",
+ "description3": "Você sempre tem a opção de tomar para sí a custódia de seus fundos, retirando-se imediatamente a qualquer momento."
},
"info3": {
- "title": "Brand it your way!",
- "description": "Customize your point of sale look and feel, or keep it on default - it's totally up to you."
+ "title": "Personalise do seu jeito!",
+ "description": "Personalize a aparência de sua página de perfil, ou mantenha o padrão - você escolhe."
}
},
"login": {
- "optional": "Optional",
+ "optional": "Opcional",
"token": "2FA Token",
- "username": "Username",
- "password": "Password",
+ "username": "Usuário",
+ "password": "Senha",
"email": "Email",
- "passwordRecommendation": "We recommend a long, random password generated with a password manager.",
- "forgotUserOrPassword": "Forgot Username or Password?",
- "haveAccount": "Already have an account?",
- "noAccount": "Don't have an account?",
- "register": "Register",
- "signIn": "Sign In",
- "failed": "Login failed"
+ "passwordRecommendation": "Recomendamos uma senha longa e aleatória gerada com um gerenciador de senhas.",
+ "forgotUserOrPassword": "Esqueceu o usuário ou senha?",
+ "haveAccount": "Já tem uma conta?",
+ "noAccount": "Não tem uma conta?",
+ "register": "Registre",
+ "signIn": "Entrar",
+ "failed": "Falha no login"
},
"nav": {
- "profile": "Profile",
- "startInSeconds": "Start in Seconds",
- "account": "Account",
- "signIn": "Sign In",
- "signOut": "Sign Out",
- "dashboard": "Dashboard",
- "settings": "Settings",
- "support": "Support"
+ "profile": "Perfil",
+ "startInSeconds": "Inicie em segundos",
+ "account": "Conta",
+ "signIn": "Entrar",
+ "signOut": "Sair",
+ "dashboard": "Painel de controle",
+ "settings": "Ajustes",
+ "support": "Suporte"
},
"newSiteHeader": "Join us at our <a href=\"/launch\" class=\"underline underline-offset-4 text-black hover:opacity-80\">launch party</a> in Vancouver on October 26! Looking for the old coinos? Visit <a href=\"https://classic.coinos.io\" class=\"underline underline-offset-4 text-black hover:opacity-80\">classic.coinos.io</a>",
"news": {
- "header": "News",
- "subheader": "Latest Updates",
+ "header": "Notícias",
+ "subheader": "Últimos Updates",
"update1": ""
},
"user": {
"bitcoin": "Bitcoin",
"lightning": "Lightning",
- "invoice": "Invoice",
- "unfollow": "Unfollow",
- "following": "Following",
- "follow": "Follow",
- "remove": "Remove",
- "scanToPayBTC": "Scan to pay with bitcoin",
- "this": "This",
- "lightningAddress": "lightning address",
- "likeAnEmail": "is like an email for bitcoin. You can publish it on your website or business cards and use it multiple times.",
+ "invoice": "Fatura",
+ "unfollow": "Deixar de seguir",
+ "following": "Seguindo",
+ "follow": "Seguir",
+ "followers": "Seguidores",
+ "remove": "Remover",
+ "noNotes": "Nenhuma nota ainda",
+ "message": "Mensagem",
+ "messages": "Mensagens",
+ "feed": "Feed",
+ "notes": "Notas",
+ "pay": "Pagar",
+ "scanToPayBTC": "Escaneie para pagar com bitcoin",
+ "this": "Este",
+ "lightningAddress": "endereço lightning",
+ "likeAnEmail": "é como um email para o bitcoin. Você pode publicá-lo em seu site ou cartão de visita e usá-lo várias vezes.",
"dashboard": {
- "go": "Go",
- "dashboard": "Dashboard",
- "accountBalance": "Account Balance",
- "bitcoinPrice": "bitcoin Price:",
- "conversionFeeWarning": "You will be charged a 1% withdrawal fee on the amount.",
+ "go": "Ir",
+ "dashboard": "Painel de controle",
+ "accountBalance": "Saldo na conta",
+ "bitcoinPrice": "Preço do bitcoin:",
+ "conversionFeeWarning": "Será cobrada uma taxa de retirada de 1% sobre o valor.",
"dataFromBinance": "Data from <span class=\"text-black\">Binance</span>.",
- "fetchingRate": "fetching rate...",
- "optionalNote": "Note (Optional)",
- "send": "Send",
- "receive": "Receive",
- "withdraw": "Send",
+ "fetchingRate": "buscando taxa...",
+ "optionalNote": "Nota (Opcional)",
+ "send": "Enviar",
+ "receive": "Receber",
+ "withdraw": "Enviar",
"deposit": "Deposit",
"voucher": "Voucher"
},
"receive": {
- "lessThan1BTCWarning": "Please enter an amount less than 1 bitcoin.",
- "showQR": "Show QR"
+ "lessThan1BTCWarning": "Favor inserir uma quantidade inferior a 1 bitcoin.",
+ "showQR": "Mostrar QR code"
},
"send": {
- "contacts": "Contacts",
- "scan": "Scan",
- "paste": "Paste",
- "next": "Next",
- "placeholder": "Paste a bitcoin address, lightning invoice or coinos username"
+ "contacts": "Contatos",
+ "scan": "Escanear",
+ "paste": "Colar",
+ "next": "Continuar",
+ "placeholder": "Colar um endereço de bitcoin, fatura lightning ou nome de usuário na coinos"
},
"settings": {
- "bannerImage": "Banner Image",
- "profileImage": "Profile Image",
- "oneTimeCode": "Enter one-time code",
- "accountId": "Account Secret",
- "header": "Settings",
- "ACCOUNT": "ACCOUNT",
- "POINT_OF_SALE": "POINT OF SALE",
- "SECURITY": "SECURITY",
- "locale": "Language",
- "username": "Username",
+ "saveSettings": "Salvar ajustes",
+ "bannerImage": "Imagem do banner",
+ "profileImage": "Imagem do perfil",
+ "oneTimeCode": "Entre com o código único",
+ "accountId": "ID da conta",
+ "header": "Ajustes",
+ "ACCOUNT": "CONTA",
+ "POINT_OF_SALE": "PREFERÊNCIAS",
+ "SECURITY": "SEGURANÇA",
+ "locale": "Idioma",
+ "username": "Usuário",
"email": "Email",
- "newPassword": "New Password",
- "businessAddress": "Business Address",
- "emailNotifications": "Email Notifications",
- "emailNotificationsDescription": "Get notified via email when a change occurs on your account or when people send you payments.",
- "smsNotifications": "SMS Notifications",
- "smsNotificationsDescription": "Coinos will send you text notifications when this is enabled.",
- "theme": "Color Theme",
- "themeDescription": "Select from the default color themes.",
- "localCurrency": "Local Currency",
- "btcUnit": "Display bitcoin in:",
+ "newPassword": "Alterar senhar",
+ "about": "Sobre",
+ "aboutPlaceholder": "Um pouco sobre você ou seu endereço comercial e horários",
+ "emailNotifications": "Notificações por email",
+ "emailNotificationsDescription": "Ser notificado por e-mail quando ocorrer uma mudança em sua conta ou quando as pessoas lhe enviarem pagamentos.",
+ "smsNotifications": "Notificações por SMS",
+ "smsNotificationsDescription": "A Coinos lhe enviará notificações por SMS quando isto estiver habilitado.",
+ "theme": "Cor do tema",
+ "tipPrompt": "Mostrar opção gorjetas",
+ "tipPromptDescription": "Pergunte às pessoas se elas querem acrescentar uma gorjeta antes de pagar sua fatura",
+ "displayName": "Mostrar nome",
+ "themeDescription": "Selecionar entre os temas de cor padrão.",
+ "localCurrency": "Moeda",
+ "btcUnit": "Mostrar bitcoin em:",
"bitcoin": "bitcoin",
+ "minutes": "minutos",
+ "hour": "hora",
+ "hours": "horas",
"satoshis": "Satoshis",
- "securityPIN": "Security PIN",
- "securityPINDescription": "Set a security pin that will be used to unlock your settings and to send outgoing payments.",
- "setPIN": "Set PIN",
- "autoLock": "Auto-Lock Timer",
- "autoLockDescription": "Your account will go into a secure mode automatically after a specified time:",
- "twofa": "2-Factor Authentication",
- "twofaDescription": "Add an extra level of security to your device by enableing 2-factor authentication.",
- "twofaSetup": "Set up 2FA",
- "twofaDisable": "Disable 2FA",
- "select": "Select",
- "verifyPIN": "Verify PIN",
- "enablePIN": "Enable PIN",
- "disablePIN": "Disable PIN"
+ "securityPIN": "PIN de segurança",
+ "securityPINDescription": "Estabeleça um PIN de segurança que será usado para desbloquear suas configurações e para enviar pagamentos.",
+ "setPIN": "Insira o PIN",
+ "autoLock": "Bloqueio automático",
+ "autoLockDescription": "Sua conta entrará em um modo seguro, exigindo que você reentre suas credenciais após um tempo especificado:",
+ "twofa": "Eutenticação de 2 fatores",
+ "twofaDescription": "Adicione um nível extra de segurança ao seu dispositivo, permitindo autenticação de 2 fatores.",
+ "twofaSetup": "Habilitar o 2FA",
+ "twofaDisable": "Desabilitar o 2FA",
+ "select": "Selecionar",
+ "verifyPIN": "Verificar PIN",
+ "enablePIN": "Habilitar PIN",
+ "disablePIN": "Desabilitar PIN"
},
"support": {
- "header": "Support",
+ "header": "Suporte",
"email": "Email",
- "accountName": "Account Name",
- "message": "Message",
- "send": "Send"
+ "accountName": "Usuário",
+ "message": "Mensagem",
+ "send": "Enviar"
}
},
"payments": {
- "header": "Transactions",
- "AMOUNT": "AMOUNT",
- "TYPE": "TYPE",
- "TIME": "TIME",
- "sent": "Sent",
- "received": "Received",
- "empty": "No transactions yet.",
- "send": "Send",
- "next": "Next",
- "sendersChoice": "Sender's choice",
- "sendTo": "Send to",
- "to": "to",
- "pay": "Pay",
- "requesting": "is requesting",
- "sentInvoiceTo": "Sent invoice to",
- "sentRequestTo": "Sent request to",
- "pleasePay": "Please pay",
- "day": "Day",
- "week": "Week",
- "month": "Month",
- "all": "All",
- "export": "Export",
- "invoice": "Invoice",
- "readyToPay": "is ready to pay",
- "describeOrder": "Use this field to describe your order",
- "requestAnInvoice": "Request an invoice",
- "submit": "Submit",
- "isRequesting": "is requesting",
- "awaiting": "Awaiting"
+ "amount": "Quantidade",
+ "date": "Data",
+ "id": "ID de transação",
+ "details": "Details da transação",
+ "header": "Pagamentos",
+ "preimage": "Prova de pagamento",
+ "type": "Tipo",
+ "time": "Hora",
+ "sent": "Enviado",
+ "received": "Recebido",
+ "empty": "Nenhuma transação ainda.",
+ "send": "Enviar",
+ "next": "Continuar",
+ "sendersChoice": "A escolha do remetente",
+ "sendTo": "Enviar para",
+ "to": "para",
+ "pay": "Pagar",
+ "requesting": "solicitando",
+ "sentInvoiceTo": "Fatura enviada para",
+ "sentRequestTo": "Solicitação enviada!",
+ "pleasePay": "Por favor pague",
+ "day": "Dia",
+ "week": "Semana",
+ "month": "Mês",
+ "all": "Todos",
+ "export": "Exportar",
+ "invoice": "Fatura",
+ "readyToPay": "está pronta para pagar",
+ "describeOrder": "Use este campo para descrever seu pedido",
+ "requestAnInvoice": "Solicitar Fatura",
+ "submit": "O envio",
+ "isRequesting": "foi solicitado",
+ "invoiceFrom": "Fatura de",
+ "awaiting": "Aguardando",
+ "tapAnywhere": "Toque em qualquer lugar para continuar",
+ "from": "de",
+ "redeem": "Resgatar",
+ "redeemed": "Resgatado",
+ "redeemedBy": "Resgatado por",
+ "sendMessage": "Escreva uma mensagem..."
}
}
diff --git a/src/locales/zh.json b/src/locales/zh.json
new file mode 100644
index 00000000..402f0214
--- /dev/null
+++ b/src/locales/zh.json
@@ -0,0 +1,291 @@
+{
+ "404": {
+ "404": "404",
+ "pageNotFound": "Page not found."
+ },
+ "about": {
+ "header": "About",
+ "p1": "Coinos is an open source bitcoin payment app. Development started in Vancouver in September 2012 as a way to provide local merchants with a convenient way to accept bitcoin payments.",
+ "p2": "You can download the source code to run an instance and connect it to your own bitcoin and lightning nodes. We'll help you get started being a benevolent custodian and payments provider for your community.",
+ "p3": "Account transfers amongst users on our server are done off-chain so they're completely free, instant, and unlimited.",
+ "team": {
+ "header": "The Team",
+ "adam": {
+ "name": "Adam Soltys",
+ "position": "Chief Technical Officer",
+ "bio": "Adam is a web developer and bitcoin enthusiast who believes sound money is key to increasing global prosperity. He lives in North Vancouver with his wife Chelsea and two children."
+ },
+ "shauna": {
+ "name": "Shauna",
+ "position": "Operations & Marketing",
+ "bio": "Strategic & Project Management. Jack of all Trades. Can always find time to meet for coffee."
+ },
+ "cole": {
+ "name": "Cole",
+ "position": "Certified Bitcoin Professional",
+ "bio": "Cole is a driven individual with a passion for freedom. He came across bitcoin in 2016 and studied it in depth. Now he wants to introduce the lightning network to as many users as possible."
+ },
+ "john": {
+ "name": "John Barratt",
+ "position": "Full Stack Web Developer",
+ "bio": "When not coding you can find John out in the woods hiking all the great trails that BC has to offer."
+ }
+ },
+ "locations": {
+ "header": "Locations",
+ "description": "Here are some businesses that are accepting bitcoin with Coinos. We hope that yours will be next!"
+ }
+ },
+ "error": {
+ "header": "Error",
+ "message": "Please try again or contact support.",
+ "signIn": "Please sign in to continue."
+ },
+ "faq": {
+ "header": "FAQ",
+ "cost": {
+ "question": "What does it cost?",
+ "answer": "It's free to register an account and receive payments over the lightning network. We charge a 0.1% fee to convert from lightning to bitcoin."
+ },
+ "compatibility": {
+ "question": "Do I need any special device or software?",
+ "answer": "No. Coinos is web-based, so as long as you have an internet connection, you can use it on any device. Retail merchants might use it on a phone or a tablet."
+ },
+ "safety": {
+ "question": "When do my funds settle?",
+ "answer": "Lightning payments are settled instantly. You can withdraw your balance to another wallet or exchange right after payments are received."
+ }
+ },
+ "footer": {
+ "company": "Company",
+ "followUs": "Follow Us",
+ "github": "GitHub",
+ "jobs": "Jobs",
+ "support": "Support",
+ "telegram": "Telegram",
+ "twitter": "Twitter",
+ "documentation": "Documentation"
+ },
+ "howItWorks": {
+ "header": "How it works",
+ "step1": {
+ "header": "1. Create your account",
+ "description": "A username and password is all you need to get started."
+ },
+ "step2": {
+ "header": "2. Ask for payments",
+ "description": "The same as you normally would, but with bitcoin."
+ },
+ "step3": {
+ "header": "3. That's all!",
+ "description": "Spend your coin at other merchants or save it for a rainy day, the choice is yours."
+ }
+ },
+ "invoice": {
+ "addTip": "Add a tip",
+ "addTipq": "Add a tip?",
+ "done": "Done",
+ "paymentSuccessful": "Payment received!",
+ "invoice": "Invoice",
+ "total": "Total",
+ "tip": "Tip"
+ },
+ "jobs": {
+ "header": "Jobs",
+ "subheader": "Current Openings",
+ "pitch1": "Coinos is actively looking for developers to join our growing team (full-time or part-time, remote working OK).",
+ "pitch2": "Excited about working on open source bitcoin? Then take a look at our repositories on",
+ "github": "GitHub",
+ "pitch3": "If you think you can make an impact, we'd love to hear from you. Send us your resume to"
+ },
+ "landing": {
+ "header_before_icon": "The",
+ "header_with_icon": "easiest",
+ "header_after_icon": "way to accept bitcoin in your store.",
+ "subheader": "Grow your customer base and revenue by accepting bitcoin with an easy-to-use web app. No software or hardware required.",
+ "info1": {
+ "title": "Instant payments via Lightning",
+ "description": "The moment your customer sends a payment, it’s there. No waiting for bank settlements or worrying about chargebacks. Instant settlement, in seconds. Say goodbye to 3% credit card processing fees."
+ },
+ "info2": {
+ "title": "Simple to use",
+ "description1": "Just enter an amount in your local currency, share the QR code and you’re done.",
+ "description2": "We do all the heavy lifting so you can focus on your business.",
+ "description3": "You always have the option of taking custody of your funds by withdrawing instantly at any time."
+ },
+ "info3": {
+ "title": "Brand it your way!",
+ "description": "Customize your point of sale look and feel, or keep it on default - it's totally up to you."
+ }
+ },
+ "login": {
+ "optional": "Optional",
+ "token": "2FA Token",
+ "username": "Username",
+ "password": "Password",
+ "email": "Email",
+ "passwordRecommendation": "We recommend a long, random password generated with a password manager.",
+ "forgotUserOrPassword": "Forgot Username or Password?",
+ "haveAccount": "Already have an account?",
+ "noAccount": "Don't have an account?",
+ "register": "Register",
+ "signIn": "Sign In",
+ "failed": "Login failed"
+ },
+ "nav": {
+ "profile": "Profile",
+ "startInSeconds": "Start in Seconds",
+ "account": "Account",
+ "signIn": "Sign In",
+ "signOut": "Sign Out",
+ "dashboard": "Dashboard",
+ "settings": "Settings",
+ "support": "Support"
+ },
+ "newSiteHeader": "Join us at our <a href=\"/launch\" class=\"underline underline-offset-4 text-black hover:opacity-80\">launch party</a> in Vancouver on October 26! Looking for the old coinos? Visit <a href=\"https://classic.coinos.io\" class=\"underline underline-offset-4 text-black hover:opacity-80\">classic.coinos.io</a>",
+ "news": {
+ "header": "News",
+ "subheader": "Latest Updates",
+ "update1": ""
+ },
+ "user": {
+ "bitcoin": "Bitcoin",
+ "lightning": "Lightning",
+ "invoice": "Invoice",
+ "unfollow": "Unfollow",
+ "following": "Following",
+ "follow": "Follow",
+ "followers": "Followers",
+ "remove": "Remove",
+ "noNotes": "No notes yet",
+ "message": "Message",
+ "messages": "Messages",
+ "feed": "Feed",
+ "notes": "Notes",
+ "pay": "Pay",
+ "scanToPayBTC": "Scan to pay with bitcoin",
+ "this": "This",
+ "lightningAddress": "lightning address",
+ "likeAnEmail": "is like an email for bitcoin. You can publish it on your website or business cards and use it multiple times.",
+ "dashboard": {
+ "go": "Go",
+ "dashboard": "Dashboard",
+ "accountBalance": "Account Balance",
+ "bitcoinPrice": "bitcoin Price:",
+ "conversionFeeWarning": "You will be charged a 1% withdrawal fee on the amount.",
+ "dataFromBinance": "Data from <span class=\"text-black\">Binance</span>.",
+ "fetchingRate": "fetching rate...",
+ "optionalNote": "Note (Optional)",
+ "send": "Send",
+ "receive": "Receive",
+ "withdraw": "Send",
+ "deposit": "Deposit",
+ "voucher": "Voucher"
+ },
+ "receive": {
+ "lessThan1BTCWarning": "Please enter an amount less than 1 bitcoin.",
+ "showQR": "Show QR"
+ },
+ "send": {
+ "contacts": "Contacts",
+ "scan": "Scan",
+ "paste": "Paste",
+ "next": "Next",
+ "placeholder": "Paste a bitcoin address, lightning invoice or coinos username"
+ },
+ "settings": {
+ "bannerImage": "Banner Image",
+ "profileImage": "Profile Image",
+ "oneTimeCode": "Enter one-time code",
+ "accountId": "Account Secret",
+ "header": "Settings",
+ "ACCOUNT": "ACCOUNT",
+ "POINT_OF_SALE": "PREFERENCES",
+ "SECURITY": "SECURITY",
+ "locale": "Language",
+ "username": "Username",
+ "email": "Email",
+ "newPassword": "Change Password",
+ "about": "About",
+ "aboutPlaceholder": "A little something about you or your business address and hours",
+ "emailNotifications": "Email Notifications",
+ "emailNotificationsDescription": "Get notified via email when a change occurs on your account or when people send you payments.",
+ "smsNotifications": "SMS Notifications",
+ "smsNotificationsDescription": "Coinos will send you text notifications when this is enabled.",
+ "theme": "Color Theme",
+ "tipPrompt": "Prompt for tips",
+ "tipPromptDescription": "Ask people if they want to add a tip before paying their invoices",
+ "displayName": "Display name",
+ "themeDescription": "Select from the default color themes.",
+ "localCurrency": "Currency",
+ "btcUnit": "Display bitcoin in:",
+ "bitcoin": "bitcoin",
+ "minutes": "minutes",
+ "hour": "hour",
+ "hours": "hours",
+ "satoshis": "Satoshis",
+ "securityPIN": "Security PIN",
+ "securityPINDescription": "Set a security pin that will be used to unlock your settings and to send outgoing payments.",
+ "setPIN": "Set PIN",
+ "autoLock": "Auto-Lock Timer",
+ "autoLockDescription": "Your account will go into a secure mode, requiring you to re-enter your credentials after a specified time:",
+ "twofa": "2-Factor Authentication",
+ "twofaDescription": "Add an extra level of security to your device by enabling 2-factor authentication.",
+ "twofaSetup": "Set up 2FA",
+ "twofaDisable": "Disable 2FA",
+ "select": "Select",
+ "verifyPIN": "Verify PIN",
+ "enablePIN": "Enable PIN",
+ "disablePIN": "Disable PIN"
+ },
+ "support": {
+ "header": "Support",
+ "email": "Email",
+ "accountName": "Account Name",
+ "message": "Message",
+ "send": "Send"
+ }
+ },
+ "payments": {
+ "amount": "Amount",
+ "date": "Date",
+ "id": "Transaction ID",
+ "details": "Transaction Details",
+ "header": "Payments",
+ "preimage": "Proof of payment",
+ "type": "Type",
+ "time": "Time",
+ "sent": "Sent",
+ "received": "Received",
+ "empty": "No transactions yet.",
+ "send": "Send",
+ "next": "Next",
+ "sendersChoice": "Sender's choice",
+ "sendTo": "Send to",
+ "to": "to",
+ "pay": "Pay",
+ "requesting": "is requesting",
+ "sentInvoiceTo": "Sent invoice to",
+ "sentRequestTo": "Request sent!",
+ "pleasePay": "Please pay",
+ "day": "Day",
+ "week": "Week",
+ "month": "Month",
+ "all": "All",
+ "export": "Export",
+ "invoice": "Invoice",
+ "readyToPay": "is ready to pay",
+ "describeOrder": "Use this field to describe your order",
+ "requestAnInvoice": "Request Invoice",
+ "submit": "Submit",
+ "isRequesting": "is requesting",
+ "invoiceFrom": "Invoice from",
+ "awaiting": "Awaiting",
+ "tapAnywhere": "Tap anywhere to continue",
+ "from": "from",
+ "redeem": "Redeem",
+ "redeemed": "Redeemed",
+ "redeemedBy": "Redeemed by",
+ "sendMessage": "Write a message..."
+ }
+}
diff --git a/src/routes/(app)/+layout.server.js b/src/routes/(app)/+layout.server.js
index ef278744..e80e1e64 100644
--- a/src/routes/(app)/+layout.server.js
+++ b/src/routes/(app)/+layout.server.js
@@ -30,6 +30,9 @@ export async function load({ cookies, request, url, params }) {
throw redirect(307, '/login');
}
+ if (user && !user.pubkey && !pathname.includes('generate'))
+ throw redirect(307, `/${user.username}/generate`);
+
try {
rate = await get('/rate');
rates = await get('/rates');
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index 45469bdb..0dcdc1b2 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -25,7 +25,6 @@
};
$: $selectedRate = r * (rates[u?.currency || 'USD'] / rates.USD);
- $: $selectedRate = $rate * (rates[u?.currency || 'USD'] / rates.USD);
onMount(() => {
let localStorageLocale = localStorage.getItem(localeLocalStorageKey);
diff --git a/src/routes/(app)/[username]/(profile)/+layout.svelte b/src/routes/(app)/[username]/(profile)/+layout.svelte
index 6eb2a850..bbb2379e 100644
--- a/src/routes/(app)/[username]/(profile)/+layout.svelte
+++ b/src/routes/(app)/[username]/(profile)/+layout.svelte
@@ -4,7 +4,6 @@
import { Icon } from '$comp';
import { t } from '$lib/translations';
import { sign, send } from '$lib/nostr';
- import { v4 } from 'uuid';
export let data;
let events, user, subject, src, text;
@@ -60,6 +59,9 @@
t++;
}, 10);
};
+
+ let hideBio = true;
+ let toggleBio = () => (hideBio = false);
</script>
<div class="container mx-auto w-full px-4 mb-4 flex flex-wrap lg:flex-nowrap">
@@ -67,7 +69,11 @@
<div class="w-[240px] lg:absolute space-y-5 left-20 mx-auto lg:mr-10 mb-10">
<h1 class="text-3xl font-bold text-center mx-auto">{display || username}</h1>
- <div class="text-secondary mx-auto text-center lg:text-left lg:mx-0">
+ <div
+ class="text-secondary mx-auto text-center lg:text-left lg:mx-0"
+ class:line-clamp-3={hideBio}
+ on:click={toggleBio}
+ >
{subject.address && subject.address !== 'null' ? subject.address : ''}
</div>
diff --git a/src/routes/(app)/[username]/(profile)/+page.server.js b/src/routes/(app)/[username]/(profile)/+page.server.js
index d6cf3984..e37db1fe 100644
--- a/src/routes/(app)/[username]/(profile)/+page.server.js
+++ b/src/routes/(app)/[username]/(profile)/+page.server.js
@@ -5,31 +5,31 @@ export async function load({ cookies, params, parent, url }) {
let { pubkey } = subject;
let { since = 0 } = params;
- let messages, invoices, sent, received;
- messages = invoices = sent = received = [];
+ let messages, invoices, sent, received, notes;
+ messages = invoices = sent = received = notes = [];
+
+ if (pubkey) {
+ if (user) {
+ try {
+ messages = await get(`/${user.pubkey}/${since}/messages`);
+ messages = messages.sort((a, b) => b.created_at - a.created_at);
+ } catch (e) {
+ console.log(`failed to fetch nostr messages`, e);
+ }
+
+ ({ invoices, sent, received } = await get('/requests', auth(cookies)));
+ }
- if (user) {
try {
- messages = await get(`/${user.pubkey}/${since}/messages`);
- messages = messages.sort((a, b) => b.created_at - a.created_at);
+ notes = await get(`/${pubkey}/notes`);
} catch (e) {
- console.log(`failed to fetch nostr messages`, e);
+ console.log(`failed to fetch nostr notes for ${pubkey}`, e);
}
- ({ invoices, sent, received } = await get('/requests', auth(cookies)));
+ notes.map((e) => {
+ e.seen = e.created_at;
+ });
}
- let notes = [];
-
- try {
- notes = await get(`/${pubkey}/notes`);
- } catch (e) {
- console.log(`failed to fetch nostr notes for ${pubkey}`, e);
- }
-
- notes.map((e) => {
- e.seen = e.created_at;
- });
-
return { invoices, messages, notes, sent, received };
}
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 168556e4..09335e9c 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -36,7 +36,7 @@
let initialize = async (p) => {
ready = false;
if (!p) return;
- while (latest.length < 3 && i < messages.length) {
+ while (i < messages.length) {
let event = messages[i];
let k = event.author.id === user.id ? event.recipient.pubkey : event.author.pubkey;
if (!keys.has(k)) {
diff --git a/src/routes/(app)/[username]/(profile)/followers/+page.svelte b/src/routes/(app)/[username]/(profile)/followers/+page.svelte
index 77b337b6..56b3ba16 100644
--- a/src/routes/(app)/[username]/(profile)/followers/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/followers/+page.svelte
@@ -1,4 +1,5 @@
<script>
+ import { t } from '$lib/translations';
import { Avatar } from '$comp';
import { browser } from '$app/environment';
import VirtualScroll from 'svelte-virtual-scroll-list';
@@ -14,7 +15,9 @@
<svelte:window bind:innerWidth={w} />
<div class="px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">Followers</h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t('user.followers')}
+ </h1>
{#if browser}
{#if followers.length}
diff --git a/src/routes/(app)/[username]/(profile)/follows/+page.svelte b/src/routes/(app)/[username]/(profile)/follows/+page.svelte
index 78566fc4..b238c3a7 100644
--- a/src/routes/(app)/[username]/(profile)/follows/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/follows/+page.svelte
@@ -1,4 +1,5 @@
<script>
+ import { t } from '$lib/translations';
import { Avatar } from '$comp';
import { browser } from '$app/environment';
import VirtualScroll from 'svelte-virtual-scroll-list';
@@ -13,7 +14,9 @@
<svelte:window bind:innerWidth={w} />
<div class="px-3 md:px-0 w-full md:w-[400px] mx-auto space-y-8">
- <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">Following</h1>
+ <h1 class="px-3 md:px-0 text-center text-3xl md:text-4xl font-semibold">
+ {$t('user.following')}
+ </h1>
{#if browser}
{#if follows.length}
diff --git a/src/routes/(app)/[username]/dashboard/+page.svelte b/src/routes/(app)/[username]/dashboard/+page.svelte
index 499034e6..d8761ea9 100644
--- a/src/routes/(app)/[username]/dashboard/+page.svelte
+++ b/src/routes/(app)/[username]/dashboard/+page.svelte
@@ -7,7 +7,6 @@
import { goto } from '$app/navigation';
import { t } from '$lib/translations';
import { events } from '$lib/store';
- import { v4 } from 'uuid';
export let data;
$: ({ invoices, requests } = data);
diff --git a/src/routes/(app)/[username]/invoice/+server.js b/src/routes/(app)/[username]/invoice/+server.js
index 1321739e..461d380c 100644
--- a/src/routes/(app)/[username]/invoice/+server.js
+++ b/src/routes/(app)/[username]/invoice/+server.js
@@ -1,5 +1,7 @@
import { auth, post } from '$lib/utils';
import { json } from '@sveltejs/kit';
-export let POST = async ({ cookies: c, request: r }) =>
- json(await post('/invoice', await r.json(), auth(c)));
+export let POST = async ({ cookies: c, request: r }) => {
+ let j = await r.json();
+ return json(await post('/invoice', j, auth(c)));
+};
diff --git a/src/routes/(app)/[username]/invoice/[hash]/+layout.server.js b/src/routes/(app)/[username]/invoice/[hash]/+layout.server.js
index b5323d3f..7cf87e14 100644
--- a/src/routes/(app)/[username]/invoice/[hash]/+layout.server.js
+++ b/src/routes/(app)/[username]/invoice/[hash]/+layout.server.js
@@ -19,7 +19,7 @@ export async function load({ depends, params, url, parent }) {
if (paid && !url.pathname.endsWith('paid'))
throw redirect(307, `/${params.username}/invoice/${hash}/paid`);
- let sm = Qr.drawImg(invoice.text, { size: 300 });
+ let sm = Qr.drawImg(invoice.text || '', { size: 300 });
return { hash, invoice, sm, lg: sm };
}
diff --git a/src/routes/(app)/[username]/invoice/[hash]/+page.svelte b/src/routes/(app)/[username]/invoice/[hash]/+page.svelte
index 2ff92687..fff2b22c 100644
--- a/src/routes/(app)/[username]/invoice/[hash]/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[hash]/+page.svelte
@@ -7,7 +7,6 @@
import { last } from '$lib/store';
import { Avatar, Icon, Heart, Image } from '$comp';
import { t } from '$lib/translations';
- import screenfull from 'screenfull';
import { goto, invalidate } from '$app/navigation';
export let data;
@@ -67,14 +66,6 @@
$: link = type === 'bitcoin' ? text : `lightning:${text}`;
$: txt = type === 'bitcoin' ? hash : text;
- let full;
- let toggle = () => {
- screenfull.toggle(qr);
- full = !full;
- if (full) src = lg;
- else src = sm;
- };
-
let toggleType = async () => {
invoice.type = invoice.type === types.lightning ? types.bitcoin : types.lightning;
({ hash } = await post(`/${username}/invoice`, {
@@ -111,21 +102,18 @@
<div class="my-auto">Bitcoin</div>
</button>
</div>
- <div class="relative flex">
- <div class="flex mx-auto w-[360px] h-[360px]">
- <img
- {src}
- class:p-4={full}
- class="w-[300px] h-[300px] mx-auto z-10 mt-[20px]"
- bind:this={qr}
- on:click={toggle}
- on:keydown={toggle}
- alt={txt}
- />
- </div>
- <div
- class="absolute m-auto left-0 right-0 w-[340px] h-[340px] rounded-full bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] z-0"
- />
+
+ <div>
+ <a href={invoice.type === 'bitcoin' ? invoice.text : 'lightning:' + invoice.text}>
+ <div class="relative flex">
+ <div class="flex mx-auto w-[360px] h-[360px]">
+ <img {src} class="w-[300px] h-[300px] mx-auto z-10 mt-[20px]" bind:this={qr} alt={txt} />
+ </div>
+ <div
+ class="absolute m-auto left-0 right-0 w-[340px] h-[340px] rounded-full bg-gradient-to-r from-[#F2F6FC] to-[#E1E3FF] z-0"
+ />
+ </div>
+ </a>
</div>
{#if amount > 0}
diff --git a/src/routes/(app)/[username]/invoice/[hash]/paid/+page.svelte b/src/routes/(app)/[username]/invoice/[hash]/paid/+page.svelte
index 3a281728..4d7aae51 100644
--- a/src/routes/(app)/[username]/invoice/[hash]/paid/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[hash]/paid/+page.svelte
@@ -21,7 +21,7 @@
{#if received}
<h1 class="text-3xl md:text-4xl font-bold mb-6">{$t('invoice.paymentSuccessful')}</h1>
<h2 class="text-2xl md:text-3xl font-semibold">
- {f(fiat(received - tip, rate), currency)}
+ {f(fiat(received, rate), currency)}
{#if tip}
<span class="text-lg">
+ {f(fiat(tip, rate), currency)}
@@ -29,7 +29,7 @@
{/if}
</h2>
<h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(received - tip)}
+ ⚡️{s(received)}
{#if tip}
<span class="text-lg">
@@ -48,7 +48,7 @@
{/if}
</h2>
<h3 class="text-secondary md:text-lg mb-6 mt-1">
- ⚡️{s(pending - tip)}
+ ⚡️{s(pending)}
{#if tip}
<span class="text-lg">
+ ⚡️{s(tip)}
diff --git a/src/routes/(app)/[username]/invoice/[hash]/tip/+page.svelte b/src/routes/(app)/[username]/invoice/[hash]/tip/+page.svelte
index 296ef9c6..9d9021a6 100644
--- a/src/routes/(app)/[username]/invoice/[hash]/tip/+page.svelte
+++ b/src/routes/(app)/[username]/invoice/[hash]/tip/+page.svelte
@@ -59,6 +59,7 @@
let { invoice, id, user } = data;
let {
amount,
+ hash,
type,
rate,
received,
@@ -79,6 +80,7 @@
({ invoice, id } = data);
({
amount,
+ hash,
type,
rate,
received,
@@ -110,6 +112,8 @@
<input type="hidden" name="rate" value={rate} />
<input type="hidden" name="prompt" value="false" />
<input type="hidden" name="type" value={type} />
+ <input type="hidden" name="hash" value={hash} />
+
<input type="hidden" name="request_id" value={invoice.request?.id} />
<h1 class="text-4xl font-semibold my-8">{$t('invoice.addTipq')}</h1>
diff --git a/src/routes/(app)/[username]/messages/+page.server.js b/src/routes/(app)/[username]/messages/+page.server.js
index 7b651389..8347c184 100644
--- a/src/routes/(app)/[username]/messages/+page.server.js
+++ b/src/routes/(app)/[username]/messages/+page.server.js
@@ -11,6 +11,8 @@ export async function load({ params, parent, url }) {
messages = messages
.filter((m) => m.recipient.id === subject.id || m.author.id === subject.id)
.sort((a, b) => a.created_at - b.created_at);
+
+ if (user.id === subject.id) messages = messages.filter((m) => m.recipient.id === m.author.id);
} catch (e) {
console.log(`failed to fetch nostr messages`, e);
}
diff --git a/src/routes/(app)/[username]/messages/+page.svelte b/src/routes/(app)/[username]/messages/+page.svelte
index 5de18c66..7a10d605 100644
--- a/src/routes/(app)/[username]/messages/+page.svelte
+++ b/src/routes/(app)/[username]/messages/+page.svelte
@@ -2,7 +2,7 @@
import { cubicInOut } from 'svelte/easing';
import { browser } from '$app/environment';
import { t } from '$lib/translations';
- import { format, parseISO } from 'date-fns';
+ import { format } from 'date-fns';
import { scale, fade, fly } from 'svelte/transition';
import { enhance } from '$app/forms';
import { Avatar, Icon } from '$comp';
@@ -68,7 +68,6 @@
event.message = message;
message = '';
- input.focus();
event.author = user;
event.recipient = subject;
@@ -92,9 +91,16 @@
message = '';
submitting = false;
};
+
+ let keydown = (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ submit();
+ }
+ };
</script>
-<div class="container max-w-xl mx-auto px-4">
+<div class="container max-w-xl mx-auto px-4 space-y-5">
<div
class="h-[50vh] max-h-[50vh] overflow-y-scroll scrollbar-thin scrollbar-thumb-[#F2F6FC] scrollbar-track-white pr-8"
bind:this={pane}
@@ -133,22 +139,24 @@
{/each}
</div>
- <form method="POST" class="space-y-5 relative" on:submit|preventDefault={submit}>
+ <form
+ method="POST"
+ class="w-full border rounded-xl outline-none gap-4 flex p-0 pr-2"
+ on:submit|preventDefault={submit}
+ >
<input type="hidden" name="requester_id" value={user.id} />
<input type="hidden" name="recipient" value={subject.username} />
- <button type="submit" class="absolute right-2 bottom-2">
- <Icon icon="send" style="w-10" />
- </button>
-
- <input
+ <div
use:focus
- name="message"
- placeholder={$t('payments.sendMessage')}
- class="w-full p-4 border rounded-xl outline-none"
- bind:value={message}
- bind:this={input}
+ contenteditable
+ class="grow break-all py-4 outline-none mt-0 pl-4"
+ bind:innerHTML={message}
+ on:keydown={keydown}
/>
+ <button type="submit" class="my-auto shrink-0">
+ <Icon icon="send" style="w-8" />
+ </button>
</form>
</div>
diff --git a/src/routes/(app)/[username]/payments/[...page]/+page.svelte b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
index aacba81e..c9cc5c40 100644
--- a/src/routes/(app)/[username]/payments/[...page]/+page.svelte
+++ b/src/routes/(app)/[username]/payments/[...page]/+page.svelte
@@ -1,7 +1,7 @@
<script>
import { Avatar, Icon } from '$comp';
import { onMount } from 'svelte';
- import { format, parseISO } from 'date-fns';
+ import { format } from 'date-fns';
import { newPayment, payments } from '$lib/store';
import { t } from '$lib/translations';
import { get, f, s, sat, sats } from '$lib/utils';
@@ -60,9 +60,11 @@
payments = payments.map((p) => ({
...p,
created: new Date(p.created),
+ total: p.amount + (p.tip || 0),
amount_fiat: f((p.amount * p.rate) / sats, p.currency),
fee_fiat: p.fee ? f((p.fee * p.rate) / sats, p.currency) : null,
- tip_fiat: p.tip ? f((p.tip * p.rate) / sats, p.currency) : null
+ tip_fiat: p.tip ? f((p.tip * p.rate) / sats, p.currency) : null,
+ total_fiat: f(((p.amount + (p.tip || 0)) * p.rate) / sats, p.currency)
}));
let keys = [
@@ -72,11 +74,13 @@
'rate',
'currency',
'amount',
- 'fee',
'tip',
+ 'total',
+ 'fee',
'amount_fiat',
- 'fee_fiat',
- 'tip_fiat'
+ 'tip_fiat',
+ 'total_fiat',
+ 'fee_fiat'
];
let csv =
@@ -143,24 +147,20 @@
<a href={`/payment/${p.id}`}>
<div class="grid grid-cols-3 border-b h-24 hover:bg-gray-100 px-4">
<div class="whitespace-nowrap my-auto">
- <div class="font-bold">
- {f(p.amount * (p.rate / sats), p.currency)}
-
+ <div class="font-bold" class:text-red-800={p.amount < 0}>
+ {f((Math.abs(p.amount) + (p.tip || 0)) * (p.rate / sats), p.currency)}
{#if p.tip}
- <span class="text-sm">
- +{f(p.tip * (p.rate / sats), p.currency)}
+ <span class="text-xs text-secondary">
+ {f(Math.abs(p.amount) * (p.rate / sats), p.currency)} + {f(
+ p.tip * (p.rate / sats),
+ p.currency
+ )}
</span>
{/if}
</div>
<div class="text-secondary">
- {sat(p.amount)}
-
- {#if p.tip}
- <span class="text-sm">
- +{sat(p.tip)}
- </span>
- {/if}
+ {sat(p.amount + (p.tip || 0))}
</div>
</div>
diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
index 5638a22b..997d8b4a 100644
--- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
+++ b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte
@@ -5,7 +5,7 @@
import { Icon, Spinner, Pin } from '$comp';
import { t } from '$lib/translations';
- import { fail, post, success } from '$lib/utils';
+ import { fail, post, warning, success } from '$lib/utils';
import { avatar, banner, password, pin } from '$lib/store';
import { upload } from '$lib/upload';
import { page } from '$app/stores';
@@ -55,7 +55,12 @@
let data = new FormData(formElement);
if (data.get('password')) {
- data.set('cipher', await reEncryptEntropy(user, data.get('password')));
+ try {
+ data.set('cipher', await reEncryptEntropy(user, data.get('password')));
+ } catch (e) {
+ console.log('Failed to encrypt keys with new password');
+ throw e;
+ }
}
if ($avatar) {
@@ -80,8 +85,12 @@
tags: []
};
- await sign({ event, user });
- await send(event);
+ try {
+ await sign({ event, user });
+ await send(event);
+ } catch (e) {
+ warning('nostr profile could not be updated');
+ }
const response = await fetch(formElement.action, {
method: 'POST',
@@ -150,7 +159,7 @@
{#if loading}
<Spinner />
{:else}
- <div class="my-auto">Save Settings</div>
+ <div class="my-auto">{$t('user.settings.saveSettings')}</div>
{/if}
</button>
</div>
diff --git a/src/routes/(app)/admin/+layout.server.js b/src/routes/(app)/admin/+layout.server.js
new file mode 100644
index 00000000..26449467
--- /dev/null
+++ b/src/routes/(app)/admin/+layout.server.js
@@ -0,0 +1,11 @@
+import { auth, get } from '$lib/utils';
+import { redirect } from '@sveltejs/kit';
+
+export async function load({ cookies, parent }) {
+ let token = cookies.get('token');
+ if (!token) throw redirect(307, '/login');
+ let user = await get('/me', auth(cookies));
+ if (!user) throw redirect(307, '/login');
+
+ return { user };
+}
diff --git a/src/routes/(app)/admin/+layout.svelte b/src/routes/(app)/admin/+layout.svelte
new file mode 100644
index 00000000..d7561e32
--- /dev/null
+++ b/src/routes/(app)/admin/+layout.svelte
@@ -0,0 +1,10 @@
+<script>
+ import { LandingHeader } from '$comp';
+ export let data;
+ let { user } = data;
+</script>
+
+<LandingHeader {user} />
+<main class="container px-10 md:px-40 mx-auto min-h-[600px] pb-20">
+ <slot />
+</main>
diff --git a/src/routes/(app)/admin/+page.server.js b/src/routes/(app)/admin/+page.server.js
new file mode 100644
index 00000000..c4079a2f
--- /dev/null
+++ b/src/routes/(app)/admin/+page.server.js
@@ -0,0 +1,10 @@
+import { get } from '$lib/utils';
+import { redirect } from '@sveltejs/kit';
+
+export async function load({ parent }) {
+ let { user } = await parent();
+ if (!user) throw redirect(307, '/login');
+
+ let users = (await get('/users')).sort((a, b) => b.balance - a.balance);
+ return { users };
+}
diff --git a/src/routes/(app)/admin/+page.svelte b/src/routes/(app)/admin/+page.svelte
new file mode 100644
index 00000000..1a747f1f
--- /dev/null
+++ b/src/routes/(app)/admin/+page.svelte
@@ -0,0 +1,11 @@
+<script>
+ export let data;
+ let { users } = data;
+</script>
+
+{#each users as { balance, username }}
+ <div class="grid grid-cols-2">
+ <div>{username}</div>
+ <div>{balance}</div>
+ </div>
+{/each}
diff --git a/src/routes/(app)/launch/thanks/[asset]/+page.server.js b/src/routes/(app)/launch/thanks/[asset]/+page.server.js
deleted file mode 100644
index e9e01376..00000000
--- a/src/routes/(app)/launch/thanks/[asset]/+page.server.js
+++ /dev/null
@@ -1 +0,0 @@
-// export const load = ({ parent }) => parent();
diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte
index b8a23605..cf30db35 100644
--- a/src/routes/(app)/payment/[id]/+page.svelte
+++ b/src/routes/(app)/payment/[id]/+page.svelte
@@ -7,6 +7,7 @@
export let data;
let { user, payment: p } = data;
+
let { username } = user;
let { id, amount, created, rate, type, ref, tip, ourfee, fee, currency } = p;
let a = Math.abs(amount);
@@ -26,42 +27,51 @@
{#if p.with}
<div>
<span class="text-lg text-secondary my-auto mr-2">{amount > 0 ? 'From' : 'To'}</span>
- <a href={`/${p.with.username}`}>
- <div class="flex">
- <div class="my-auto">
- <Avatar user={p.with} size={20} />
+ <a href={`/${p.with.username}`}>
+ <div class="flex">
+ <div class="my-auto">
+ <Avatar user={p.with} size={20} />
+ </div>
+ <div class="my-auto ml-1">{p.with.username}</div>
</div>
- <div class="my-auto ml-1">{p.with.username}</div>
- </div>
- </a>
+ </a>
</div>
{/if}
-
<div>
<span class="text-lg text-secondary">Amount</span>
<div>
{f(fiat(a, rate), currency)}
- {#if tip}
- <span class="text-lg">
- + {f(fiat(tip, rate), currency)}
- </span>
- {/if}
- <span class="text-secondary">⚡️{`${s(a)}`}
-
- {#if tip}
- <span class="text-lg">
- + ⚡️{s(tip)}
- </span>
- {/if}
- </span>
+ <span class="text-secondary text-lg">
+ ⚡️{s(a)}
+ </span>
</div>
</div>
+ {#if tip}
+ <div>
+ <span class="text-lg text-secondary">Tip</span>
+ <div>
+ {f(fiat(tip, rate), currency)}
+ <span class="text-secondary text-lg">
+ ⚡️{s(tip)}
+ </span>
+ </div>
+ </div>
+
+ <div>
+ <span class="text-lg text-secondary">Total</span>
+ <div>
+ {f(fiat(a + (tip || 0), rate), currency)}
+ <span class="text-secondary text-lg">⚡️{`${s(a + (tip || 0))}`} </span>
+ </div>
+ </div>
+ {/if}
+
<div>
<span class="text-lg text-secondary">Network fee</span>
<div>
{f(fiat(fee, rate), currency)}
- <span class="text-secondary">⚡️{`${s(fee)}`}</span>
+ <span class="text-secondary text-lg">⚡️{`${s(fee)}`}</span>
</div>
</div>
diff --git a/src/routes/(app)/send/[...text]/+page.svelte b/src/routes/(app)/send/[...text]/+page.svelte
index 5f37cad5..3abc5a1a 100644
--- a/src/routes/(app)/send/[...text]/+page.svelte
+++ b/src/routes/(app)/send/[...text]/+page.svelte
@@ -7,7 +7,6 @@
import { t } from '$lib/translations';
import { AppHeader, Avatar, Icon, Spinner } from '$comp';
import { back, fail } from '$lib/utils';
- import { format, parseISO } from 'date-fns';
export let data;
export let form;
diff --git a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
index b3a944f4..77d5fde6 100644
--- a/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
+++ b/src/routes/(app)/send/bitcoin/[address]/[...amount]/+page.svelte
@@ -22,7 +22,7 @@
loading = false;
if (form?.message?.includes('pin')) $pin = undefined;
if (form) ({ min, max, feeRate, fee, ourfee, subtract } = form);
- if (!ourfee) ourfee = 0;
+ if (!ourfee) ourfee = 0;
if (feeRate) confirmed = true;
stale = false;
};
@@ -77,7 +77,9 @@
{f(toFiat(fee + ourfee, $selectedRate), currency)}
</h2>
<h3 class="text-secondary mb-6 mt-1">⚡️{s(fee + ourfee)}</h3>
- <button class="absolute right-1/4 top-0 text-secondary p-4" on:click={toggleAdjusting}><Icon icon="settings" style="opacity-30" /></button>
+ <button class="absolute right-1/4 top-0 text-secondary p-4" on:click={toggleAdjusting}
+ ><Icon icon="settings" style="opacity-30" /></button
+ >
</div>
</div>
{:else}
@@ -104,7 +106,9 @@
<div class="my-auto">
<Toggle id="subtract" checked={subtract} bind:value={subtract} />
</div>
- <label for="subtract" class="text-secondary text-lg my-auto mb-2">Take fee from amount</label>
+ <label for="subtract" class="text-secondary text-lg my-auto mb-2"
+ >Take fee from amount</label
+ >
</div>
{/if}
diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.js b/src/routes/(app)/send/invoice/[id]/+page.server.js
index dc0d3a3d..c9c2b222 100644
--- a/src/routes/(app)/send/invoice/[id]/+page.server.js
+++ b/src/routes/(app)/send/invoice/[id]/+page.server.js
@@ -1,21 +1,30 @@
import { error, fail, redirect } from '@sveltejs/kit';
-import { fd, btc as asset, auth, get, post } from '$lib/utils';
+import { fd, auth, get, post } from '$lib/utils';
-export async function load({ params: { id }, parent }) {
+export async function load({ params: { id }, parent, url }) {
let { user } = await parent();
- let { amount, address, tip, text, user: recipient } = await get(`/invoice/${id}`);
+ let {
+ amount,
+ address,
+ hash,
+ rate,
+ prompt,
+ tip,
+ text,
+ user: recipient
+ } = await get(`/invoice/${id}`);
+
+ if (prompt && tip === null) throw redirect(307, `/${recipient.username}/invoice/${id}/tip`);
if (recipient.username === user.username) throw error(500, { message: 'Cannot send to self' });
- if (tip) amount += tip;
- return { amount, address, payreq: text, recipient };
+ return { amount, address, hash, tip, rate, payreq: text, recipient };
}
export const actions = {
- default: async ({ cookies, params: { hash }, request }) => {
+ default: async ({ cookies, params: { id }, request }) => {
try {
let body = await fd(request);
- body.amount = parseInt(body.amount);
- body.hash = hash;
+ body.hash = id;
await post('/payments', body, auth(cookies));
} catch (e) {
diff --git a/src/routes/(app)/send/invoice/[id]/+page.svelte b/src/routes/(app)/send/invoice/[id]/+page.svelte
index 7570a61a..c9cb9392 100644
--- a/src/routes/(app)/send/invoice/[id]/+page.svelte
+++ b/src/routes/(app)/send/invoice/[id]/+page.svelte
@@ -5,11 +5,11 @@
import { enhance } from '$app/forms';
import { Avatar, Icon, Numpad, Spinner } from '$comp';
import { page } from '$app/stores';
- import { back, f, s, sats } from '$lib/utils';
+ import { back, f, fiat as toFiat, s, sats } from '$lib/utils';
export let data;
export let form;
- let { address, payreq, recipient, user } = data;
+ let { address, payreq, recipient, rate, tip, user } = data;
let { currency } = user;
let amount = form?.amount || data.amount;
@@ -49,9 +49,23 @@
<h1 class="text-xl md:text-2xl text-secondary mb-2">
{$t('payments.send')}
</h1>
- <p class="text-6xl break-words mb-4">
- {fiat ? f(amountFiat, currency) : '⚡️' + s(amount)}
- </p>
+ <h2 class="text-2xl md:text-3xl font-semibold">
+ {f(toFiat(amount, rate), currency)}
+ {#if tip}
+ <span class="text-lg">
+ + {f(toFiat(tip, rate), currency)}
+ </span>
+ {/if}
+ </h2>
+ <h3 class="text-secondary md:text-lg mb-6 mt-1">
+ ⚡️{s(amount)}
+
+ {#if tip}
+ <span class="text-lg">
+ + ⚡️{s(tip)}
+ </span>
+ {/if}
+ </h3>
<h1 class="text-xl md:text-2xl text-secondary mb-2">{$t('payments.to')}</h1>
diff --git a/src/routes/(app)/send/lightning/[payreq]/+page.server.js b/src/routes/(app)/send/lightning/[payreq]/+page.server.js
index 02511a8f..94231376 100644
--- a/src/routes/(app)/send/lightning/[payreq]/+page.server.js
+++ b/src/routes/(app)/send/lightning/[payreq]/+page.server.js
@@ -1,5 +1,5 @@
import { fail, redirect } from '@sveltejs/kit';
-import { fd, btc as asset, auth, post } from '$lib/utils';
+import { fd, auth, post } from '$lib/utils';
export async function load({ params }) {
return post('/parse', params);
diff --git a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
index ad0219e0..c63328e2 100644
--- a/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
+++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.server.js
@@ -1,5 +1,5 @@
import { error, fail, redirect } from '@sveltejs/kit';
-import { fd, btc as asset, auth, get, post } from '$lib/utils';
+import { fd, auth, get, post } from '$lib/utils';
export async function load({ params, parent }) {
let { user } = await parent();
@@ -13,9 +13,6 @@ export const actions = {
default: async ({ cookies, request }) => {
try {
let body = await fd(request);
- let { amount } = body;
- amount = parseInt(amount);
-
await post('/payments', body, auth(cookies));
} catch (e) {
console.log(e);
diff --git a/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte b/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
index 74da1fa8..af6a6368 100644
--- a/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
+++ b/src/routes/(app)/send/user/[username]/[...amount]/+page.svelte
@@ -17,22 +17,28 @@
let a,
af,
fiat = !amount,
- hash;
+ hash,
+ rate,
+ amountFiat = 0;
- $: amountFiat = amount * ($selectedRate / sats);
+ selectedRate.subscribe((r) => rate || (amountFiat = amount * (r / sats)));
let setAmount = async () => {
amount = a;
- amountFiat = af;
-
+ amountFiat = parseFloat(af).toFixed(2);
+ rate = fiat ? (sats * amountFiat) / amount : $selectedRate;
({ hash } = await post(`/${subject.username}/invoice`, {
invoice: {
amount,
+ hash: 'internal',
rate: rates[subject.currency],
+ prompt: subject.prompt,
type: 'internal'
},
user: subject
}));
+
+ goto(`/${subject.username}/invoice/${hash}`);
};
let loading;
@@ -54,17 +60,7 @@
{/if}
<div class="container px-4 mt-20 max-w-xl mx-auto space-y-8">
- {#if amount}
- <h1 class="text-center text-3xl md:text-4xl font-bold mb-6">Sending</h1>
- <div class="text-center mb-8">
- <h2 class="text-2xl md:text-3xl font-semibold">
- {f(amountFiat, currency)}
- </h2>
- <h3 class="text-secondary md:text-lg mb-6 mt-1">{sat(amount)}</h3>
- </div>
- {:else}
- <Numpad bind:amount={a} bind:amountFiat={af} {currency} bind:fiat />
- {/if}
+ <Numpad bind:amount={a} bind:amountFiat={af} {currency} bind:fiat />
<form method="POST" use:enhance on:submit={submit}>
<input name="amount" value={amount} type="hidden" />
diff --git a/src/routes/(app)/sent/+page.svelte b/src/routes/(app)/sent/+page.svelte
index 72511487..dbd18c2e 100644
--- a/src/routes/(app)/sent/+page.svelte
+++ b/src/routes/(app)/sent/+page.svelte
@@ -7,7 +7,7 @@
export let data;
let { payments, user } = data;
- let { currency, username } = user;
+ let { currency, username } = user;
let payment = payments[0];
let { amount, rate, tip } = payment;
@@ -20,7 +20,7 @@
<div class="flex w-full max-w-[200px] mx-auto py-20" in:scale={{ start: 0.5 }}>
<Icon icon="check" style="mx-auto" />
</div>
- <h1 class="text-3xl md:text-4xl font-bold mb-6">Sent!</h1>
+ <h1 class="text-3xl md:text-4xl font-bold mb-6">{$t('payments.sent')}!</h1>
<h2 class="text-2xl md:text-3xl font-semibold">
{f(fiat(amount, rate), currency)}
{#if tip}
diff --git a/src/routes/(app)/support/+page.server.js b/src/routes/(app)/support/+page.server.js
deleted file mode 100644
index 6d0af616..00000000
--- a/src/routes/(app)/support/+page.server.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { fd, post } from '$lib/utils';
-import { fail } from '@sveltejs/kit';
-
-export const actions = {
- default: async ({ cookies, request }) => {
- let form = await fd(request);
- try {
- await post('/email', form);
- } catch (e) {
- return fail(400, { error: e.message });
- }
-
- return { success: true };
- }
-};
diff --git a/src/routes/(app)/support/+page.svelte b/src/routes/(app)/support/+page.svelte
index d00cc069..8dbfc1af 100644
--- a/src/routes/(app)/support/+page.svelte
+++ b/src/routes/(app)/support/+page.svelte
@@ -1,10 +1,10 @@
<script>
- import { enhance } from '$app/forms';
+ import { fail, success, post } from '$lib/utils';
import { scale } from 'svelte/transition';
import { AppHeader, Icon } from '$comp';
import { t } from '$lib/translations';
- export let data, form;
+ export let data;
let { user } = data;
$: if (user) {
@@ -13,41 +13,58 @@
let email;
let message;
+ let sent;
+
+ let submit = (e) => {
+ e.preventDefault();
+ grecaptcha.ready(() => {
+ grecaptcha
+ .execute('6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM', { action: 'submit' })
+ .then((token) =>
+ post('/support', { email, message, token })
+ .then(() => (sent = true))
+ .catch(() => fail('problem submitting'))
+ );
+ });
+ };
</script>
+<svelte:head>
+ <script
+ src="https://www.google.com/recaptcha/api.js?render=6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM"
+ ></script>
+</svelte:head>
+
{#if user}
<AppHeader {data} />
{/if}
-{#if form?.error}
- <div class="text-red-600 text-center">
- {form.error}
- </div>
-{/if}
+<div class="container px-4 max-w-lg mx-auto mt-20 space-y-8">
+ {#if sent}
+ <h1 class="text-center text-3xl md:text-4xl font-semibold">Thank you!</h1>
+ <div class="text-center text-lg text-secondary">Someone will be in touch shortly.</div>
-<div class="container px-4 max-w-lg mx-auto mt-20 space-y-5">
- {#if form?.success}
- <h1 class="text-center text-3xl md:text-4xl font-semibold mb-8">Thank you!</h1>
- <p class="text-center mb-8">Someone will be in touch shortly.</p>
-
- <a href={user ? `/${user.username}` : '/'}>
- <button class="rounded-full border py-2 px-5 font-bold hover:opacity-80 w-full mb-2"
- >Done</button
- >
- </a>
+ <div>
+ <a href={user ? `/${user.username}` : '/'}>
+ <button
+ class="rounded-full border py-2 px-5 font-bold hover:opacity-80 w-full bg-black text-white"
+ >Ok</button
+ >
+ </a>
+ </div>
{:else}
<h1 class="text-center text-3xl md:text-4xl font-semibold mb-8">
{$t('user.support.header')}
</h1>
- <p class="text-secondary">
+ <p class="text-secondary text-lg">
Fill out this form or email us directly at <a
class="underline"
> and we'll do our best to get back to you in a timely manner.
</p>
- <form method="POST" use:enhance>
+ <form on:submit={submit}>
<div class="mb-4">
<label for="account" class="font-semibold">{$t('user.support.accountName')}</label>
<input
@@ -79,7 +96,7 @@
<button
type="submit"
disabled={!email || !message}
- class="{!email || !message
+ class="g-recaptcha {!email || !message
? 'opacity-50'
: 'opacity-100'} bg-black text-white font-bold rounded-xl py-3 w-full mx-auto {email &&
message
diff --git a/src/routes/(app)/support/+server.js b/src/routes/(app)/support/+server.js
new file mode 100644
index 00000000..6b1778a8
--- /dev/null
+++ b/src/routes/(app)/support/+server.js
@@ -0,0 +1,9 @@
+import { json } from '@sveltejs/kit';
+import { auth, post } from '$lib/utils';
+
+export const prerender = false;
+
+export async function POST({ cookies, request }) {
+ let result = await post('/email', await request.json(), auth(cookies));
+ return json(result);
+}
diff --git a/src/routes/(static)/+layout.server.js b/src/routes/(static)/+layout.server.js
new file mode 100644
index 00000000..2deeebc5
--- /dev/null
+++ b/src/routes/(static)/+layout.server.js
@@ -0,0 +1,16 @@
+import { get, post, auth, protectedRoutes } from '$lib/utils';
+
+export async function load({ cookies, request, url, params }) {
+ let token = cookies.get('token');
+ let rate,
+ rates = { USD: 1 };
+
+ let user;
+ if (token) {
+ try {
+ user = await get('/me', auth(cookies));
+ } catch (e) {}
+ }
+
+ return { user };
+}
diff --git a/src/routes/(static)/+layout.svelte b/src/routes/(static)/+layout.svelte
index 6e31f17e..3d596dff 100644
--- a/src/routes/(static)/+layout.svelte
+++ b/src/routes/(static)/+layout.svelte
@@ -1,8 +1,12 @@
<script>
+ import { SvelteToast } from '@zerodevx/svelte-toast';
import { Footer, LandingHeader } from '$comp';
+ export let data;
+ $: ({ user } = data);
</script>
-<LandingHeader />
+<SvelteToast options={{ reversed: true, intro: { y: 192 } }} />
+<LandingHeader {user} />
<main class="container flex px-10 md:px-40 mx-auto min-h-[600px] pb-20">
<slot />
</main>
diff --git a/src/routes/(static)/bounties/+page.svelte b/src/routes/(static)/bounties/+page.svelte
index d53a8edc..32a962fc 100644
--- a/src/routes/(static)/bounties/+page.svelte
+++ b/src/routes/(static)/bounties/+page.svelte
@@ -54,8 +54,11 @@
<p>
<b>⚡️750,000</b> will be awarded once the merchant has accepted bitcoin for 6 months. We'll be
sending
- <a href="https://btcmap.org/leaderboard" class="text-black hover:underline" target="_blank"
- >shadowy supertaggers</a
+ <a
+ href="https://btcmap.org/leaderboard"
+ class="text-black hover:underline"
+ target="_blank"
+ rel="noreferrer">shadowy supertaggers</a
> to check up on them periodically.
</p>
diff --git a/src/routes/(static)/docs/+page.server.js b/src/routes/(static)/docs/+page.server.js
new file mode 100644
index 00000000..e80365fa
--- /dev/null
+++ b/src/routes/(static)/docs/+page.server.js
@@ -0,0 +1,4 @@
+export let load = async ({ cookies }) => {
+ let token = cookies.get('token');
+ return { token };
+};
diff --git a/src/routes/(static)/docs/+page.svelte b/src/routes/(static)/docs/+page.svelte
index c634cd76..8ddb98cd 100644
--- a/src/routes/(static)/docs/+page.svelte
+++ b/src/routes/(static)/docs/+page.svelte
@@ -1,17 +1,77 @@
-Coming soon
-<!-- <h1>API Examples</h1> -->
-<!-- <pre><code class="whitespace-pre-wrap" -->
-<!-- >{` -->
-<!-- api="https://coinos.io/api" -->
-<!-- json="Content-Type: application/json" -->
-<!-- -->
-<!-- username=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) -->
-<!-- password="pw" -->
-<!-- -->
-<!-- echo Create user $username with password $password -->
-<!-- curl -s "$api/register" -H "$json" -d '{"user": {"username": "'$username'", "password": "'$password'", "confirm": "'$password'"}}' > /dev/null -->
-<!-- echo "" -->
-<!-- -->
+<script>
+ import { copy } from '$lib/utils';
+ import { Code, Icon } from '$comp';
+ import { PUBLIC_COINOS_URL } from '$env/static/public';
+ export let data;
+ let { token } = data;
+ let api = PUBLIC_COINOS_URL + '/api';
+
+ if (!token) token = '<login to see your token here>';
+ let tokenSample = `export token="${token}"`;
+</script>
+
+<div class="space-y-8 lg:text-xl mt-20 w-full max-w-full">
+ <h1 class="text-4xl">API Documentation</h1>
+
+ <p class="text-secondary">
+ Coinos has a simple REST API that can be used to register accounts, fetch data, and make
+ payments
+ </p>
+
+ <p class="text-secondary">* = required parameter</p>
+
+ <h2 class="text-2xl">Base URL</h2>
+ <div class="bg-black text-white rounded-lg p-4 flex gap-4">
+ <div>{api}</div>
+ <button class="ml-auto my-auto invert opacity-90" on:click={() => copy(api)}
+ ><Icon icon="copy" style="w-10 max-w-none" /></button
+ >
+ </div>
+
+ <h2 class="text-2xl">Auth Token</h2>
+ <p class="text-secondary">This JWT authorizes you to use the API</p>
+ <div class="bg-black text-white rounded-lg p-4 flex gap-4">
+ <div class="w-full break-all">{tokenSample}</div>
+ <button class="ml-auto my-auto invert opacity-90" on:click={() => copy(tokenSample)}
+ ><Icon icon="copy" style="w-10 max-w-none" /></button
+ >
+ </div>
+
+ <h2 class="text-2xl">POST /register</h2>
+ <p class="text-secondary">Register a new user account with a username and password</p>
+ <Code sample="register" />
+
+ <h2 class="text-2xl">POST /login</h2>
+ <p class="text-secondary">Login to an account to get its auth token</p>
+ <Code sample="login" />
+
+ <h2 class="text-2xl">POST /invoice</h2>
+ <p class="text-secondary">Create an invoice.</p>
+ <div>
+ <div class="text-secondary">
+ <span class="font-mono font-bold">type</span>: bitcoin or lightning
+ </div>
+ <div class="text-secondary">
+ <span class="font-mono font-bold">amount</span>: amount in satoshis
+ </div>
+ <div class="text-secondary">
+ <span class="font-mono font-bold">webhook</span>: an endpoint to hit when the invoice is paid
+ </div>
+ <div class="text-secondary">
+ <span class="font-mono font-bold">secret</span>: a secret string for the webhook to check
+ </div>
+ </div>
+
+ hey
+ <Code sample="invoice" />
+
+ <h2 class="text-2xl">GET /invoice/:hash</h2>
+ <p class="text-secondary">
+ Fetch an invoice by passing a bitcoin address or lightning payment hash
+ </p>
+ <Code sample="fetchInvoice" />
+</div>
+
<!-- echo Login and get an API token -->
<!-- token=$(curl -s "$api/taboggan" -H "$json" -d '{"username": "'$username'", "password": "'$password'"}' | jq -r .token) -->
<!-- echo $token -->
@@ -73,5 +133,5 @@ Coming soon
<!-- -->
<!-- curl -s "$api/bitcoin/send" -H "$json" -H "$auth" -d '{"address": "bc1qqc9azhdjjpdxenrndt8cw4tw9del5pyrjn0wln", "tx": { "hex": "'$hex'", "fee": "'$fee'" }}' | jq -r '{ amount, fee, balance: (.account.balance)}' -->
<!-- echo "" -->
-<!-- `}</code -->
-<!-- ></pre> -->
+
+<!-- `} -->
diff --git a/src/routes/+page.server.js b/src/routes/+page.server.js
index 4bcc6089..7936e88b 100644
--- a/src/routes/+page.server.js
+++ b/src/routes/+page.server.js
@@ -1,6 +1,6 @@
import { get } from '$lib/utils';
-export const prerender = true;
+// export const prerender = true;
export let load = async () => {
let data = { locations: [] };