mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
fix: return optional total balance in list apps response for subwallets (#2057)
* fix: return optional total balance in list apps response for subwallets * chore: add error handling to subwallet balance query * chore: add METADATA_APPSTORE_APP_ID_KEY constant * chore: add MAX_FREE_SUBWALLETS constant * chore: use subwallet query and total count for limit check
This commit is contained in:
parent
9701c726ac
commit
a2af8fd598
11 changed files with 183 additions and 56 deletions
34
api/api.go
34
api/api.go
|
|
@ -177,7 +177,7 @@ func (api *api) UpdateApp(userApp *db.App, updateAppRequest *UpdateAppRequest) e
|
||||||
}).Error("Failed to deserialize app metadata")
|
}).Error("Failed to deserialize app metadata")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if existingMetadata["app_store_app_id"] == constants.SUBWALLET_APPSTORE_APP_ID {
|
if existingMetadata[constants.METADATA_APPSTORE_APP_ID_KEY] == constants.SUBWALLET_APPSTORE_APP_ID {
|
||||||
return errors.New("Cannot update sub-wallet to be non-isolated")
|
return errors.New("Cannot update sub-wallet to be non-isolated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -487,7 +487,7 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
||||||
}
|
}
|
||||||
|
|
||||||
if filters.AppStoreAppId != "" {
|
if filters.AppStoreAppId != "" {
|
||||||
query = query.Where(datatypes.JSONQuery("metadata").Equals(filters.AppStoreAppId, "app_store_app_id"))
|
query = query.Where(datatypes.JSONQuery("metadata").Equals(filters.AppStoreAppId, constants.METADATA_APPSTORE_APP_ID_KEY))
|
||||||
}
|
}
|
||||||
|
|
||||||
if filters.Unused {
|
if filters.Unused {
|
||||||
|
|
@ -495,12 +495,16 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
||||||
query = query.Where("last_used_at IS NULL OR last_used_at < ?", time.Now().Add(-60*24*time.Hour))
|
query = query.Where("last_used_at IS NULL OR last_used_at < ?", time.Now().Add(-60*24*time.Hour))
|
||||||
}
|
}
|
||||||
|
|
||||||
if filters.SubWallets != nil && !*filters.SubWallets {
|
if filters.SubWallets != nil {
|
||||||
// exclude subwallets :scream:
|
if *filters.SubWallets {
|
||||||
if api.db.Dialector.Name() == "sqlite" {
|
query = query.Where(datatypes.JSONQuery("metadata").Equals(constants.SUBWALLET_APPSTORE_APP_ID, constants.METADATA_APPSTORE_APP_ID_KEY))
|
||||||
query = query.Where("metadata is NULL OR JSON_EXTRACT(metadata, '$.app_store_app_id') IS NULL OR JSON_EXTRACT(metadata, '$.app_store_app_id') != ?", constants.SUBWALLET_APPSTORE_APP_ID)
|
|
||||||
} else {
|
} else {
|
||||||
query = query.Where("metadata IS NULL OR metadata->>'app_store_app_id' IS NULL OR metadata->>'app_store_app_id' != ?", constants.SUBWALLET_APPSTORE_APP_ID)
|
// exclude subwallets :scream:
|
||||||
|
if api.db.Dialector.Name() == "sqlite" {
|
||||||
|
query = query.Where(fmt.Sprintf("metadata is NULL OR JSON_EXTRACT(metadata, '$.%s') IS NULL OR JSON_EXTRACT(metadata, '$.%s') != ?", constants.METADATA_APPSTORE_APP_ID_KEY, constants.METADATA_APPSTORE_APP_ID_KEY), constants.SUBWALLET_APPSTORE_APP_ID)
|
||||||
|
} else {
|
||||||
|
query = query.Where(fmt.Sprintf("metadata IS NULL OR metadata->>'%s' IS NULL OR metadata->>'%s' != ?", constants.METADATA_APPSTORE_APP_ID_KEY, constants.METADATA_APPSTORE_APP_ID_KEY), constants.SUBWALLET_APPSTORE_APP_ID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -523,6 +527,17 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
||||||
logger.Logger.WithError(result.Error).Error("Failed to count DB apps")
|
logger.Logger.WithError(result.Error).Error("Failed to count DB apps")
|
||||||
return nil, result.Error
|
return nil, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var totalBalance *int64
|
||||||
|
if filters.SubWallets != nil && *filters.SubWallets {
|
||||||
|
totalBalanceMsat, err := queries.GetTotalSubwalletBalance(api.db)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger.WithError(err).Error("Failed to calculate total subwallet balance")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
totalBalance = &totalBalanceMsat
|
||||||
|
}
|
||||||
|
|
||||||
query = query.Offset(int(offset)).Limit(int(limit))
|
query = query.Offset(int(offset)).Limit(int(limit))
|
||||||
|
|
||||||
err := query.Find(&dbApps).Error
|
err := query.Find(&dbApps).Error
|
||||||
|
|
@ -598,8 +613,9 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
||||||
apiApps = append(apiApps, apiApp)
|
apiApps = append(apiApps, apiApp)
|
||||||
}
|
}
|
||||||
return &ListAppsResponse{
|
return &ListAppsResponse{
|
||||||
Apps: apiApps,
|
Apps: apiApps,
|
||||||
TotalCount: uint64(totalCount),
|
TotalCount: uint64(totalCount),
|
||||||
|
TotalBalance: totalBalance,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,9 @@ type ListAppsFilters struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListAppsResponse struct {
|
type ListAppsResponse struct {
|
||||||
Apps []App `json:"apps"`
|
Apps []App `json:"apps"`
|
||||||
TotalCount uint64 `json:"totalCount"`
|
TotalCount uint64 `json:"totalCount"`
|
||||||
|
TotalBalance *int64 `json:"totalBalance,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateAppRequest struct {
|
type UpdateAppRequest struct {
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ const (
|
||||||
ENCRYPTION_TYPE_NIP44_V2 = "nip44_v2"
|
ENCRYPTION_TYPE_NIP44_V2 = "nip44_v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const METADATA_APPSTORE_APP_ID_KEY = "app_store_app_id"
|
||||||
|
|
||||||
const SUBWALLET_APPSTORE_APP_ID = "uncle-jim"
|
const SUBWALLET_APPSTORE_APP_ID = "uncle-jim"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
40
db/queries/get_total_subwallet_balance.go
Normal file
40
db/queries/get_total_subwallet_balance.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/getAlby/hub/constants"
|
||||||
|
"github.com/getAlby/hub/db"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTotalSubwalletBalance(tx *gorm.DB) (int64, error) {
|
||||||
|
subwalletAppIDsQuery := tx.Model(&db.App{}).
|
||||||
|
Select("id").
|
||||||
|
Where(datatypes.JSONQuery("metadata").Equals(constants.SUBWALLET_APPSTORE_APP_ID, constants.METADATA_APPSTORE_APP_ID_KEY))
|
||||||
|
|
||||||
|
var received struct {
|
||||||
|
Sum int64
|
||||||
|
}
|
||||||
|
res := tx.
|
||||||
|
Table("transactions").
|
||||||
|
Select("SUM(amount_msat) as sum").
|
||||||
|
Where("app_id IN (?) AND type = ? AND state = ?", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_INCOMING, constants.TRANSACTION_STATE_SETTLED).
|
||||||
|
Scan(&received)
|
||||||
|
if res.Error != nil {
|
||||||
|
return 0, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var spent struct {
|
||||||
|
Sum int64
|
||||||
|
}
|
||||||
|
res = tx.
|
||||||
|
Table("transactions").
|
||||||
|
Select("SUM(amount_msat + fee_msat + fee_reserve_msat) as sum").
|
||||||
|
Where("app_id IN (?) AND type = ? AND (state = ? OR state = ?)", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING).
|
||||||
|
Scan(&spent)
|
||||||
|
if res.Error != nil {
|
||||||
|
return 0, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return received.Sum - spent.Sum, nil
|
||||||
|
}
|
||||||
64
db/queries/get_total_subwallet_balance_test.go
Normal file
64
db/queries/get_total_subwallet_balance_test.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
|
||||||
|
"github.com/getAlby/hub/constants"
|
||||||
|
"github.com/getAlby/hub/db"
|
||||||
|
"github.com/getAlby/hub/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetTotalSubwalletBalance(t *testing.T) {
|
||||||
|
svc, err := tests.CreateTestService(t)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer svc.Remove()
|
||||||
|
|
||||||
|
subwalletA, _, err := tests.CreateApp(svc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
subwalletA.Isolated = true
|
||||||
|
subwalletA.Metadata = datatypes.JSON([]byte(fmt.Sprintf(`{"%s":"%s"}`, constants.METADATA_APPSTORE_APP_ID_KEY, constants.SUBWALLET_APPSTORE_APP_ID)))
|
||||||
|
svc.DB.Save(&subwalletA)
|
||||||
|
|
||||||
|
subwalletB, _, err := tests.CreateApp(svc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
subwalletB.Isolated = true
|
||||||
|
subwalletB.Metadata = datatypes.JSON([]byte(fmt.Sprintf(`{"%s":"%s"}`, constants.METADATA_APPSTORE_APP_ID_KEY, constants.SUBWALLET_APPSTORE_APP_ID)))
|
||||||
|
svc.DB.Save(&subwalletB)
|
||||||
|
|
||||||
|
incomingSubwalletTx := db.Transaction{
|
||||||
|
AppId: &subwalletA.ID,
|
||||||
|
Type: constants.TRANSACTION_TYPE_INCOMING,
|
||||||
|
State: constants.TRANSACTION_STATE_SETTLED,
|
||||||
|
AmountMsat: 5000,
|
||||||
|
}
|
||||||
|
svc.DB.Save(&incomingSubwalletTx)
|
||||||
|
|
||||||
|
outgoingSettledSubwalletTx := db.Transaction{
|
||||||
|
AppId: &subwalletA.ID,
|
||||||
|
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||||
|
State: constants.TRANSACTION_STATE_SETTLED,
|
||||||
|
AmountMsat: 1000,
|
||||||
|
FeeMsat: 100,
|
||||||
|
FeeReserveMsat: 0,
|
||||||
|
}
|
||||||
|
svc.DB.Save(&outgoingSettledSubwalletTx)
|
||||||
|
|
||||||
|
outgoingPendingSubwalletTx := db.Transaction{
|
||||||
|
AppId: &subwalletB.ID,
|
||||||
|
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||||
|
State: constants.TRANSACTION_STATE_PENDING,
|
||||||
|
AmountMsat: 2000,
|
||||||
|
FeeMsat: 0,
|
||||||
|
FeeReserveMsat: 300,
|
||||||
|
}
|
||||||
|
svc.DB.Save(&outgoingPendingSubwalletTx)
|
||||||
|
|
||||||
|
total, err := GetTotalSubwalletBalance(svc.DB)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(1600), total)
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ export const ALBY_MIN_HOSTED_BALANCE_FOR_FIRST_CHANNEL = 10_000;
|
||||||
|
|
||||||
export const LIST_TRANSACTIONS_LIMIT = 20;
|
export const LIST_TRANSACTIONS_LIMIT = 20;
|
||||||
export const LIST_APPS_LIMIT = 20;
|
export const LIST_APPS_LIMIT = 20;
|
||||||
|
export const MAX_FREE_SUBWALLETS = 3;
|
||||||
|
|
||||||
export const SUPPORT_ALBY_CONNECTION_NAME = `ZapPlanner - Alby Hub`;
|
export const SUPPORT_ALBY_CONNECTION_NAME = `ZapPlanner - Alby Hub`;
|
||||||
export const SUPPORT_ALBY_LIGHTNING_ADDRESS = "hub@getalby.com";
|
export const SUPPORT_ALBY_LIGHTNING_ADDRESS = "hub@getalby.com";
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import ResponsiveExternalLinkButton from "src/components/ResponsiveExternalLinkB
|
||||||
import { LoadingButton } from "src/components/ui/custom/loading-button";
|
import { LoadingButton } from "src/components/ui/custom/loading-button";
|
||||||
import { Input } from "src/components/ui/input";
|
import { Input } from "src/components/ui/input";
|
||||||
import { Label } from "src/components/ui/label";
|
import { Label } from "src/components/ui/label";
|
||||||
import { SUBWALLET_APPSTORE_APP_ID } from "src/constants";
|
import { MAX_FREE_SUBWALLETS, SUBWALLET_APPSTORE_APP_ID } from "src/constants";
|
||||||
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
||||||
import { useApps } from "src/hooks/useApps";
|
import { useApps } from "src/hooks/useApps";
|
||||||
import { useInfo } from "src/hooks/useInfo";
|
import { useInfo } from "src/hooks/useInfo";
|
||||||
|
|
@ -19,11 +19,11 @@ import { handleRequestError } from "src/utils/handleRequestError";
|
||||||
export function NewSubwallet() {
|
export function NewSubwallet() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [name, setName] = React.useState("");
|
const [name, setName] = React.useState("");
|
||||||
const { data: appsData } = useApps(
|
const { data: subwalletAppsData } = useApps(
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
appStoreAppId: SUBWALLET_APPSTORE_APP_ID,
|
subWallets: true,
|
||||||
},
|
},
|
||||||
"created_at"
|
"created_at"
|
||||||
);
|
);
|
||||||
|
|
@ -34,20 +34,21 @@ export function NewSubwallet() {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!info ||
|
!info ||
|
||||||
!appsData ||
|
!subwalletAppsData ||
|
||||||
(info.albyAccountConnected && !albyMe && !albyMeError)
|
(info.albyAccountConnected && !albyMe && !albyMeError)
|
||||||
) {
|
) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subwalletApps = appsData?.apps;
|
|
||||||
|
|
||||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!albyMe?.subscription.plan_code && subwalletApps?.length >= 3) {
|
if (
|
||||||
|
!albyMe?.subscription.plan_code &&
|
||||||
|
subwalletAppsData.totalCount >= MAX_FREE_SUBWALLETS
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Max limit reached. Please upgrade to Pro to create more sub-wallets."
|
"Max limit reached. Please upgrade to Pro to create more sub-wallets."
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import {
|
||||||
import { ExternalLinkButton } from "src/components/ui/custom/external-link-button";
|
import { ExternalLinkButton } from "src/components/ui/custom/external-link-button";
|
||||||
import { LinkButton } from "src/components/ui/custom/link-button";
|
import { LinkButton } from "src/components/ui/custom/link-button";
|
||||||
import { UpgradeDialog } from "src/components/UpgradeDialog";
|
import { UpgradeDialog } from "src/components/UpgradeDialog";
|
||||||
import { LIST_APPS_LIMIT, SUBWALLET_APPSTORE_APP_ID } from "src/constants";
|
import { LIST_APPS_LIMIT, MAX_FREE_SUBWALLETS } from "src/constants";
|
||||||
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
||||||
import { useApps } from "src/hooks/useApps";
|
import { useApps } from "src/hooks/useApps";
|
||||||
import { useBalances } from "src/hooks/useBalances";
|
import { useBalances } from "src/hooks/useBalances";
|
||||||
|
|
@ -38,11 +38,11 @@ export function SubwalletList() {
|
||||||
const { data: info } = useInfo();
|
const { data: info } = useInfo();
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const appsListRef = useRef<HTMLDivElement>(null);
|
const appsListRef = useRef<HTMLDivElement>(null);
|
||||||
const { data: appsData } = useApps(
|
const { data: subwalletAppsData } = useApps(
|
||||||
undefined,
|
undefined,
|
||||||
page,
|
page,
|
||||||
{
|
{
|
||||||
appStoreAppId: SUBWALLET_APPSTORE_APP_ID,
|
subWallets: true,
|
||||||
},
|
},
|
||||||
"created_at"
|
"created_at"
|
||||||
);
|
);
|
||||||
|
|
@ -59,21 +59,20 @@ export function SubwalletList() {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!info ||
|
!info ||
|
||||||
!appsData ||
|
!subwalletAppsData ||
|
||||||
!balances ||
|
!balances ||
|
||||||
(info.albyAccountConnected && !albyMe && !albyMeError)
|
(info.albyAccountConnected && !albyMe && !albyMeError)
|
||||||
) {
|
) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subwalletApps = appsData.apps;
|
const subwalletApps = subwalletAppsData.apps;
|
||||||
|
|
||||||
if (!subwalletApps.length) {
|
if (!subwalletAppsData.totalCount) {
|
||||||
return <SubwalletIntro />;
|
return <SubwalletIntro />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subwalletTotalAmount =
|
const subwalletTotalAmount = subwalletAppsData.totalBalance || 0;
|
||||||
subwalletApps.reduce((total, app) => total + app.balance, 0) || 0;
|
|
||||||
const isSufficientlyBacked =
|
const isSufficientlyBacked =
|
||||||
subwalletTotalAmount <= balances.lightning.totalSpendable;
|
subwalletTotalAmount <= balances.lightning.totalSpendable;
|
||||||
|
|
||||||
|
|
@ -91,7 +90,8 @@ export function SubwalletList() {
|
||||||
>
|
>
|
||||||
<HelpCircle className="size-4" />
|
<HelpCircle className="size-4" />
|
||||||
</ExternalLinkButton>
|
</ExternalLinkButton>
|
||||||
{!albyMe?.subscription.plan_code && subwalletApps?.length >= 3 ? (
|
{!albyMe?.subscription.plan_code &&
|
||||||
|
subwalletAppsData.totalCount >= MAX_FREE_SUBWALLETS ? (
|
||||||
<UpgradeDialog>
|
<UpgradeDialog>
|
||||||
<ResponsiveButton icon={CirclePlusIcon} text="New Sub-wallet" />
|
<ResponsiveButton icon={CirclePlusIcon} text="New Sub-wallet" />
|
||||||
</UpgradeDialog>
|
</UpgradeDialog>
|
||||||
|
|
@ -106,26 +106,27 @@ export function SubwalletList() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!albyMe?.subscription.plan_code && subwalletApps.length >= 3 && (
|
{!albyMe?.subscription.plan_code &&
|
||||||
<>
|
subwalletAppsData.totalCount >= MAX_FREE_SUBWALLETS && (
|
||||||
<Alert>
|
<>
|
||||||
<InfoIcon />
|
<Alert>
|
||||||
<AlertTitle>Need more Sub-wallets?</AlertTitle>
|
<InfoIcon />
|
||||||
<AlertDescription className="flex flex-row gap-3">
|
<AlertTitle>Need more Sub-wallets?</AlertTitle>
|
||||||
<p className="grow">
|
<AlertDescription className="flex flex-row gap-3">
|
||||||
Upgrade your subscription plan to Pro unlock unlimited number of
|
<p className="grow">
|
||||||
Sub-wallets.
|
Upgrade your subscription plan to Pro unlock unlimited number
|
||||||
</p>
|
of Sub-wallets.
|
||||||
<UpgradeDialog>
|
</p>
|
||||||
<Button>
|
<UpgradeDialog>
|
||||||
<SparklesIcon />
|
<Button>
|
||||||
Upgrade
|
<SparklesIcon />
|
||||||
</Button>
|
Upgrade
|
||||||
</UpgradeDialog>
|
</Button>
|
||||||
</AlertDescription>
|
</UpgradeDialog>
|
||||||
</Alert>
|
</AlertDescription>
|
||||||
</>
|
</Alert>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isSufficientlyBacked && (
|
{!isSufficientlyBacked && (
|
||||||
<Alert variant="warning">
|
<Alert variant="warning">
|
||||||
|
|
@ -168,8 +169,8 @@ export function SubwalletList() {
|
||||||
<CardContent className="grow flex flex-col gap-4">
|
<CardContent className="grow flex flex-col gap-4">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<span className="text-2xl font-medium">
|
<span className="text-2xl font-medium">
|
||||||
{subwalletApps.length} /{" "}
|
{subwalletAppsData.totalCount} /{" "}
|
||||||
{albyMe?.subscription.plan_code ? "∞" : 3}
|
{albyMe?.subscription.plan_code ? "∞" : MAX_FREE_SUBWALLETS}
|
||||||
</span>
|
</span>
|
||||||
{isSufficientlyBacked ? (
|
{isSufficientlyBacked ? (
|
||||||
<div className="flex items-center text-positive-foreground text-sm">
|
<div className="flex items-center text-positive-foreground text-sm">
|
||||||
|
|
@ -202,7 +203,7 @@ export function SubwalletList() {
|
||||||
|
|
||||||
<CustomPagination
|
<CustomPagination
|
||||||
limit={LIST_APPS_LIMIT}
|
limit={LIST_APPS_LIMIT}
|
||||||
totalCount={appsData.totalCount}
|
totalCount={subwalletAppsData.totalCount}
|
||||||
page={page}
|
page={page}
|
||||||
handlePageChange={handlePageChange}
|
handlePageChange={handlePageChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -640,6 +640,7 @@ export type OnchainTransaction = {
|
||||||
export type ListAppsResponse = {
|
export type ListAppsResponse = {
|
||||||
apps: App[];
|
apps: App[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
|
totalBalance?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListTransactionsResponse = {
|
export type ListTransactionsResponse = {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func (controller *nip47Controller) HandleGetInfoEvent(ctx context.Context, nip47
|
||||||
if !app.Isolated {
|
if !app.Isolated {
|
||||||
lightningAddress, _ := controller.albyOAuthService.GetLightningAddress()
|
lightningAddress, _ := controller.albyOAuthService.GetLightningAddress()
|
||||||
responsePayload.LightningAddress = &lightningAddress
|
responsePayload.LightningAddress = &lightningAddress
|
||||||
} else if metadata["app_store_app_id"] == constants.SUBWALLET_APPSTORE_APP_ID && metadata["lud16"] != nil {
|
} else if metadata[constants.METADATA_APPSTORE_APP_ID_KEY] == constants.SUBWALLET_APPSTORE_APP_ID && metadata["lud16"] != nil {
|
||||||
lightningAddress := metadata["lud16"].(string)
|
lightningAddress := metadata["lud16"].(string)
|
||||||
responsePayload.LightningAddress = &lightningAddress
|
responsePayload.LightningAddress = &lightningAddress
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ func TestHandleGetInfoEvent_SubwalletNoPermission(t *testing.T) {
|
||||||
lightningAddress := "hello@getalby.com"
|
lightningAddress := "hello@getalby.com"
|
||||||
|
|
||||||
metadata := map[string]interface{}{
|
metadata := map[string]interface{}{
|
||||||
"app_store_app_id": constants.SUBWALLET_APPSTORE_APP_ID,
|
constants.METADATA_APPSTORE_APP_ID_KEY: constants.SUBWALLET_APPSTORE_APP_ID,
|
||||||
"lud16": lightningAddress,
|
"lud16": lightningAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.Cfg.SetUpdate("LNBackendType", config.LDKBackendType, "")
|
svc.Cfg.SetUpdate("LNBackendType", config.LDKBackendType, "")
|
||||||
|
|
@ -248,9 +248,9 @@ func TestHandleGetInfoEvent_SubwalletWithMetadata(t *testing.T) {
|
||||||
lightningAddress := "hello@getalby.com"
|
lightningAddress := "hello@getalby.com"
|
||||||
|
|
||||||
metadata := map[string]interface{}{
|
metadata := map[string]interface{}{
|
||||||
"app_store_app_id": constants.SUBWALLET_APPSTORE_APP_ID,
|
constants.METADATA_APPSTORE_APP_ID_KEY: constants.SUBWALLET_APPSTORE_APP_ID,
|
||||||
"lud16": lightningAddress,
|
"lud16": lightningAddress,
|
||||||
"a": 123,
|
"a": 123,
|
||||||
}
|
}
|
||||||
|
|
||||||
svc.Cfg.SetUpdate("LNBackendType", config.LDKBackendType, "")
|
svc.Cfg.SetUpdate("LNBackendType", config.LDKBackendType, "")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue