13 Commits

Author SHA1 Message Date
4e0d4bf86d 0.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-17 14:59:07 -06:00
333bcbfe7e Remove Sass dependency
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-17 13:30:10 -06:00
875af6d14c Merge pull request 'Add transaction history view to wallet' (#66) from feature/wallet_history into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #66
2022-03-17 19:28:58 +00:00
8f87a03060 Merge pull request 'Finish Tailwind migration' (#67) from chore/finish_tailwind_migration into feature/wallet_history
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: #67
2022-03-17 19:27:52 +00:00
7838fe5f34 Remove legacy CSS build from task
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-03-17 13:26:36 -06:00
512798d122 Port last remaining styles from legacy to Tailwind
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2022-03-17 13:24:13 -06:00
384c28aaaa Build PRs for all branches
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-17 13:06:33 -06:00
8e5d6dabdc Port most remaining legacy styles to Tailwind
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-03-11 10:15:09 -06:00
ade9261c2c Remove obsolete CSS 2022-03-11 09:52:11 -06:00
bd2a161306 Add tab menu to wallet pages
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-02 19:18:28 -06:00
78c243c985 Add wallet transactions
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-02 18:43:22 -06:00
cf62bfc5c2 WIP Add wallet transactions route, view
All checks were successful
continuous-integration/drone/push Build is passing
Adds a new component for the wallet summary as well, and makes the
component tests work with RSpec.
2022-03-02 15:31:39 -06:00
10f179a095 Port shared CSS for tables to Tailwind 2022-03-02 15:30:50 -06:00
30 changed files with 218 additions and 288 deletions

View File

@@ -29,9 +29,6 @@ steps:
- yarn install - yarn install
- rake css:build - rake css:build
- rake spec - rake spec
when:
branch:
- master
- name: rebuild-cache - name: rebuild-cache
image: drillster/drone-volume-cache image: drillster/drone-volume-cache
volumes: volumes:

View File

@@ -7,3 +7,4 @@
@import "components/forms"; @import "components/forms";
@import "components/links"; @import "components/links";
@import "components/notifications"; @import "components/notifications";
@import "components/tables";

View File

@@ -1,11 +1,12 @@
@layer base { @layer base {
body { body {
@apply leading-none @apply leading-none bg-cover bg-fixed;
background-image: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(13,79,153,0.8) 100%), url('/img/bg-1.jpg');
} }
/* h1, h2, h3 { */ body#admin {
/* @apply font-light; */ background-image: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(153,12,14,0.9) 100%), url('/img/bg-1.jpg');
/* } */ }
h1 { h1 {
@apply text-3xl uppercase; @apply text-3xl uppercase;
@@ -26,4 +27,16 @@
main section:first-of-type { main section:first-of-type {
@apply pt-0; @apply pt-0;
} }
main p {
@apply mb-4 leading-6;
}
main ul {
@apply mb-6;
}
main ul li {
@apply leading-6;
}
} }

View File

@@ -0,0 +1,22 @@
@layer components {
table {
@apply w-full;
}
table thead tr {
@apply text-left;
}
table th {
@apply pb-3.5 text-sm font-normal uppercase text-gray-500;
}
table th:not(:last-of-type),
table td:not(:last-of-type) {
@apply pr-2;
}
table td {
@apply py-2;
}
}

View File

@@ -1,2 +0,0 @@
@import "legacy/layout";
@import "legacy/main_nav";

View File

@@ -1,118 +0,0 @@
@import "variables";
@import "mediaqueries";
body {
background: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(13,79,153,0.8) 100%),
url('/img/bg-1.jpg');
background-size: cover;
background-attachment: fixed;
}
body#admin {
background: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(153,12,14,0.9) 100%),
url('/img/bg-1.jpg');
background-size: cover;
background-attachment: fixed;
}
.ks-site-icon {
svg {
display: inline-block;
height: 1.875rem;
vertical-align: top;
width: auto;
}
}
#wrapper {
width: 100%;
text-align: center;
> header {
margin: 0 auto;
padding: 4rem 0;
text-align: center;
background: linear-gradient(35deg, rgba(255,0,255,0.2) 0, rgba(13,79,153,0.8) 100%),
url('/img/bg-1.jpg');
background-size: cover;
@include media-max(small) {
padding: 3rem 0;
}
h1 {
color: #fff;
span.project-name {
display: none;
}
}
p.current-user {
color: rgba(255,255,255,0.6);
@include media-max(small) {
font-size: 0.85rem;
}
}
a {
color: rgba(255,255,255,0.6);
transition: color 0.1s linear;
&:hover, &:active {
color: #fff;
}
}
}
}
main {
p {
line-height: 1.5rem;
margin-bottom: 1rem;
&.notice {
text-align: center;
}
}
ul {
margin-bottom: 1.5rem;
li {
line-height: 1.5rem;
}
}
table {
width: 100%;
th {
color: $text-color-discreet;
font-weight: normal;
text-transform: uppercase;
font-size: 0.85rem;
padding-bottom: 0.825rem;
}
td {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
}
}
.grid {
display: grid;
&.services {
grid-template-columns: 1fr 1fr 1fr;
grid-row-gap: 1rem;
grid-column-gap: 2rem;
@include media-max(small) {
grid-template-columns: 1fr;
}
}
}

View File

@@ -1,54 +0,0 @@
@import "variables";
@import "mediaqueries";
#main-nav {
width: 100%;
text-align: center;
background-color: #efefef;
.wrapper {
width: $content-width;
max-width: $content-max-width;
margin: 0 auto;
}
ul {
@include media-max(large) {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
li {
@include media-min(large) {
display: inline;
}
@include media-max(large) {
display: block;
}
a {
display: inline-block;
padding: 1.5rem 2rem;
text-decoration: none;
color: $text-color-discreet;
@include media-max(large) {
display: block;
text-align: center;
padding-left: 0;
padding-right: 0;
}
@include media-max(small) {
font-size: 0.85rem;
}
&.active {
color: $text-color-body;
border-bottom: 2px solid #4ea2df;
}
}
}
}
}

View File

@@ -1,33 +0,0 @@
$breakpoints-max: (
small: 600px,
medium: 960px,
large: 1280px
);
$breakpoints-min: (
small: 601px,
medium: 961px,
large: 1281px
);
@mixin media-max($screen-size) {
@if map-has-key($breakpoints-max, $screen-size) {
@media (max-width: map-get($breakpoints-max, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
@mixin media-min($screen-size) {
@if map-has-key($breakpoints-min, $screen-size) {
@media (min-width: map-get($breakpoints-min, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}

View File

@@ -1,13 +0,0 @@
$content-width: 800px;
$content-max-width: 100%;
$text-color-body: #222;
$text-color-discreet: #888;
$background-color-notice: #efffc4;
$background-color-alert: #fff4c2;
$color-blue: #0d4f99;
$color-purple: #8955a0;
$color-red-bright: #c00;
$color-red-dark: #990c0e;

View File

@@ -0,0 +1,18 @@
<section class="w-full grid grid-cols-1 md:grid-cols-12 md:mb-0">
<div class="md:col-span-8">
<p>
Send and receive sats via the Bitcoin Lightning Network.
</p>
</div>
<div class="md:col-span-4 mt-4 md:mt-0">
<p class="font-mono md:text-right mb-0 p-4 border border-gray-300 rounded-lg overflow-hidden">
<% if @balance %>
<span class="text-xl"><%= number_with_delimiter @balance %> sats</span><br>
<span class="text-sm text-gray-500">Available balance</span>
<% else %>
<span class="text-xl">n/a sats</span><br>
<span class="text-sm text-gray-500">Balance unavailable</span>
<% end %>
</p>
</div>
</section>

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
class WalletSummaryComponent < ViewComponent::Base
def initialize(balance:)
@balance = balance
end
end

View File

@@ -3,10 +3,10 @@ require "rqrcode"
class WalletController < ApplicationController class WalletController < ApplicationController
before_action :require_user_signed_in before_action :require_user_signed_in
before_action :authenticate_with_lndhub before_action :authenticate_with_lndhub
before_action :set_current_section
before_action :fetch_balance
def index def index
@current_section = :wallet
@wallet_url = "lndhub://#{current_user.ln_login}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}" @wallet_url = "lndhub://#{current_user.ln_login}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}"
qrcode = RQRCode::QRCode.new(@wallet_url) qrcode = RQRCode::QRCode.new(@wallet_url)
@@ -20,8 +20,10 @@ class WalletController < ApplicationController
class: 'inline-block' class: 'inline-block'
} }
) )
end
@balance = fetch_balance rescue nil def transactions
@transactions = fetch_transactions
end end
private private
@@ -39,9 +41,41 @@ class WalletController < ApplicationController
# TODO add exception tracking # TODO add exception tracking
end end
def set_current_section
@current_section = :wallet
end
def fetch_balance def fetch_balance
lndhub = Lndhub.new lndhub = Lndhub.new
data = lndhub.balance @ln_auth_token data = lndhub.balance @ln_auth_token
data["BTC"]["AvailableBalance"] @balance = data["BTC"]["AvailableBalance"] rescue nil
end
def fetch_transactions
lndhub = Lndhub.new
txs = lndhub.gettxs @ln_auth_token
invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]}
process_transactions(txs + invoices)
end
def process_transactions(txs)
txs.collect do |tx|
if tx["type"] == "bitcoind_tx"
tx["amount_sats"] = (tx["amount"] * 100000000).to_i
tx["datetime"] = Time.at(tx["time"].to_i)
tx["title"] = "Received"
tx["description"] = "On-chain topup"
tx["received"] = true
else
tx["amount_sats"] = tx["value"] || tx["amt"]
tx["datetime"] = Time.at(tx["timestamp"].to_i)
tx["title"] = tx["type"] == "paid_invoice" ? "Sent" : "Received"
tx["description"] = tx["memo"] || tx["description"]
tx["received"] = tx["type"] == "user_invoice"
end
end
txs.sort{ |a,b| b["datetime"] <=> a["datetime"] }
end end
end end

View File

@@ -46,6 +46,14 @@ class Lndhub
get "balance", user_token || auth_token get "balance", user_token || auth_token
end end
def gettxs(user_token)
get "gettxs", user_token || auth_token
end
def getuserinvoices(user_token)
get "getuserinvoices", user_token || auth_token
end
def addinvoice(payload) def addinvoice(payload)
invoice = post "addinvoice", { invoice = post "addinvoice", {
amt: payload[:amount], amt: payload[:amount],

View File

@@ -2,14 +2,14 @@
<%= render MainSimpleComponent.new do %> <%= render MainSimpleComponent.new do %>
<% if @donations.any? %> <% if @donations.any? %>
<table class="w-full"> <table>
<thead> <thead>
<tr class="text-left"> <tr>
<th>User</th> <th>User</th>
<th>Amount BTC</th> <th class="text-right">Amount BTC</th>
<th>in EUR</th> <th class="text-right">in EUR</th>
<th>in USD</th> <th class="text-right">in USD</th>
<th>Public name</th> <th class="pl-2">Public name</th>
<th>Date</th> <th>Date</th>
<th colspan="3"></th> <th colspan="3"></th>
</tr> </tr>
@@ -19,10 +19,10 @@
<% @donations.each do |donation| %> <% @donations.each do |donation| %>
<tr> <tr>
<td><%= donation.user.address %></td> <td><%= donation.user.address %></td>
<td><%= sats_to_btc donation.amount_sats %> BTC</td> <td class="text-right"><%= sats_to_btc donation.amount_sats %></td>
<td><% if donation.amount_eur.present? %><%= number_to_currency donation.amount_eur / 100, unit: "" %><% end %></td> <td class="text-right"><% if donation.amount_eur.present? %><%= number_to_currency donation.amount_eur / 100, unit: "" %><% end %></td>
<td><% if donation.amount_usd.present? %><%= number_to_currency donation.amount_usd / 100, unit: "" %><% end %></td> <td class="text-right"><% if donation.amount_usd.present? %><%= number_to_currency donation.amount_usd / 100, unit: "" %><% end %></td>
<td><%= donation.public_name %></td> <td class="pl-2"><%= donation.public_name %></td>
<td><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d") : "" %></td> <td><%= donation.paid_at ? donation.paid_at.strftime("%Y-%m-%d") : "" %></td>
<td><%= link_to 'Show', admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td> <td><%= link_to 'Show', admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td>
<td><%= link_to 'Edit', edit_admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td> <td><%= link_to 'Edit', edit_admin_donation_path(donation), class: 'btn btn-sm btn-gray' %></td>

View File

@@ -14,7 +14,7 @@
<h3>Accepted (<%= @invitations_used.length %>)</h3> <h3>Accepted (<%= @invitations_used.length %>)</h3>
<table> <table>
<thead> <thead>
<tr class="text-left"> <tr>
<th>Token</th> <th>Token</th>
<th>Accepted</th> <th>Accepted</th>
<th>Invited user</th> <th>Invited user</th>

View File

@@ -13,7 +13,7 @@
<table> <table>
<thead> <thead>
<tr class="text-left"> <tr>
<th>UID</th> <th>UID</th>
<th>E-Mail</th> <th>E-Mail</th>
<th>Admin</th> <th>Admin</th>

View File

@@ -6,7 +6,7 @@
Your Kosmos account and password currently give you access to these Your Kosmos account and password currently give you access to these
services: services:
</p> </p>
<div class="grid services mt-12"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 services mt-12">
<div> <div>
<h3 class="mb-3.5"> <h3 class="mb-3.5">
<%= link_to "Chat", "https://wiki.kosmos.org/Services:Chat", class: "ks-text-link" %> <%= link_to "Chat", "https://wiki.kosmos.org/Services:Chat", class: "ks-text-link" %>

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link-2"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link-2 <%= custom_class %>"><path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line></svg>

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link <%= custom_class %>"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-zap"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-zap <%= custom_class %>"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 303 B

View File

@@ -8,7 +8,7 @@
</p> </p>
<table class="mt-12"> <table class="mt-12">
<thead> <thead>
<tr class="text-left"> <tr>
<th>URL</th> <th>URL</th>
</tr> </tr>
</thead> </thead>
@@ -33,7 +33,7 @@
<h3>Accepted Invitations</h3> <h3>Accepted Invitations</h3>
<table> <table>
<thead> <thead>
<tr class="text-left"> <tr>
<th class="hidden md:block">ID</th> <th class="hidden md:block">ID</th>
<th>Accepted</th> <th>Accepted</th>
<th>Invited user</th> <th>Invited user</th>

View File

@@ -6,7 +6,6 @@
<%= csp_meta_tag %> <%= csp_meta_tag %>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://assets.kosmos.org/fonts/open-sans/open-sans.css" rel="stylesheet"> <link href="https://assets.kosmos.org/fonts/open-sans/open-sans.css" rel="stylesheet">
<%= stylesheet_link_tag 'legacy', "data-turbo-track": "reload" %>
<%= stylesheet_link_tag 'application', "data-turbo-track": "reload" %> <%= stylesheet_link_tag 'application', "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %> <%= javascript_importmap_tags %>
</head> </head>
@@ -18,7 +17,7 @@
<div class="flex items-center justify-between h-16 px-4 sm:px-0"> <div class="flex items-center justify-between h-16 px-4 sm:px-0">
<div class="flex items-center"> <div class="flex items-center">
<div class="ks-site-icon flex-shrink-0"> <div class="ks-site-icon flex-shrink-0">
<%= render partial: "shared/icons/comet" %> <%= render partial: "shared/icons/comet", locals: { custom_class: "inline-block align-top w-auto h-7" } %>
</div> </div>
<% if user_signed_in? && current_user.confirmed? %> <% if user_signed_in? && current_user.confirmed? %>
<div class="hidden md:block"> <div class="hidden md:block">

View File

@@ -1 +1 @@
<svg id="icon-comet" width="65.364" height="55.773" enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 65.364 55.773" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g id="layer1" transform="translate(28.868 20.259)" fill="#fff"><path id="path2" d="m22.81-9.2546-0.0137-0.0072c-0.0445-0.0196-0.0895-0.04052-0.13335-0.06078l-23.822-10.937s2.0034 9.219 2.914 11.778c0 0-27.292-8.1582-30.623-8.9354 1.0916 4.2618 20.006 40.848 20.006 40.848 3.8225 7.7608 12.677 12.083 21.912 12.083 12.949 0 23.446-10.497 23.446-23.446 6.6e-4 -9.4655-5.609-17.62-13.685-21.323z" fill="#fff" stroke-width=".65365"/></g></svg> <svg id="icon-comet" class="<%= custom_class %>" width="65.364" height="55.773" enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 65.364 55.773" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g id="layer1" transform="translate(28.868 20.259)" fill="#fff"><path id="path2" d="m22.81-9.2546-0.0137-0.0072c-0.0445-0.0196-0.0895-0.04052-0.13335-0.06078l-23.822-10.937s2.0034 9.219 2.914 11.778c0 0-27.292-8.1582-30.623-8.9354 1.0916 4.2618 20.006 40.848 20.006 40.848 3.8225 7.7608 12.677 12.083 21.912 12.083 12.949 0 23.446-10.497 23.446-23.446 6.6e-4 -9.4655-5.609-17.62-13.685-21.323z" fill="#fff" stroke-width=".65365"/></g></svg>

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 655 B

View File

@@ -1,22 +1,14 @@
<%= render HeaderComponent.new(title: "Wallet") %> <%= render HeaderComponent.new(title: "Wallet") %>
<%= render MainSimpleComponent.new do %> <%= render MainSimpleComponent.new do %>
<section class="w-full grid grid-cols-1 md:grid-cols-12 md:mb-0"> <%= render WalletSummaryComponent.new(balance: @balance) %>
<div class="md:col-span-8">
<p> <section>
Send and receive sats via the Bitcoin Lightning Network. <div class="border-b border-gray-200">
</p> <nav class="-mb-px flex" aria-label="Tabs">
</div> <%= link_to "Info", wallet_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
<div class="md:col-span-4 mt-4 md:mt-0"> <%= link_to "Transactions", wallet_transactions_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
<p class="font-mono md:text-right mb-0 p-4 border border-gray-300 rounded-lg overflow-hidden"> </nav>
<% if @balance %>
<span class="text-xl"><%= number_with_delimiter @balance %> sats</span><br>
<span class="text-sm text-gray-500">Available balance</span>
<% else %>
<span class="text-xl">n/a sats</span><br>
<span class="text-sm text-gray-500">Balance unavailable</span>
<% end %>
</p>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,57 @@
<%= render HeaderComponent.new(title: "Wallet") %>
<%= render MainSimpleComponent.new do %>
<%= render WalletSummaryComponent.new(balance: @balance) %>
<section>
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= link_to "Info", wallet_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
<%= link_to "Transactions", wallet_transactions_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
</nav>
</div>
</section>
<section>
<h3 class="hidden">Transactions</h3>
<% if @transactions.any? %>
<ul class="list-none">
<% @transactions.each do |tx| %>
<li class="py-4 md:py-4 grid gap-y-1 gap-x-2 grid-cols-4 border-b border-dotted border-gray-300">
<h3 class="col-span-2 md:col-span-3 mb-0">
<% if tx["type"] == "bitcoind_tx" %>
<span class="inline-block">
<%= render partial: "icons/link-2", locals: { custom_class: "text-emerald-500 h-4 w-4 mr-0.5" } %>
</span>
<% else %>
<span class="inline-block">
<%= render partial: "icons/zap", locals: { custom_class: "text-amber-500 h-4 w-4 mr-0.5" } %>
</span>
<% end %>
<%= tx["title"] %>
</h3>
<p class="col-span-2 md:col-span-1 mb-0 text-right">
<span class="text-xl font-mono <%= tx["received"] ? "text-emerald-600" : "" %>">
<%= tx["received"] ? "+" : "" %><%= number_with_delimiter tx["amount_sats"] %>
<span class="hidden md:inline">sats</span>
</span>
</p>
<p class="col-span-4 md:col-span-3 mb-0 text-gray-500">
<%= tx["description"].present? ? tx["description"] : raw("<span class='text-gray-400'>No memo</span>") %>
</p>
<p class="col-span-4 md:col-span-1 md:text-right mb-0">
<span class="col-span-2 md:col-span-1 text-sm text-gray-500">
<%= tx["datetime"].strftime("%B %e, %H:%M") %>
</span>
</p>
</li>
<% end %>
</ul>
<% else %>
<p class="text-gray-500">
No transactions yet. As soon as you start receiving sats, you will find some entries here.
</p>
<% end %>
</section>
<% end %>

View File

@@ -20,6 +20,7 @@ Rails.application.routes.draw do
resources :donations resources :donations
get 'wallet', to: 'wallet#index' get 'wallet', to: 'wallet#index'
get 'wallet/transactions', to: 'wallet#transactions'
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/} get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/} get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}

View File

@@ -9,13 +9,11 @@
"postcss-import": "^14.0.2", "postcss-import": "^14.0.2",
"postcss-nested": "^5.0.6", "postcss-nested": "^5.0.6",
"postcss-preset-env": "^7.3.1", "postcss-preset-env": "^7.3.1",
"sass": "^1.49.7",
"tailwindcss": "^3.0.22" "tailwindcss": "^3.0.22"
}, },
"version": "0.3.0", "version": "0.4.0",
"scripts": { "scripts": {
"build:css:sass": "sass ./app/assets/stylesheets/legacy.sass.scss ./app/assets/builds/legacy.css --no-source-map --load-path=node_modules",
"build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css", "build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css",
"build:css": "yarn run build:css:sass && yarn run build:css:tailwind" "build:css": "yarn run build:css:tailwind"
} }
} }

View File

@@ -0,0 +1,11 @@
require "rails_helper"
RSpec.describe WalletSummaryComponent, type: :component do
it "renders the balance as a human-readable number" do
expect(
render_inline(described_class.new(balance: 2301000)) {}.css("section").to_html
).to include(
"2,301,000 sats"
)
end
end

View File

@@ -10,6 +10,8 @@ require 'capybara'
require 'devise' require 'devise'
require 'support/controller_macros' require 'support/controller_macros'
require 'support/database_cleaner' require 'support/database_cleaner'
require "view_component/test_helpers"
require "capybara/rspec"
# Requires supporting ruby files with custom matchers and macros, etc, in # Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@@ -70,5 +72,8 @@ RSpec.configure do |config|
config.include Warden::Test::Helpers config.include Warden::Test::Helpers
config.include FactoryBot::Syntax::Methods config.include FactoryBot::Syntax::Methods
config.include ActiveJob::TestHelper, type: :job config.include ActiveJob::TestHelper, type: :job
config.include ViewComponent::TestHelpers, type: :component
config.include Capybara::RSpecMatchers, type: :component
config.extend ControllerMacros, :type => :controller config.extend ControllerMacros, :type => :controller
end end

View File

@@ -202,7 +202,7 @@ chalk@^4.1.2:
ansi-styles "^4.1.0" ansi-styles "^4.1.0"
supports-color "^7.1.0" supports-color "^7.1.0"
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: chokidar@^3.5.3:
version "3.5.3" version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@@ -398,11 +398,6 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
immutable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
import-fresh@^3.2.1: import-fresh@^3.2.1:
version "3.3.0" version "3.3.0"
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
@@ -890,16 +885,7 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
sass@^1.49.7: source-map-js@^1.0.2:
version "1.49.7"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.49.7.tgz#22a86a50552b9b11f71404dfad1b9ff44c6b0c49"
integrity sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==