Compare commits
3 Commits
d8fe698963
...
1dcb6461c7
Author | SHA1 | Date | |
---|---|---|---|
|
1dcb6461c7 | ||
|
2775b49ce7 | ||
|
ea97fbf4a4 |
@ -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?.()
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
69
lib/url.js
69
lib/url.js
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user