- Get {invite.gift} free sats from{' '}
+ Get {invite.gift} cowboy credits from{' '}
@{invite.user.name}{' '}
- when you sign up today
+ when you sign up
- {me && showAlert &&
- {
- window.localStorage.setItem('hideLnAddrAlert', 'yep')
- setShowAlert(false)
- }}
- >
- You can also fund your account via lightning address with {`${me.name}@stacker.news`}
- }
-
-
@@ -205,12 +75,12 @@ export function SelectedWithdrawalForm () {
const router = useRouter()
switch (router.query.type) {
- case 'withdraw':
- return
- case 'lnurl-withdraw':
- return
- case 'lnaddr-withdraw':
+ case 'lnurl':
+ return
+ case 'lnaddr':
return
+ default:
+ return
}
}
@@ -271,7 +141,9 @@ export function InvWithdrawal () {
required
append={sats}
/>
- withdraw
+
+ withdraw
+
>
)
@@ -343,7 +215,7 @@ function LnQRWith ({ k1, encodedUrl }) {
return
}
-export function LnWithdrawal () {
+export function LnurlWithdrawal () {
// query for challenge
const [createWith, { data, error }] = useMutation(gql`
mutation createWith {
@@ -507,7 +379,9 @@ export function LnAddrWithdrawal () {
/>
}
- send
+
+ send
+
>
)
diff --git a/prisma/migrations/20241203195142_fee_credits/migration.sql b/prisma/migrations/20241203195142_fee_credits/migration.sql
new file mode 100644
index 000000000..b4a8fadbb
--- /dev/null
+++ b/prisma/migrations/20241203195142_fee_credits/migration.sql
@@ -0,0 +1,78 @@
+-- AlterTable
+ALTER TABLE "Item" ADD COLUMN "mcredits" BIGINT NOT NULL DEFAULT 0,
+ADD COLUMN "commentMcredits" BIGINT NOT NULL DEFAULT 0;
+
+-- AlterTable
+ALTER TABLE "users" ADD COLUMN "mcredits" BIGINT NOT NULL DEFAULT 0,
+ADD COLUMN "stackedMcredits" BIGINT NOT NULL DEFAULT 0,
+ADD COLUMN "receiveCreditsBelowSats" INTEGER NOT NULL DEFAULT 10,
+ADD COLUMN "sendCreditsBelowSats" INTEGER NOT NULL DEFAULT 10;
+
+-- default to true now
+ALTER TABLE "users" ALTER COLUMN "proxyReceive" SET DEFAULT true,
+ALTER COLUMN "directReceive" SET DEFAULT true;
+
+-- if they don't have either set, set to true
+UPDATE "users" SET "proxyReceive" = true, "directReceive" = true
+WHERE NOT "proxyReceive" AND NOT "directReceive";
+
+-- add mcredits check
+ALTER TABLE users ADD CONSTRAINT "mcredits_positive" CHECK ("mcredits" >= 0) NOT VALID;
+ALTER TABLE users ADD CONSTRAINT "stackedMcredits_positive" CHECK ("stackedMcredits" >= 0) NOT VALID;
+
+-- add cowboy credits
+CREATE OR REPLACE FUNCTION item_comments_zaprank_with_me(_item_id int, _global_seed int, _me_id int, _level int, _where text, _order_by text)
+ RETURNS jsonb
+ LANGUAGE plpgsql VOLATILE PARALLEL SAFE AS
+$$
+DECLARE
+ result jsonb;
+BEGIN
+ IF _level < 1 THEN
+ RETURN '[]'::jsonb;
+ END IF;
+
+ EXECUTE 'CREATE TEMP TABLE IF NOT EXISTS t_item ON COMMIT DROP AS'
+ || ' SELECT "Item".*, "Item".created_at at time zone ''UTC'' AS "createdAt", "Item".updated_at at time zone ''UTC'' AS "updatedAt", '
+ || ' "Item"."invoicePaidAt" at time zone ''UTC'' AS "invoicePaidAtUTC", to_jsonb(users.*) || jsonb_build_object(''meMute'', "Mute"."mutedId" IS NOT NULL) AS user, '
+ || ' COALESCE("ItemAct"."meMsats", 0) AS "meMsats", COALESCE("ItemAct"."mePendingMsats", 0) as "mePendingMsats", COALESCE("ItemAct"."meDontLikeMsats", 0) AS "meDontLikeMsats", '
+ || ' COALESCE("ItemAct"."meMcredits", 0) AS "meMcredits", COALESCE("ItemAct"."mePendingMcredits", 0) as "mePendingMcredits", '
+ || ' "Bookmark"."itemId" IS NOT NULL AS "meBookmark", "ThreadSubscription"."itemId" IS NOT NULL AS "meSubscription", '
+ || ' GREATEST(g.tf_hot_score, l.tf_hot_score) AS personal_hot_score, GREATEST(g.tf_top_score, l.tf_top_score) AS personal_top_score '
+ || ' FROM "Item" '
+ || ' JOIN users ON users.id = "Item"."userId" '
+ || ' LEFT JOIN "Mute" ON "Mute"."muterId" = $5 AND "Mute"."mutedId" = "Item"."userId"'
+ || ' LEFT JOIN "Bookmark" ON "Bookmark"."userId" = $5 AND "Bookmark"."itemId" = "Item".id '
+ || ' LEFT JOIN "ThreadSubscription" ON "ThreadSubscription"."userId" = $5 AND "ThreadSubscription"."itemId" = "Item".id '
+ || ' LEFT JOIN LATERAL ( '
+ || ' SELECT "itemId", '
+ || ' sum("ItemAct".msats) FILTER (WHERE "invoiceActionState" IS DISTINCT FROM ''FAILED'' AND "InvoiceForward".id IS NOT NULL AND (act = ''FEE'' OR act = ''TIP'')) AS "meMsats", '
+ || ' sum("ItemAct".msats) FILTER (WHERE "invoiceActionState" IS DISTINCT FROM ''FAILED'' AND "InvoiceForward".id IS NULL AND (act = ''FEE'' OR act = ''TIP'')) AS "meMcredits", '
+ || ' sum("ItemAct".msats) FILTER (WHERE "invoiceActionState" IS NOT DISTINCT FROM ''PENDING'' AND "InvoiceForward".id IS NOT NULL AND (act = ''FEE'' OR act = ''TIP'')) AS "mePendingMsats", '
+ || ' sum("ItemAct".msats) FILTER (WHERE "invoiceActionState" IS NOT DISTINCT FROM ''PENDING'' AND "InvoiceForward".id IS NULL AND (act = ''FEE'' OR act = ''TIP'')) AS "mePendingMcredits", '
+ || ' sum("ItemAct".msats) FILTER (WHERE "invoiceActionState" IS DISTINCT FROM ''FAILED'' AND act = ''DONT_LIKE_THIS'') AS "meDontLikeMsats" '
+ || ' FROM "ItemAct" '
+ || ' LEFT JOIN "Invoice" ON "Invoice".id = "ItemAct"."invoiceId" '
+ || ' LEFT JOIN "InvoiceForward" ON "InvoiceForward"."invoiceId" = "Invoice"."id" '
+ || ' WHERE "ItemAct"."userId" = $5 '
+ || ' AND "ItemAct"."itemId" = "Item".id '
+ || ' GROUP BY "ItemAct"."itemId" '
+ || ' ) "ItemAct" ON true '
+ || ' LEFT JOIN zap_rank_personal_view g ON g."viewerId" = $6 AND g.id = "Item".id '
+ || ' LEFT JOIN zap_rank_personal_view l ON l."viewerId" = $5 AND l.id = g.id '
+ || ' WHERE "Item".path <@ (SELECT path FROM "Item" WHERE id = $1) ' || _where || ' '
+ USING _item_id, _level, _where, _order_by, _me_id, _global_seed;
+
+ EXECUTE ''
+ || 'SELECT COALESCE(jsonb_agg(sub), ''[]''::jsonb) AS comments '
+ || 'FROM ( '
+ || ' SELECT "Item".*, item_comments_zaprank_with_me("Item".id, $6, $5, $2 - 1, $3, $4) AS comments '
+ || ' FROM t_item "Item" '
+ || ' WHERE "Item"."parentId" = $1 '
+ || _order_by
+ || ' ) sub'
+ INTO result USING _item_id, _level, _where, _order_by, _me_id, _global_seed;
+
+ RETURN result;
+END
+$$;
\ No newline at end of file
diff --git a/prisma/migrations/20250102224852_hat_streak_credits/migration.sql b/prisma/migrations/20250102224852_hat_streak_credits/migration.sql
new file mode 100644
index 000000000..0b3f667c1
--- /dev/null
+++ b/prisma/migrations/20250102224852_hat_streak_credits/migration.sql
@@ -0,0 +1,6 @@
+DROP TRIGGER IF EXISTS user_streak ON users;
+CREATE TRIGGER user_streak
+ AFTER UPDATE ON users
+ FOR EACH ROW
+ WHEN (NEW.msats < OLD.msats OR NEW.mcredits < OLD.mcredits)
+ EXECUTE PROCEDURE user_streak_check();
\ No newline at end of file
diff --git a/prisma/migrations/20250103011357_fix_expireboost_keepuntil/migration.sql b/prisma/migrations/20250103011357_fix_expireboost_keepuntil/migration.sql
deleted file mode 100644
index a67aac420..000000000
--- a/prisma/migrations/20250103011357_fix_expireboost_keepuntil/migration.sql
+++ /dev/null
@@ -1,4 +0,0 @@
--- fix existing boost jobs
-UPDATE pgboss.job
-SET keepuntil = startafter + interval '10 days'
-WHERE name = 'expireBoost' AND state = 'created';
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index ea6490f27..dcdb3b938 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -39,6 +39,7 @@ model User {
trust Float @default(0)
lastSeenAt DateTime?
stackedMsats BigInt @default(0)
+ stackedMcredits BigInt @default(0)
noteAllDescendants Boolean @default(true)
noteDeposits Boolean @default(true)
noteWithdrawals Boolean @default(true)
@@ -119,6 +120,9 @@ model User {
autoWithdrawMaxFeePercent Float?
autoWithdrawThreshold Int?
autoWithdrawMaxFeeTotal Int?
+ mcredits BigInt @default(0)
+ receiveCreditsBelowSats Int @default(10)
+ sendCreditsBelowSats Int @default(10)
muters Mute[] @relation("muter")
muteds Mute[] @relation("muted")
ArcOut Arc[] @relation("fromUser")
@@ -140,8 +144,8 @@ model User {
vaultKeyHash String @default("")
walletsUpdatedAt DateTime?
vaultEntries VaultEntry[] @relation("VaultEntries")
- proxyReceive Boolean @default(false)
- directReceive Boolean @default(false)
+ proxyReceive Boolean @default(true)
+ directReceive Boolean @default(true)
DirectPaymentReceived DirectPayment[] @relation("DirectPaymentReceived")
DirectPaymentSent DirectPayment[] @relation("DirectPaymentSent")
@@ -520,10 +524,12 @@ model Item {
pollCost Int?
paidImgLink Boolean @default(false)
commentMsats BigInt @default(0)
+ commentMcredits BigInt @default(0)
lastCommentAt DateTime?
lastZapAt DateTime?
ncomments Int @default(0)
msats BigInt @default(0)
+ mcredits BigInt @default(0)
cost Int @default(0)
weightedDownVotes Float @default(0)
bio Boolean @default(false)
diff --git a/wallets/README.md b/wallets/README.md
index 9ad23f8ab..41b0ab861 100644
--- a/wallets/README.md
+++ b/wallets/README.md
@@ -1,6 +1,6 @@
# Wallets
-Every wallet that you can see at [/settings/wallets](https://stacker.news/settings/wallets) is implemented as a plugin in this directory.
+Every wallet that you can see at [/wallets](https://stacker.news/wallets) is implemented as a plugin in this directory.
This README explains how you can add another wallet for use with Stacker News.
@@ -59,11 +59,11 @@ This is an optional value. Set this to true if your wallet needs to be configure
- `fields: WalletField[]`
-Wallet fields define what this wallet requires for configuration and thus are used to construct the forms like the one you can see at [/settings/wallets/lnbits](https://stacker.news/settings/walletslnbits).
+Wallet fields define what this wallet requires for configuration and thus are used to construct the forms like the one you can see at [/wallets/lnbits](https://stacker.news/walletslnbits).
- `card: WalletCard`
-Wallet cards are the components you can see at [/settings/wallets](https://stacker.news/settings/wallets). This property customizes this card for this wallet.
+Wallet cards are the components you can see at [/wallets](https://stacker.news/wallets). This property customizes this card for this wallet.
- `validate: (config) => void`
diff --git a/wallets/config.js b/wallets/config.js
index cdc574240..25f1f2ba2 100644
--- a/wallets/config.js
+++ b/wallets/config.js
@@ -2,7 +2,7 @@ import { useMe } from '@/components/me'
import useVault from '@/components/vault/use-vault'
import { useCallback } from 'react'
import { canReceive, canSend, getStorageKey, saveWalletLocally, siftConfig, upsertWalletVariables } from './common'
-import { useMutation } from '@apollo/client'
+import { gql, useMutation } from '@apollo/client'
import { generateMutation } from './graphql'
import { REMOVE_WALLET } from '@/fragments/wallet'
import { useWalletLogger } from '@/wallets/logger'
@@ -18,6 +18,7 @@ export function useWalletConfigurator (wallet) {
const logger = useWalletLogger(wallet)
const [upsertWallet] = useMutation(generateMutation(wallet?.def))
const [removeWallet] = useMutation(REMOVE_WALLET)
+ const [disableFreebies] = useMutation(gql`mutation { disableFreebies }`)
const _saveToServer = useCallback(async (serverConfig, clientConfig, validateLightning) => {
const variables = await upsertWalletVariables(
@@ -116,6 +117,7 @@ export function useWalletConfigurator (wallet) {
}
if (newCanSend) {
+ disableFreebies().catch(console.error)
if (oldCanSend) {
logger.ok('details for sending updated')
} else {
@@ -130,7 +132,7 @@ export function useWalletConfigurator (wallet) {
logger.info('details for sending deleted')
}
}, [isActive, wallet.def, wallet.config, _saveToServer, _saveToLocal, _validate,
- _detachFromLocal, _detachFromServer])
+ _detachFromLocal, _detachFromServer, disableFreebies])
const detach = useCallback(async () => {
if (isActive) {
diff --git a/worker/search.js b/worker/search.js
index 2e7ed21a8..7e91c10f5 100644
--- a/worker/search.js
+++ b/worker/search.js
@@ -27,9 +27,11 @@ const ITEM_SEARCH_FIELDS = gql`
remote
upvotes
sats
+ credits
boost
lastCommentAt
commentSats
+ commentCredits
path
ncomments
}`