Compare commits

...

3 Commits

Author SHA1 Message Date
keyan
1dcb6461c7 give sndev logs better default params 2024-06-04 13:07:03 -05:00
keyan
2775b49ce7 fix inconsistency in url handling and don't let parseEmbedURL throw 2024-06-04 12:10:37 -05:00
ekzyis
ea97fbf4a4
Avoid manual optimistic updates for now (#1220)
* Avoid manual optimistic zap updates for now

* Remove manual optimistic updates for pay-bounty and poll
2024-06-04 03:02:34 -05:00
6 changed files with 114 additions and 96 deletions

View File

@ -10,7 +10,7 @@ import { useToast } from './toast'
import { useLightning } from './lightning'
import { nextTip } from './upvote'
import { InvoiceCanceledError, usePayment } from './payment'
import { optimisticUpdate } from '@/lib/apollo'
// import { optimisticUpdate } from '@/lib/apollo'
import { Types as ClientNotification, ClientNotifyProvider, useClientNotifications } from './client-notifications'
import { ZAP_UNDO_DELAY_MS } from '@/lib/constants'
@ -120,23 +120,31 @@ export default function ItemAct ({ onClose, item, down, children, abortSignal })
act: down ? 'DONT_LIKE_THIS' : 'TIP',
hash,
hmac
}
},
optimisticResponse: {
act: { id: item.id, sats: Number(amount), act: down ? 'DONT_LIKE_THIS' : 'TIP', path: item.path }
},
update: actUpdate({ me })
})
if (!me) setItemMeAnonSats({ id: item.id, amount })
addCustomTip(Number(amount))
}, [me, act, down, item.id, strike])
const optimisticUpdate = useCallback(({ amount }) => {
const variables = {
id: item.id,
sats: Number(amount),
act: down ? 'DONT_LIKE_THIS' : 'TIP'
}
const optimisticResponse = { act: { ...variables, path: item.path } }
strike()
onClose()
return { mutation: ACT_MUTATION, variables, optimisticResponse, update: actUpdate({ me }) }
}, [item.id, down, !!me, strike])
}, [me, act, down, item.id, strike])
// XXX avoid manual optimistic updates until
// https://github.com/stackernews/stacker.news/issues/1218 is fixed
// const optimisticUpdate = useCallback(({ amount }) => {
// const variables = {
// id: item.id,
// sats: Number(amount),
// act: down ? 'DONT_LIKE_THIS' : 'TIP'
// }
// const optimisticResponse = { act: { ...variables, path: item.path } }
// strike()
// onClose()
// return { mutation: ACT_MUTATION, variables, optimisticResponse, update: actUpdate({ me }) }
// }, [item.id, down, !!me, strike])
return (
<ClientNotifyProvider additionalProps={{ itemId: item.id }}>
@ -147,7 +155,7 @@ export default function ItemAct ({ onClose, item, down, children, abortSignal })
}}
schema={amountSchema}
prepaid
optimisticUpdate={optimisticUpdate}
// optimisticUpdate={optimisticUpdate}
onSubmit={onSubmit}
clientNotification={ClientNotification.Zap}
signal={abortSignal}
@ -266,8 +274,10 @@ export function useZap () {
let revert, cancel, nid
try {
revert = optimisticUpdate({ mutation: ZAP_MUTATION, variables, optimisticResponse, update })
strike()
// XXX avoid manual optimistic updates until
// https://github.com/stackernews/stacker.news/issues/1218 is fixed
// revert = optimisticUpdate({ mutation: ZAP_MUTATION, variables, optimisticResponse, update })
// strike()
await abortSignal.pause({ me, amount: satsDelta })
@ -277,7 +287,11 @@ export function useZap () {
let hash, hmac;
[{ hash, hmac }, cancel] = await payment.request(satsDelta)
await zap({ variables: { ...variables, hash, hmac } })
// XXX related to comment above
// await zap({ variables: { ...variables, hash, hmac } })
await zap({ variables: { ...variables, hash, hmac }, optimisticResponse, update })
strike()
} catch (error) {
revert?.()

View File

@ -6,9 +6,8 @@ import { useMe } from './me'
import { numWithUnits } from '@/lib/format'
import { useShowModal } from './modal'
import { useRoot } from './root'
import { useAct, actUpdate, ACT_MUTATION } from './item-act'
import { useAct, actUpdate } from './item-act'
import { InvoiceCanceledError, usePayment } from './payment'
import { optimisticUpdate } from '@/lib/apollo'
import { useLightning } from './lightning'
import { useToast } from './toast'
import { Types as ClientNotification, useClientNotifications } from './client-notifications'
@ -45,30 +44,21 @@ export default function PayBounty ({ children, item }) {
const notifyProps = { itemId: item.id, sats }
const optimisticResponse = { act: { ...variables, path: item.path } }
let revert, cancel, nid
let cancel, nid
try {
revert = optimisticUpdate({
mutation: ACT_MUTATION,
variables,
optimisticResponse,
update: actUpdate({ me, onUpdate: onUpdate(onComplete) })
})
if (me) {
nid = notify(ClientNotification.Bounty.PENDING, notifyProps)
}
let hash, hmac;
[{ hash, hmac }, cancel] = await payment.request(sats)
await act({
variables: { hash, hmac, ...variables },
optimisticResponse: {
act: variables
}
optimisticResponse,
update: actUpdate({ me, onUpdate: onUpdate(onComplete) })
})
} catch (error) {
revert?.()
if (error instanceof InvoiceCanceledError) {
return
}

View File

@ -8,7 +8,6 @@ import { signIn } from 'next-auth/react'
import ActionTooltip from './action-tooltip'
import { POLL_COST } from '@/lib/constants'
import { InvoiceCanceledError, usePayment } from './payment'
import { optimisticUpdate } from '@/lib/apollo'
import { useToast } from './toast'
import { Types as ClientNotification, useClientNotifications } from './client-notifications'
@ -55,20 +54,17 @@ export default function Poll ({ item }) {
const variables = { id: v.id }
const notifyProps = { itemId: item.id }
const optimisticResponse = { pollVote: v.id }
let revert, cancel, nid
let cancel, nid
try {
revert = optimisticUpdate({ mutation: POLL_VOTE_MUTATION, variables, optimisticResponse, update })
if (me) {
nid = notify(ClientNotification.PollVote.PENDING, notifyProps)
}
let hash, hmac;
[{ hash, hmac }, cancel] = await payment.request(item.pollCost || POLL_COST)
await pollVote({ variables: { hash, hmac, ...variables } })
} catch (error) {
revert?.()
await pollVote({ variables: { hash, hmac, ...variables }, optimisticResponse, update })
} catch (error) {
if (error instanceof InvoiceCanceledError) {
return
}

View File

@ -259,39 +259,35 @@ export default memo(function Text ({ rel, imgproxyUrls, children, tab, itemId, o
paddingRight: '15px'
}
try {
const { provider, id, meta } = parseEmbedUrl(href)
// Youtube video embed
if (provider === 'youtube') {
return (
<div style={videoWrapperStyles}>
<YouTube
videoId={id} className={styles.videoContainer} opts={{
playerVars: {
start: meta?.start || 0
}
}}
const { provider, id, meta } = parseEmbedUrl(href)
// Youtube video embed
if (provider === 'youtube') {
return (
<div style={videoWrapperStyles}>
<YouTube
videoId={id} className={styles.videoContainer} opts={{
playerVars: {
start: meta?.start || 0
}
}}
/>
</div>
)
}
// Rumble video embed
if (provider === 'rumble') {
return (
<div style={videoWrapperStyles}>
<div className={styles.videoContainer}>
<iframe
title='Rumble Video'
allowFullScreen=''
src={meta?.href}
/>
</div>
)
}
// Rumble video embed
if (provider === 'rumble') {
return (
<div style={videoWrapperStyles}>
<div className={styles.videoContainer}>
<iframe
title='Rumble Video'
allowFullScreen=''
src={meta?.href}
/>
</div>
</div>
)
}
} catch {
// ignore invalid URLs
</div>
)
}
// assume the link is an image which will fallback to link if it's not

View File

@ -1,10 +1,23 @@
export function ensureProtocol (value) {
if (!value) return value
value = value.trim()
if (!/^([a-z0-9]+:\/\/|mailto:)/.test(value)) {
value = 'http://' + value
let url
try {
url = new URL(value)
} catch {
try {
url = new URL('http://' + value)
} catch {
return value
}
}
return value
// remove trailing slash if new URL() added it
if (url.href.endsWith('/') && !value.endsWith('/')) {
return url.href.slice(0, -1)
}
return url.href
}
export function isExternal (url) {
@ -63,38 +76,42 @@ export function parseInternalLinks (href) {
}
export function parseEmbedUrl (href) {
const { hostname, pathname, searchParams } = new URL(href)
try {
const { hostname, pathname, searchParams } = new URL(href)
if (hostname.endsWith('youtube.com') && pathname.includes('/watch')) {
return {
provider: 'youtube',
id: searchParams.get('v'),
meta: {
href,
start: searchParams.get('t')
if (hostname.endsWith('youtube.com') && pathname.includes('/watch')) {
return {
provider: 'youtube',
id: searchParams.get('v'),
meta: {
href,
start: searchParams.get('t')
}
}
}
}
if (hostname.endsWith('youtu.be') && pathname.length > 1) {
return {
provider: 'youtube',
id: pathname.slice(1), // remove leading slash
meta: {
href,
start: searchParams.get('t')
if (hostname.endsWith('youtu.be') && pathname.length > 1) {
return {
provider: 'youtube',
id: pathname.slice(1), // remove leading slash
meta: {
href,
start: searchParams.get('t')
}
}
}
}
if (hostname.endsWith('rumble.com') && pathname.includes('/embed')) {
return {
provider: 'rumble',
id: null, // not required
meta: {
href
if (hostname.endsWith('rumble.com') && pathname.includes('/embed')) {
return {
provider: 'rumble',
id: null, // not required
meta: {
href
}
}
}
} catch {
// ignore
}
// Important to return empty object as default

5
sndev
View File

@ -159,6 +159,11 @@ OPTIONS"
sndev__logs() {
shift
if [ $# -eq 1 ]; then
docker__compose logs -t --tail=1000 -f "$@"
exit 0
fi
docker__compose logs "$@"
}