Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show tlv records in transaction popup #365

Merged
merged 23 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
33a5714
feat: show tlv records in transaction popup
im-adithya Jul 29, 2024
832bbf4
chore: add podcastinginfo component
im-adithya Jul 30, 2024
10afe14
chore: remove ? in podcasting info component
im-adithya Jul 30, 2024
65e6752
Merge branch 'master' into task-tlv-records
im-adithya Aug 5, 2024
308e4ff
chore: decode tlv record values in backend
im-adithya Aug 6, 2024
185895f
chore: add backup for tx description
im-adithya Aug 6, 2024
eb84d63
feat: add boostagram column and use jsonb in metadata
im-adithya Aug 6, 2024
a6406a8
chore: do not modify metadata while parsing
im-adithya Aug 7, 2024
25064af
chore: combine boostagram and metadata migration
im-adithya Aug 7, 2024
d9d99c0
feat: extract description from custom records
im-adithya Aug 8, 2024
178410f
chore: fix keysend tests
im-adithya Aug 8, 2024
4ee56dc
chore: change metadata param type in makeinvoice
im-adithya Aug 8, 2024
41c290b
chore: fix make invoice tests
im-adithya Aug 8, 2024
e12433e
chore: metadata fixes
im-adithya Aug 8, 2024
168d7cf
Merge remote-tracking branch 'origin/master' into task-tlv-records
rolznz Aug 8, 2024
3b96728
fix: boostragram and metadata migration
rolznz Aug 8, 2024
7c29b25
chore: set metadata json column to null if empty
im-adithya Aug 8, 2024
8760830
fix: remove jsonb from migration
im-adithya Aug 8, 2024
fe59323
chore: code cleanup, rename api models, fix metadata usage
rolznz Aug 9, 2024
662c01d
fix: metadata format
rolznz Aug 9, 2024
fb30ae6
chore: remove unused contant
rolznz Aug 9, 2024
e6d9930
fix: tests
rolznz Aug 9, 2024
268b69a
chore: improve tests
rolznz Aug 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,36 @@ type ListTransactionsResponse = []Transaction

// TODO: camelCase
type Transaction struct {
Type string `json:"type"`
Invoice string `json:"invoice"`
Description string `json:"description"`
DescriptionHash string `json:"description_hash"`
Preimage *string `json:"preimage"`
PaymentHash string `json:"payment_hash"`
Amount uint64 `json:"amount"`
FeesPaid uint64 `json:"fees_paid"`
CreatedAt string `json:"created_at"`
SettledAt *string `json:"settled_at"`
AppId *uint `json:"app_id"`
Metadata interface{} `json:"metadata,omitempty"`
Type string `json:"type"`
Invoice string `json:"invoice"`
Description string `json:"description"`
DescriptionHash string `json:"description_hash"`
Preimage *string `json:"preimage"`
PaymentHash string `json:"payment_hash"`
Amount uint64 `json:"amount"`
FeesPaid uint64 `json:"fees_paid"`
CreatedAt string `json:"created_at"`
SettledAt *string `json:"settled_at"`
AppId *uint `json:"app_id"`
Metadata *lnclient.Metadata `json:"metadata,omitempty"`
Boostagram *Boostagram `json:"boostagram,omitempty"`
}

type Boostagram struct {
AppName string `json:"app_name"`
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
Name string `json:"name"`
Podcast string `json:"podcast"`
URL string `json:"url"`
Episode string `json:"episode,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@im-adithya why do some have omitempty others not? is it required?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took this from LBE repo here, whereever we have ?: in the Boostagram type there, I've omitted

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it looks completely arbitrary and doesn't seem to follow the spec - https://github.com/lightning/blips/blob/master/blip-0010.md#fields

FeedID string `json:"feedID,omitempty"`
ItemID string `json:"itemID,omitempty"`
Timestamp string `json:"ts,omitempty"`
Message string `json:"message,omitempty"`
SenderID string `json:"sender_id"`
SenderName string `json:"sender_name"`
Time string `json:"time"`
Action string `json:"action"`
ValueMsatTotal int `json:"value_msat_total"`
}

// debug api
Expand Down
19 changes: 16 additions & 3 deletions api/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"time"

"github.com/getAlby/hub/lnclient"
"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/transactions"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -73,9 +74,9 @@ func toApiTransaction(transaction *transactions.Transaction) *Transaction {
preimage = transaction.Preimage
}

var metadata interface{}
if transaction.Metadata != "" {
jsonErr := json.Unmarshal([]byte(transaction.Metadata), &metadata)
var metadata *lnclient.Metadata
if transaction.Metadata != nil {
jsonErr := json.Unmarshal(transaction.Metadata, &metadata)
if jsonErr != nil {
logger.Logger.WithError(jsonErr).WithFields(logrus.Fields{
"id": transaction.ID,
Expand All @@ -84,6 +85,17 @@ func toApiTransaction(transaction *transactions.Transaction) *Transaction {
}
}

var boostagram *Boostagram
rolznz marked this conversation as resolved.
Show resolved Hide resolved
if transaction.Boostagram != nil {
jsonErr := json.Unmarshal(transaction.Boostagram, &boostagram)
if jsonErr != nil {
logger.Logger.WithError(jsonErr).WithFields(logrus.Fields{
"id": transaction.ID,
rolznz marked this conversation as resolved.
Show resolved Hide resolved
"boostagram": transaction.Boostagram,
}).Error("Failed to deserialize transaction boostagram info")
}
}

return &Transaction{
Type: transaction.Type,
Invoice: transaction.PaymentRequest,
Expand All @@ -97,5 +109,6 @@ func toApiTransaction(transaction *transactions.Transaction) *Transaction {
CreatedAt: createdAt,
SettledAt: settledAt,
Metadata: metadata,
Boostagram: boostagram,
}
}
32 changes: 32 additions & 0 deletions db/migrations/202408061737_add_boostagrams_and_use_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package migrations

import (
_ "embed"

"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// This migration adds boostagram column to transactions
var _202408061737_add_boostagrams_and_use_json = &gormigrate.Migration{
ID: "202408061737_add_boostagrams_and_use_json",
Migrate: func(tx *gorm.DB) error {
if err := tx.Exec("ALTER TABLE transactions ADD COLUMN boostagram JSONB").Error; err != nil {
return err
}

if err := tx.Exec(`
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
ALTER TABLE transactions ADD COLUMN metadata_temp JSONB;
UPDATE transactions SET metadata_temp = json(metadata);
ALTER TABLE transactions DROP COLUMN metadata;
ALTER TABLE transactions RENAME COLUMN metadata_temp TO metadata;
`).Error; err != nil {
return err
}

return nil
},
Rollback: func(tx *gorm.DB) error {
return nil
},
}
1 change: 1 addition & 0 deletions db/migrations/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func Migrate(gormDB *gorm.DB) error {
_202407151352_autoincrement,
_202407201604_transactions_indexes,
_202407262257_remove_invalid_scopes,
_202408061737_add_boostagrams_and_use_json,
})

return m.Migrate()
Expand Down
9 changes: 7 additions & 2 deletions db/models.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package db

import "time"
import (
"time"

"gorm.io/datatypes"
)

type UserConfig struct {
ID uint
Expand Down Expand Up @@ -75,8 +79,9 @@ type Transaction struct {
ExpiresAt *time.Time
UpdatedAt time.Time
SettledAt *time.Time
Metadata string
Metadata datatypes.JSON
SelfPayment bool
Boostagram datatypes.JSON
}

type DBService interface {
Expand Down
63 changes: 63 additions & 0 deletions frontend/src/components/PodcastingInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Boostagram } from "src/types";

function PodcastingInfo({ boost }: { boost: Boostagram }) {
return (
<>
{boost.message && (
<div className="mt-6">
<p>Message</p>
<p className="text-muted-foreground break-all">{boost.message}</p>
</div>
)}
{boost.podcast && (
<div className="mt-6">
<p>Podcast</p>
<p className="text-muted-foreground break-all">{boost.podcast}</p>
</div>
)}
{boost.episode && (
<div className="mt-6">
<p>Episode</p>
<p className="text-muted-foreground break-all">{boost.episode}</p>
</div>
)}
{boost.action && (
<div className="mt-6">
<p>Action</p>
<p className="text-muted-foreground break-all">{boost.action}</p>
</div>
)}
{boost.ts && (
<div className="mt-6">
<p>Timestamp</p>
<p className="text-muted-foreground break-all">{boost.ts}</p>
</div>
)}
{boost.value_msat_total && (
<div className="mt-6">
<p>Total amount</p>
<p className="text-muted-foreground break-all">
{new Intl.NumberFormat(undefined, {}).format(
Math.floor(boost.value_msat_total / 1000)
)}{" "}
{Math.floor(boost.value_msat_total / 1000) == 1 ? "sat" : "sats"}
</p>
</div>
)}
{boost.sender_name && (
rolznz marked this conversation as resolved.
Show resolved Hide resolved
<div className="mt-6">
<p>Sender</p>
<p className="text-muted-foreground break-all">{boost.sender_name}</p>
</div>
)}
{boost.app_name && (
<div className="mt-6">
<p>App</p>
<p className="text-muted-foreground break-all">{boost.app_name}</p>
</div>
)}
</>
);
}

export default PodcastingInfo;
105 changes: 61 additions & 44 deletions frontend/src/components/TransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "lucide-react";
import React from "react";
import AppAvatar from "src/components/AppAvatar";
import PodcastingInfo from "src/components/PodcastingInfo";
import {
Credenza,
CredenzaBody,
Expand Down Expand Up @@ -191,52 +192,68 @@ function TransactionItem({ tx }: Props) {
</div>
)}
</CredenzaBody>
<CredenzaFooter className="!justify-start mt-4 !flex-col">
<div
className="flex items-center gap-2 cursor-pointer"
onClick={() => setShowDetails(!showDetails)}
>
Details
{showDetails ? (
<ChevronUp className="w-4 h-4" />
) : (
<ChevronDown className="w-4 h-4" />
)}
</div>
{showDetails && (
<>
<div className="mt-6 !ml-0">
<p>Preimage</p>
<div className="flex items-center gap-4">
<p className="text-muted-foreground break-all">
{tx.preimage}
</p>
<CopyIcon
className="cursor-pointer text-muted-foreground w-6 h-6"
onClick={() => {
if (tx.preimage) {
copy(tx.preimage);
}
}}
/>
<CredenzaFooter>
<div className="mt-4 w-full">
<div
className="flex items-center gap-2 cursor-pointer"
onClick={() => setShowDetails(!showDetails)}
>
Details
{showDetails ? (
<ChevronUp className="w-4 h-4" />
) : (
<ChevronDown className="w-4 h-4" />
)}
</div>
{showDetails && (
<>
{tx.metadata?.tlv_records.map((record) => {
rolznz marked this conversation as resolved.
Show resolved Hide resolved
if (record.type === 34349334) {
// Chat message
return (
<div className="mt-6" key={record.type}>
<p>Whatsat Message</p>
<p className="text-muted-foreground break-all">
{record.value}
</p>
</div>
);
}
})}
{tx.boostagram && <PodcastingInfo boost={tx.boostagram} />}
<div className="mt-6">
<p>Preimage</p>
<div className="flex items-center gap-4">
<p className="text-muted-foreground break-all">
{tx.preimage}
</p>
<CopyIcon
className="cursor-pointer text-muted-foreground w-6 h-6"
onClick={() => {
if (tx.preimage) {
copy(tx.preimage);
}
}}
/>
</div>
</div>
</div>
<div className="mt-6 !ml-0">
<p>Hash</p>
<div className="flex items-center gap-4">
<p className="text-muted-foreground break-all">
{tx.payment_hash}
</p>
<CopyIcon
className="cursor-pointer text-muted-foreground w-6 h-6"
onClick={() => {
copy(tx.payment_hash);
}}
/>
<div className="mt-6">
<p>Hash</p>
<div className="flex items-center gap-4">
<p className="text-muted-foreground break-all">
{tx.payment_hash}
</p>
<CopyIcon
className="cursor-pointer text-muted-foreground w-6 h-6"
onClick={() => {
copy(tx.payment_hash);
}}
/>
</div>
</div>
</div>
</>
)}
</>
)}
</div>
</CredenzaFooter>
</CredenzaContent>
</Credenza>
Expand Down
30 changes: 29 additions & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,35 @@ export type Transaction = {
fees_paid: number;
created_at: string;
settled_at: string | undefined;
metadata: unknown;
metadata?: {
tlv_records: TLVRecord[];
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
};
boostagram?: Boostagram;
};

export type TLVRecord = {
im-adithya marked this conversation as resolved.
Show resolved Hide resolved
type: number;
/**
* hex-decoded value
*/
value: string;
};

export type Boostagram = {
app_name: string;
rolznz marked this conversation as resolved.
Show resolved Hide resolved
name: string;
podcast: string;
url: string;
episode?: string;
feedID?: string;
itemID?: string;
ts?: string;
message?: string;
sender_id: string;
sender_name: string;
time: string;
action: "boost";
value_msat_total: number;
};

export type NewChannelOrderStatus = "pay" | "paid" | "success" | "opening";
Expand Down
Loading
Loading