Compare commits
8 Commits
8441e04d3e
...
c88afc5aae
Author | SHA1 | Date |
---|---|---|
Simone Cervino | c88afc5aae | |
ekzyis | 6bae1f1a89 | |
ekzyis | 82fead60f1 | |
k00b | 5dac2f2ed0 | |
Keyan | 9179688abc | |
k00b | 66ec5d5da8 | |
k00b | f3cc0f9e1d | |
k00b | aa4c448999 |
|
@ -2,11 +2,11 @@ import { USER_ID } from '@/lib/constants'
|
|||
import { deleteReminders, getDeleteAt, getRemindAt } from '@/lib/item'
|
||||
import { parseInternalLinks } from '@/lib/url'
|
||||
|
||||
export async function getMentions ({ text }, { me, models }) {
|
||||
export async function getMentions ({ text }, { me, tx }) {
|
||||
const mentionPattern = /\B@[\w_]+/gi
|
||||
const names = text.match(mentionPattern)?.map(m => m.slice(1))
|
||||
if (names?.length > 0) {
|
||||
const users = await models.user.findMany({
|
||||
const users = await tx.user.findMany({
|
||||
where: {
|
||||
name: {
|
||||
in: names
|
||||
|
@ -21,7 +21,7 @@ export async function getMentions ({ text }, { me, models }) {
|
|||
return []
|
||||
}
|
||||
|
||||
export const getItemMentions = async ({ text }, { me, models }) => {
|
||||
export const getItemMentions = async ({ text }, { me, tx }) => {
|
||||
const linkPattern = new RegExp(`${process.env.NEXT_PUBLIC_URL}/items/\\d+[a-zA-Z0-9/?=]*`, 'gi')
|
||||
const refs = text.match(linkPattern)?.map(m => {
|
||||
try {
|
||||
|
@ -33,7 +33,7 @@ export const getItemMentions = async ({ text }, { me, models }) => {
|
|||
}).filter(r => !!r)
|
||||
|
||||
if (refs?.length > 0) {
|
||||
const referee = await models.item.findMany({
|
||||
const referee = await tx.item.findMany({
|
||||
where: {
|
||||
id: { in: refs },
|
||||
userId: { not: me?.id || USER_ID.anon }
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useShowModal } from './modal'
|
|||
import { useToast } from './toast'
|
||||
import ItemAct from './item-act'
|
||||
import AccordianItem from './accordian-item'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import getColor from '@/lib/rainbow'
|
||||
import BoostIcon from '@/svgs/arrow-up-double-line.svg'
|
||||
import styles from './upvote.module.css'
|
||||
|
@ -12,32 +12,29 @@ import classNames from 'classnames'
|
|||
|
||||
export default function Boost ({ item, className, ...props }) {
|
||||
const { boost } = item
|
||||
const [hover, setHover] = useState(false)
|
||||
|
||||
const [color, nextColor] = useMemo(() => [getColor(boost), getColor(boost + BOOST_MULT)], [boost])
|
||||
|
||||
const style = useMemo(() => (hover || boost
|
||||
? {
|
||||
fill: hover ? nextColor : color,
|
||||
filter: `drop-shadow(0 0 6px ${hover ? nextColor : color}90)`
|
||||
}
|
||||
: undefined), [boost, hover])
|
||||
const style = useMemo(() => ({
|
||||
'--hover-fill': nextColor,
|
||||
'--hover-filter': `drop-shadow(0 0 6px ${nextColor}90)`,
|
||||
'--fill': color,
|
||||
'--filter': `drop-shadow(0 0 6px ${color}90)`
|
||||
}), [color, nextColor])
|
||||
|
||||
return (
|
||||
<Booster
|
||||
item={item} As={({ ...oprops }) =>
|
||||
item={item} As={oprops =>
|
||||
<div className='upvoteParent'>
|
||||
<div
|
||||
className={styles.upvoteWrapper}
|
||||
>
|
||||
<BoostIcon
|
||||
{...props} {...oprops} style={style}
|
||||
{...props}
|
||||
{...oprops}
|
||||
style={style}
|
||||
width={26}
|
||||
height={26}
|
||||
onPointerEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
onTouchEnd={() => setHover(false)}
|
||||
className={classNames(styles.boost, className, boost && styles.voted)}
|
||||
className={classNames(styles.boost, className, boost && styles.boosted)}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
|
|
|
@ -78,6 +78,11 @@ export const FileUpload = forwardRef(({ children, className, onSelect, onUpload,
|
|||
|
||||
element.onerror = reject
|
||||
element.src = window.URL.createObjectURL(file)
|
||||
|
||||
// iOS Force the video to load metadata
|
||||
if (element.tagName === 'VIDEO') {
|
||||
element.load()
|
||||
}
|
||||
})
|
||||
}, [toaster, getSignedPOST])
|
||||
|
||||
|
|
|
@ -109,7 +109,6 @@ export default function UpVote ({ item, className, collapsed }) {
|
|||
const [tipShow, _setTipShow] = useState(false)
|
||||
const ref = useRef()
|
||||
const { me } = useMe()
|
||||
const [hover, setHover] = useState(false)
|
||||
const [setWalkthrough] = useMutation(
|
||||
gql`
|
||||
mutation setWalkthrough($upvotePopover: Boolean, $tipPopover: Boolean) {
|
||||
|
@ -172,10 +171,6 @@ export default function UpVote ({ item, className, collapsed }) {
|
|||
me, item?.meSats, item?.meAnonSats, me?.privates?.tipDefault, me?.privates?.turboDefault,
|
||||
me?.privates?.tipRandom, me?.privates?.tipRandomMin, me?.privates?.tipRandomMax, pending])
|
||||
|
||||
const handleModalClosed = () => {
|
||||
setHover(false)
|
||||
}
|
||||
|
||||
const handleLongPress = (e) => {
|
||||
if (!item) return
|
||||
|
||||
|
@ -195,7 +190,7 @@ export default function UpVote ({ item, className, collapsed }) {
|
|||
setController(c)
|
||||
|
||||
showModal(onClose =>
|
||||
<ItemAct onClose={onClose} item={item} abortSignal={c.signal} />, { onClose: handleModalClosed })
|
||||
<ItemAct onClose={onClose} item={item} abortSignal={c.signal} />)
|
||||
}
|
||||
|
||||
const handleShortPress = async () => {
|
||||
|
@ -223,19 +218,16 @@ export default function UpVote ({ item, className, collapsed }) {
|
|||
|
||||
await zap({ item, me, abortSignal: c.signal })
|
||||
} else {
|
||||
showModal(onClose => <ItemAct onClose={onClose} item={item} />, { onClose: handleModalClosed })
|
||||
showModal(onClose => <ItemAct onClose={onClose} item={item} />)
|
||||
}
|
||||
}
|
||||
|
||||
const style = useMemo(() => {
|
||||
const fillColor = pending || hover ? nextColor : color
|
||||
return meSats || hover || pending
|
||||
? {
|
||||
fill: fillColor,
|
||||
filter: `drop-shadow(0 0 6px ${fillColor}90)`
|
||||
}
|
||||
: undefined
|
||||
}, [hover, pending, nextColor, color, meSats])
|
||||
const style = useMemo(() => ({
|
||||
'--hover-fill': nextColor,
|
||||
'--hover-filter': `drop-shadow(0 0 6px ${nextColor}90)`,
|
||||
'--fill': color,
|
||||
'--filter': `drop-shadow(0 0 6px ${color}90)`
|
||||
}), [color, nextColor])
|
||||
|
||||
return (
|
||||
<div ref={ref} className='upvoteParent'>
|
||||
|
@ -246,9 +238,6 @@ export default function UpVote ({ item, className, collapsed }) {
|
|||
<ActionTooltip notForm disable={disabled} overlayText={overlayText}>
|
||||
<div className={classNames(disabled && styles.noSelfTips, styles.upvoteWrapper)}>
|
||||
<UpBolt
|
||||
onPointerEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
onTouchEnd={() => setHover(false)}
|
||||
width={26}
|
||||
height={26}
|
||||
className={classNames(styles.upvote,
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
.upvote:hover {
|
||||
fill: var(--hover-fill) !important;
|
||||
filter: var(--hover-filter) !important;
|
||||
}
|
||||
|
||||
.boost {
|
||||
fill: var(--theme-clickToContextColor);
|
||||
user-select: none;
|
||||
|
@ -12,6 +17,16 @@
|
|||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
.boost:hover {
|
||||
fill: var(--hover-fill) !important;
|
||||
filter: var(--hover-filter) !important;
|
||||
}
|
||||
|
||||
.boost.boosted {
|
||||
fill: var(--fill);
|
||||
filter: var(--filter);
|
||||
}
|
||||
|
||||
.upvoteWrapper {
|
||||
position: relative;
|
||||
padding-right: .2rem;
|
||||
|
@ -28,8 +43,8 @@
|
|||
}
|
||||
|
||||
.upvote.voted {
|
||||
fill: #F6911D;
|
||||
filter: drop-shadow(0 0 6px #f6911d90);
|
||||
fill: var(--fill);
|
||||
filter: var(--filter);
|
||||
}
|
||||
|
||||
.cover {
|
||||
|
@ -43,6 +58,7 @@
|
|||
}
|
||||
|
||||
.pending {
|
||||
fill: var(--hover-fill);
|
||||
animation-name: pulse;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
|
|
|
@ -27,12 +27,17 @@ function useIndexedDB ({ dbName, storeName, options = DEFAULT_OPTIONS, indices =
|
|||
db.transaction(storeName)
|
||||
while (operationQueue.current.length > 0) {
|
||||
const operation = operationQueue.current.shift()
|
||||
operation(db)
|
||||
// if the db is the same as the one we're processing, run the operation
|
||||
// else, we'll just clear the operation queue
|
||||
// XXX this is a consquence of using a ref to store the queue and should be fixed
|
||||
if (dbName === db.name) {
|
||||
operation(db)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
}, [storeName, handleError, operationQueue])
|
||||
}, [dbName, storeName, handleError, operationQueue])
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
|
|
|
@ -14,6 +14,7 @@ import { schnorr } from '@noble/curves/secp256k1'
|
|||
import { notifyReferral } from '@/lib/webPush'
|
||||
import { hashEmail } from '@/lib/crypto'
|
||||
import * as cookie from 'cookie'
|
||||
import { multiAuthMiddleware } from '@/pages/api/graphql'
|
||||
|
||||
/**
|
||||
* Stores userIds in user table
|
||||
|
@ -132,6 +133,9 @@ function setMultiAuthCookies (req, res, { id, jwt, name, photoId }) {
|
|||
// add JWT to **httpOnly** cookie
|
||||
res.appendHeader('Set-Cookie', cookie.serialize(`multi_auth.${id}`, jwt, cookieOptions))
|
||||
|
||||
// switch to user we just added
|
||||
res.appendHeader('Set-Cookie', cookie.serialize('multi_auth.user-id', id, { ...cookieOptions, httpOnly: false }))
|
||||
|
||||
let newMultiAuth = [{ id, name, photoId }]
|
||||
if (req.cookies.multi_auth) {
|
||||
const oldMultiAuth = b64Decode(req.cookies.multi_auth)
|
||||
|
@ -140,9 +144,6 @@ function setMultiAuthCookies (req, res, { id, jwt, name, photoId }) {
|
|||
newMultiAuth = [...oldMultiAuth, ...newMultiAuth]
|
||||
}
|
||||
res.appendHeader('Set-Cookie', cookie.serialize('multi_auth', b64Encode(newMultiAuth), { ...cookieOptions, httpOnly: false }))
|
||||
|
||||
// switch to user we just added
|
||||
res.appendHeader('Set-Cookie', cookie.serialize('multi_auth.user-id', id, { ...cookieOptions, httpOnly: false }))
|
||||
}
|
||||
|
||||
async function pubkeyAuth (credentials, req, res, pubkeyColumnName) {
|
||||
|
@ -165,6 +166,7 @@ async function pubkeyAuth (credentials, req, res, pubkeyColumnName) {
|
|||
let user = await prisma.user.findUnique({ where: { [pubkeyColumnName]: pubkey } })
|
||||
|
||||
// get token if it exists
|
||||
req = multiAuthMiddleware(req)
|
||||
const token = await getToken({ req })
|
||||
if (!user) {
|
||||
// we have not seen this pubkey before
|
||||
|
|
|
@ -82,7 +82,7 @@ export default startServerAndCreateNextHandler(apolloServer, {
|
|||
}
|
||||
})
|
||||
|
||||
function multiAuthMiddleware (request) {
|
||||
export function multiAuthMiddleware (request) {
|
||||
// switch next-auth session cookie with multi_auth cookie if cookie pointer present
|
||||
|
||||
// is there a cookie pointer?
|
||||
|
|
|
@ -13,11 +13,12 @@ import { canReceive, canSend, isConfigured } from '@/wallets/common'
|
|||
import { SSR } from '@/lib/constants'
|
||||
import WalletButtonBar from '@/components/wallet-buttonbar'
|
||||
import { useWalletConfigurator } from '@/wallets/config'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useMe } from '@/components/me'
|
||||
import validateWallet from '@/wallets/validate'
|
||||
import { ValidationError } from 'yup'
|
||||
import { useFormikContext } from 'formik'
|
||||
import useDarkMode from '@/components/dark-mode'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
||||
|
||||
|
@ -28,6 +29,8 @@ export default function WalletSettings () {
|
|||
const wallet = useWallet(name)
|
||||
const { me } = useMe()
|
||||
const { save, detach } = useWalletConfigurator(wallet)
|
||||
const [dark] = useDarkMode()
|
||||
const [imgSrc, setImgSrc] = useState(wallet?.def.card?.image?.src)
|
||||
|
||||
const initial = useMemo(() => {
|
||||
const initial = wallet?.def.fields.reduce((acc, field) => {
|
||||
|
@ -69,12 +72,16 @@ export default function WalletSettings () {
|
|||
|
||||
const { card: { image, title, subtitle } } = wallet?.def || { card: {} }
|
||||
|
||||
useEffect(() => {
|
||||
if (!imgSrc) return
|
||||
// wallet.png <-> wallet-dark.png
|
||||
setImgSrc(dark ? image?.src.replace(/\.([a-z]{3})$/, '-dark.$1') : image?.src)
|
||||
}, [dark])
|
||||
|
||||
return (
|
||||
<CenterLayout>
|
||||
{image
|
||||
? typeof image === 'object'
|
||||
? <img {...image} alt={title} className='pb-2' />
|
||||
: <img src={image} width='33%' alt={title} className='pb-2' />
|
||||
? <img alt={title} {...image} src={imgSrc} className='pb-3 px-2 mw-100' />
|
||||
: <h2 className='pb-2'>{title}</h2>}
|
||||
<h6 className='text-muted text-center pb-3'><Text>{subtitle}</Text></h6>
|
||||
<Form
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "WalletBlink" ALTER COLUMN "apiKeyRecv" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "WalletLNbits" ALTER COLUMN "invoiceKey" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "WalletNWC" ALTER COLUMN "nwcUrlRecv" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "WalletPhoenixd" ALTER COLUMN "secondaryPassword" DROP NOT NULL;
|
|
@ -289,7 +289,7 @@ model WalletLNbits {
|
|||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
url String
|
||||
invoiceKey String
|
||||
invoiceKey String?
|
||||
}
|
||||
|
||||
model WalletNWC {
|
||||
|
@ -298,7 +298,7 @@ model WalletNWC {
|
|||
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
nwcUrlRecv String
|
||||
nwcUrlRecv String?
|
||||
}
|
||||
|
||||
model WalletBlink {
|
||||
|
@ -307,7 +307,7 @@ model WalletBlink {
|
|||
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
apiKeyRecv String
|
||||
apiKeyRecv String?
|
||||
currencyRecv String?
|
||||
}
|
||||
|
||||
|
@ -318,7 +318,7 @@ model WalletPhoenixd {
|
|||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||
url String
|
||||
secondaryPassword String
|
||||
secondaryPassword String?
|
||||
}
|
||||
|
||||
model Mute {
|
||||
|
|
|
@ -119,11 +119,12 @@ async function performPessimisticAction ({ lndInvoice, dbInvoice, tx, models, ln
|
|||
const context = {
|
||||
tx,
|
||||
cost: BigInt(lndInvoice.received_mtokens),
|
||||
me: dbInvoice.user,
|
||||
sybilFeePercent: await paidActions[dbInvoice.actionType].getSybilFeePercent?.()
|
||||
me: dbInvoice.user
|
||||
}
|
||||
|
||||
const result = await paidActions[dbInvoice.actionType].perform(args, context)
|
||||
const sybilFeePercent = await paidActions[dbInvoice.actionType].getSybilFeePercent?.(args, context)
|
||||
|
||||
const result = await paidActions[dbInvoice.actionType].perform(args, { ...context, sybilFeePercent })
|
||||
await tx.invoice.update({
|
||||
where: { id: dbInvoice.id },
|
||||
data: {
|
||||
|
|
Loading…
Reference in New Issue