Compare commits

...

17 Commits

Author SHA1 Message Date
Keyan 76f90e0691
Update awards.csv 2024-04-04 19:53:19 -05:00
abhiShandy 2a08abd90c
fix: Reward page render error #1006 (#1018)
* fix: Reward page render error #1006

* accept coderabbit's suggestion

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* address lint issues

* use existing patterns

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
2024-04-04 19:31:38 -05:00
Ben Allen 5fa7fd9a83
Bottom nav uses fixed position to fix firefox bug (#1011)
* use fixed position + div placeholder

* hide footer padding when not shown

* account for mobile inset

---------

Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
2024-04-04 18:50:52 -05:00
keyan a0b402b4d6 fix sndev pr (detatched version) 2024-04-04 18:31:07 -05:00
keyan f0334b6719 Merge branch 'rerenders' 2024-04-04 18:10:13 -05:00
Keyan 5c18ef2317
prevent contexts causing rerenders (#1022) 2024-04-04 18:09:42 -05:00
keyan e7fec21375 fix hidden wallet layout shift 2024-04-04 18:07:59 -05:00
keyan b995035f46 prevent contexts causing rerenders 2024-04-04 17:52:59 -05:00
Keyan 2ee730f8b5
Update awards.csv 2024-04-04 16:36:01 -05:00
Felipe Bueno a0e705b1c0
Just some minor fixes (#1012)
* Add .vscode/settings.json to .gitignore to allow local vscode settings without making the work tree dirty

* Swap (fix) Login & SignUp button ids + Make them both 112px wide
2024-04-04 16:31:53 -05:00
Keyan c2d207fbbb
Update awards.csv 2024-04-04 16:07:21 -05:00
Keyan 168fd95bf0
Update awards.csv 2024-04-04 16:07:01 -05:00
Keyan fa237d98c9
Update awards.csv 2024-04-04 16:04:58 -05:00
keyan 2bf11dc848 remove reliance on intersection observer 2024-04-04 15:38:27 -05:00
keyan a785f907cb add sndev pr tracking option 2024-04-04 15:38:27 -05:00
ekzyis 9d897e9bf7
Use same onclose listener in sendPayment (#1020)
This is the onclose listener from getInfo.

This might also be a potential fix for undefined errors that I am getting.

With "undefined errors" I mean that the error itself is literally undefined.
2024-04-04 12:28:52 -05:00
ekzyis 6e75b9d274
Fix effect dependencies (#1019)
* Fix missing effect dependencies

* Remove unnecessary effect dependency
2024-04-04 12:23:49 -05:00
15 changed files with 163 additions and 91 deletions

3
.gitignore vendored
View File

@ -32,6 +32,9 @@ envbak
.env*
!.env.sample
# local settings
.vscode/settings.json
# vercel
.vercel

View File

@ -1,5 +1,5 @@
name,type,pr id,issue ids,difficulty,priority,changes requested,notes,amount,receive method,date paid
jp-melanson,pr,#898,#680,good-first-issue,,,,20k,jpmelanson@getalby.com,2024-03-16
jp30566347,pr,#898,#680,good-first-issue,,,,20k,jpmelanson@getalby.com,2024-03-16
NEEDcreations,issue,#898,#680,good-first-issue,,,,2k,NEEDcreations@stacker.news,2024-03-16
SatsAllDay,docs,#925,,,,,typo,100,weareallsatoshi@getalby.com,2024-03-16
SatsAllDay,issue,#933,#928,medium,,,,25k,weareallsatoshi@getalby.com,2024-03-18
@ -18,7 +18,23 @@ felipebueno,pr,#948,,,,,,100k,felipe@stacker.news,2024-03-26
benalleng,pr,#972,#923,good-first-issue,,,,20k,BenAllenG@stacker.news,2024-03-26
SatsAllDay,issue,#972,#923,good-first-issue,,,,2k,weareallsatoshi@getalby.com,2024-03-26
felipebueno,pr,#974,#884,good-first-issue,,,,20k,felipe@stacker.news,2024-03-26
h0dlr,issue,#974,#884,good-first-issue,,,,2k,???,???
h0dlr,issue,#974,#884,good-first-issue,,,,2k,0xe14b9b5981c729a3@ln.tips,2024-04-04
benalleng,pr,#975,,,,,,20k,BenAllenG@stacker.news,2024-03-26
SatsAllDay,security,#980,GHSA-qg4g-m4xq-695p,,,,,100k,weareallsatoshi@getalby.com,2024-03-28
SatsAllDay,code review,#980,GHSA-qg4g-m4xq-695p,medium,,,,25k,weareallsatoshi@getalby.com,2024-03-28
Darth-Coin,issue,#1009,#1002,easy,,,,10k,darthcoin@stacker.news,2024-04-04
atomantic,issue,#1009,#679,medium,high,,,50k,antic@stacker.news,2024-04-04
aniskhalfallah,pr,#1001,#976,good-first-issue,,,,20k,aniskhalfallah@stacker.news,2024-04-04
SatsAllDay,pr,#944,#1000,medium,,,,250k,weareallsatoshi@getalby.com,2024-04-04
SatsAllDay,issue,#944,#1000,medium,,,,25k,weareallsatoshi@getalby.com,2024-04-04
SatsAllDay,pr,#989,#984,medium,,,,250k,weareallsatoshi@getalby.com,2024-04-04
SatsAllDay,issue,#989,#984,medium,,,,25k,weareallsatoshi@getalby.com,2024-04-04
SouthKoreaLN,pr,#1015,#1010,good-first-issue,,,,20k,south_korea_ln@stacker.news,2024-04-04
SatsAllDay,issue,#1015,#1010,good-first-issue,,,,20k,weareallsatoshi@getalby.com,2024-04-04
jp30566347,pr,#991,#718,good-first-issue,,,,20k,jpmelanson@getalby.com,2024-04-04
benalleng,helpfulness,#1015,#1010,good-first-issue,,,,2k,BenAllenG@stacker.news,2024-04-04
felipebueno,pr,#1012,,,,,,20k,felipe@stacker.news,2024-04-24
abhiShandy,helpfulness,#1018,#1006,good-first-issue,,,identified problem,2k,???,2024-04-24
benalleng,issue,#1018,#1006,good-first-issue,,,,2k,BenAllenG@stacker.news,2024-04-04
benalleng,issue,#1011,#993,easy,high,,,20k,BenAllenG@stacker.news,2024-04-04
benalleng,pr,#1011,#993,easy,high,,tortured them,200k,BenAllenG@stacker.news,2024-04-04

1 name type pr id issue ids difficulty priority changes requested notes amount receive method date paid
2 jp-melanson jp30566347 pr #898 #680 good-first-issue 20k jpmelanson@getalby.com 2024-03-16
3 NEEDcreations issue #898 #680 good-first-issue 2k NEEDcreations@stacker.news 2024-03-16
4 SatsAllDay docs #925 typo 100 weareallsatoshi@getalby.com 2024-03-16
5 SatsAllDay issue #933 #928 medium 25k weareallsatoshi@getalby.com 2024-03-18
18 benalleng pr #972 #923 good-first-issue 20k BenAllenG@stacker.news 2024-03-26
19 SatsAllDay issue #972 #923 good-first-issue 2k weareallsatoshi@getalby.com 2024-03-26
20 felipebueno pr #974 #884 good-first-issue 20k felipe@stacker.news 2024-03-26
21 h0dlr issue #974 #884 good-first-issue 2k ??? 0xe14b9b5981c729a3@ln.tips ??? 2024-04-04
22 benalleng pr #975 20k BenAllenG@stacker.news 2024-03-26
23 SatsAllDay security #980 GHSA-qg4g-m4xq-695p 100k weareallsatoshi@getalby.com 2024-03-28
24 SatsAllDay code review #980 GHSA-qg4g-m4xq-695p medium 25k weareallsatoshi@getalby.com 2024-03-28
25 Darth-Coin issue #1009 #1002 easy 10k darthcoin@stacker.news 2024-04-04
26 atomantic issue #1009 #679 medium high 50k antic@stacker.news 2024-04-04
27 aniskhalfallah pr #1001 #976 good-first-issue 20k aniskhalfallah@stacker.news 2024-04-04
28 SatsAllDay pr #944 #1000 medium 250k weareallsatoshi@getalby.com 2024-04-04
29 SatsAllDay issue #944 #1000 medium 25k weareallsatoshi@getalby.com 2024-04-04
30 SatsAllDay pr #989 #984 medium 250k weareallsatoshi@getalby.com 2024-04-04
31 SatsAllDay issue #989 #984 medium 25k weareallsatoshi@getalby.com 2024-04-04
32 SouthKoreaLN pr #1015 #1010 good-first-issue 20k south_korea_ln@stacker.news 2024-04-04
33 SatsAllDay issue #1015 #1010 good-first-issue 20k weareallsatoshi@getalby.com 2024-04-04
34 jp30566347 pr #991 #718 good-first-issue 20k jpmelanson@getalby.com 2024-04-04
35 benalleng helpfulness #1015 #1010 good-first-issue 2k BenAllenG@stacker.news 2024-04-04
36 felipebueno pr #1012 20k felipe@stacker.news 2024-04-24
37 abhiShandy helpfulness #1018 #1006 good-first-issue identified problem 2k ??? 2024-04-24
38 benalleng issue #1018 #1006 good-first-issue 2k BenAllenG@stacker.news 2024-04-04
39 benalleng issue #1011 #993 easy high 20k BenAllenG@stacker.news 2024-04-04
40 benalleng pr #1011 #993 easy high tortured them 200k BenAllenG@stacker.news 2024-04-04

View File

@ -25,4 +25,4 @@
font-size: 75%;
margin-top: .15rem;
color: var(--theme-grey) !important;
}
}

View File

@ -228,7 +228,7 @@ const WalletLoggerProvider = ({ children }) => {
const log = { wallet, level, message, ts: +new Date() }
saveLog(log)
setLogs((prevLogs) => [...prevLogs, log])
}, [setLogs, saveLog])
}, [saveLog])
return (
<WalletLogsContext.Provider value={logs}>

View File

@ -35,19 +35,20 @@ export default function useModal () {
setModalStack(modalStack.slice(0, -1))
modalOptions?.onClose?.()
return setModalContent(previousModalContent)
}, [modalStack, setModalStack])
}, [modalStack, setModalStack, modalOptions?.onClose])
// this is called on every navigation due to below useEffect
const onClose = useCallback(() => {
setModalContent(null)
setModalStack([])
setModalStack(ms => ms.length > 0 ? [] : ms)
modalOptions?.onClose?.()
}, [modalOptions?.onClose])
}, [setModalStack, setModalContent, modalOptions?.onClose])
const router = useRouter()
useEffect(() => {
router.events.on('routeChangeStart', onClose)
return () => router.events.off('routeChangeStart', onClose)
}, [router, onClose])
}, [router.events, onClose])
const modal = useMemo(() => {
if (modalContent === null) {
@ -76,7 +77,7 @@ export default function useModal () {
</Modal.Body>
</Modal>
)
}, [modalContent, onClose])
}, [modalContent, onClose, modalOptions, onBack, modalStack])
const showModal = useCallback(
(getContent, options) => {

View File

@ -236,8 +236,8 @@ export function SignUpButton ({ className = 'py-0' }) {
return (
<Button
className={classNames('align-items-center ps-2 pe-3', className)}
style={{ borderWidth: '2px' }}
id='login'
style={{ borderWidth: '2px', width: '112px' }}
id='signup'
onClick={() => handleLogin('/signup')}
>
<LightningIcon
@ -259,8 +259,8 @@ export default function LoginButton ({ className }) {
return (
<Button
className='align-items-center px-3 py-1 mb-2'
id='signup'
style={{ borderWidth: '2px' }}
id='login'
style={{ borderWidth: '2px', width: '112px' }}
variant='outline-grey-darkmode'
onClick={() => handleLogin('/login')}
>
@ -353,7 +353,7 @@ export function MeCorner ({ dropNavKey, me, className }) {
<div className={className}>
<NavNotifications />
<MeDropdown me={me} dropNavKey={dropNavKey} />
<NavWalletSummary />
<NavWalletSummary className='d-inline-block' />
</div>
)
}

View File

@ -47,16 +47,19 @@ export default function BottomBar ({ sub }) {
}
return (
<div className={classNames('d-block d-md-none', styles.footer, styles.footerPadding)}>
<Navbar className='container px-0'>
<Nav className={styles.footerNav}>
<Offcanvas me={me} {...props} />
<SearchItem {...props} />
<Brand />
<NavNotifications />
<PostItem {...props} className='btn-sm' />
</Nav>
</Navbar>
</div>
<nav className='d-block d-md-none'>
<div style={{ marginBottom: '53px' }} className={styles.footerPadding} />
<div className={classNames(styles.footer, styles.footerPadding)}>
<Navbar className='container px-0'>
<Nav className={styles.footerNav}>
<Offcanvas me={me} {...props} />
<SearchItem {...props} />
<Brand />
<NavNotifications />
<PostItem {...props} className='btn-sm' />
</Nav>
</Navbar>
</div>
</nav>
)
}

View File

@ -1,6 +1,6 @@
.footer {
position: sticky;
position: fixed;
bottom: 0;
width: 100%;
background-color: var(--bs-body-bg);

View File

@ -22,9 +22,10 @@ export default function OffCanvas ({ me, dropNavKey }) {
src={me?.photoId ? `${MEDIA_URL}/${me.photoId}` : '/dorian400.jpg'} width='28' height='28'
style={{ clipPath: 'polygon(0 0, 83% 0, 100% 100%, 17% 100%)' }}
onClick={onClick}
className='pointer'
/>
)
: <span className='text-muted'><AnonIcon onClick={onClick} width='22' height='22' /></span>
: <span className='text-muted pointer'><AnonIcon onClick={onClick} width='22' height='22' /></span>
return (
<>

View File

@ -7,51 +7,52 @@ import classNames from 'classnames'
export default function StickyBar ({ prefix, sub, path, topNavKey, dropNavKey }) {
const ref = useRef()
const sticky = useRef()
const me = useMe()
useEffect(() => {
const observer = new window.IntersectionObserver(([entry]) => {
sticky?.current?.classList.toggle(styles.hide, entry.isIntersecting)
})
ref?.current && observer.observe(ref.current)
const stick = () => {
if (window.scrollY > 100) {
ref.current?.classList.remove(styles.hide)
} else {
ref.current?.classList.add(styles.hide)
}
}
window.addEventListener('scroll', stick)
return () => {
ref?.current && observer.unobserve(ref.current)
window.removeEventListener('scroll', stick)
}
}, [ref?.current, sticky?.current])
}, [ref?.current])
return (
<>
<div ref={ref} style={{ position: 'relative', top: '50px' }} />
<div className={classNames(styles.hide, styles.sticky)} ref={sticky}>
<Container className='px-0 d-none d-md-block'>
<Navbar className='py-0'>
<Nav
className={styles.navbarNav}
activeKey={topNavKey}
>
<Back />
<Brand className='me-1' />
<SearchItem className='me-0 ms-2' />
<NavPrice />
{me ? <MeCorner dropNavKey={dropNavKey} me={me} className='d-flex' /> : <AnonCorner path={path} className='d-flex' />}
</Nav>
</Navbar>
</Container>
<Container className='px-sm-0 d-block d-md-none'>
<Navbar className='py-0'>
<Nav
className={classNames(styles.navbarNav, 'justify-content-between')}
activeKey={topNavKey}
>
<Back />
<NavPrice className='flex-shrink-1 flex-grow-0' />
{me ? <NavWalletSummary className='px-2' /> : <SignUpButton />}
</Nav>
</Navbar>
</Container>
</div>
</>
<div className={classNames(styles.hide, styles.sticky)} ref={ref}>
<Container className='px-0 d-none d-md-block'>
<Navbar className='py-0'>
<Nav
className={styles.navbarNav}
activeKey={topNavKey}
>
<Back />
<Brand className='me-1' />
<SearchItem className='me-0 ms-2' />
<NavPrice />
{me ? <MeCorner dropNavKey={dropNavKey} me={me} className='d-flex' /> : <AnonCorner path={path} className='d-flex' />}
</Nav>
</Navbar>
</Container>
<Container className='px-sm-0 d-block d-md-none'>
<Navbar className='py-0'>
<Nav
className={classNames(styles.navbarNav, 'justify-content-between')}
activeKey={topNavKey}
>
<Back />
<NavPrice className='flex-shrink-1 flex-grow-0' />
{me ? <NavWalletSummary className='px-2' /> : <SignUpButton />}
</Nav>
</Navbar>
</Container>
</div>
)
}

View File

@ -531,7 +531,7 @@ export default function Notifications ({ ssrData }) {
}
}, router.asPath, { ...router.options, shallow: true })
}
}, [router, lastChecked])
}, [router?.query?.checkedAt, lastChecked])
if (!dat) return <CommentsFlatSkeleton />
@ -540,7 +540,7 @@ export default function Notifications ({ ssrData }) {
{notifications.map(n =>
<Notification
n={n} key={nid(n)}
fresh={new Date(n.sortTime) > new Date(router?.query?.checkedAt)}
fresh={new Date(n.sortTime) > new Date(router?.query?.checkedAt ?? lastChecked)}
/>)}
<MoreFooter cursor={cursor} count={notifications?.length} fetchMore={fetchMore} Skeleton={CommentsFlatSkeleton} noMoreText='NO MORE' />
</>

View File

@ -36,16 +36,6 @@ export const ToastProvider = ({ children }) => {
const [toasts, setToasts] = useState([])
const toastId = useRef(0)
const dispatchToast = useCallback((toast) => {
toast = {
...toast,
createdAt: +new Date(),
id: toastId.current++
}
setToasts(toasts => ensureFlow(toasts, toast).map(mapHidden(toast)))
return () => removeToast(toast)
}, [])
const removeToast = useCallback(({ id, onCancel, tag }) => {
setToasts(toasts => toasts.filter(toast => {
if (toast.id === id) {
@ -65,11 +55,21 @@ export const ToastProvider = ({ children }) => {
// remove toasts with same tag if they are not cancelable
return false
}))
}, [])
}, [setToasts])
const dispatchToast = useCallback((toast) => {
toast = {
...toast,
createdAt: +new Date(),
id: toastId.current++
}
setToasts(toasts => ensureFlow(toasts, toast).map(mapHidden(toast)))
return () => removeToast(toast)
}, [setToasts, removeToast])
const endFlow = useCallback((flowId) => {
setToasts(toasts => toasts.filter(toast => toast.flowId !== flowId))
}, [])
}, [setToasts])
const toaster = useMemo(() => ({
success: (body, options) => {
@ -110,13 +110,13 @@ export const ToastProvider = ({ children }) => {
// Only clear toasts with no cancel function on page navigation
// since navigation should not interfere with being able to cancel an action.
useEffect(() => {
const handleRouteChangeStart = () => setToasts(toasts => toasts.filter(({ onCancel, onUndo }) => onCancel || onUndo), [])
const handleRouteChangeStart = () => setToasts(toasts => toasts.length > 0 ? toasts.filter(({ onCancel, onUndo }) => onCancel || onUndo) : toasts)
router.events.on('routeChangeStart', handleRouteChangeStart)
return () => {
router.events.off('routeChangeStart', handleRouteChangeStart)
}
}, [router])
}, [router.events, setToasts])
// this function merges toasts with the same tag into one toast.
// for example: 3x 'zap pending' -> '(3) zap pending'

View File

@ -131,7 +131,7 @@ export function NWCProvider ({ children }) {
} finally {
setInitialized(true)
}
}, [logger])
}, [validateParams, logger])
const saveConfig = useCallback(async (config) => {
// immediately store config so it's not lost even if config is invalid
@ -235,8 +235,12 @@ export function NWCProvider ({ children }) {
},
onclose (reason) {
clearTimeout(timer)
reject(new Error(reason))
sub?.close()
if (!['closed by caller', 'relay connection closed by us'].includes(reason)) {
// only log if not closed by us (caller)
const msg = 'connection closed: ' + (reason || 'unknown reason')
logger.error(msg)
reject(new Error(msg))
}
}
})
})().catch(reject)
@ -252,7 +256,7 @@ export function NWCProvider ({ children }) {
// relay?.close()
if (relay) logger.info(`closed connection to ${relayUrl}`)
}
}, [walletPubkey, secret, logger])
}, [walletPubkey, relayUrl, secret, logger])
useEffect(() => {
loadConfig().catch(err => logger.error(err.message || err.toString?.()))

View File

@ -20,6 +20,7 @@ import { Col, Row } from 'react-bootstrap'
import { proportions } from '@/lib/madness'
import { useData } from '@/components/use-data'
import { GrowthPieChartSkeleton } from '@/components/charts-skeletons'
import { useMemo } from 'react'
const GrowthPieChart = dynamic(() => import('@/components/charts').then(mod => mod.GrowthPieChart), {
loading: () => <GrowthPieChartSkeleton />
@ -95,15 +96,20 @@ export default function Rewards ({ ssrData }) {
REWARDS,
SSR ? {} : { pollInterval: 1000, nextFetchPolicy: 'cache-and-network' })
const { data } = useQuery(REWARDS_FULL)
if (!data && !ssrData) return <PageLoading />
const dat = useData(data, ssrData)
let { rewards: [{ total, sources, time, leaderboard }] } = useMemo(() => {
return dat || { rewards: [{}] }
}, [dat])
let { rewards: [{ total, sources, time, leaderboard }] } = useData(data, ssrData)
if (rewardsData?.rewards?.length > 0) {
total = rewardsData.rewards[0].total
sources = rewardsData.rewards[0].sources
time = rewardsData.rewards[0].time
}
if (!dat) return <PageLoading />
function EstimatedReward ({ rank }) {
const totalRest = total - 1000000
return (

45
sndev
View File

@ -305,14 +305,47 @@ sndev__help_stacker_lncli() {
docker__stacker_lnd --help
}
sndev__pr() {
shift
__sndev__pr_track() {
json=$(curl -fsSH "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/stackernews/stacker.news/pulls/$1")
case $(git config --get remote.origin.url) in
"http"*) url=$(echo "$json" | grep -e '"clone_url"' | head -n1 | sed -e 's/^.*"clone_url":[[:space:]]*"//; s/",[[:space:]]*$//') ;;
*) url=$(echo "$json" | grep -e '"ssh_url"' | head -n1 | sed -e 's/^.*"ssh_url":[[:space:]]*"//; s/",[[:space:]]*$//') ;;
esac
push=$(git remote -v | grep -e "$url .*push" | head -n1) || true
if [ -n "$push" ]; then
remote=$(printf "%s" $(cut -f 1 <<<"$push"))
else
remote=$(echo "$json" | grep -e '"login"' | head -n1 | sed -e 's/^.*"login":[[:space:]]*"//; s/",[[:space:]]*$//')
git remote remove "$remote" 1>/dev/null 2>&1 || true
git remote add "$remote" "$url"
fi
ref=$(echo "$json" | grep -e '"ref"' | head -n1 | sed -e 's/^.*"ref":[[:space:]]*"//; s/",[[:space:]]*$//')
git fetch "$remote" "$ref"
git checkout -b "pr/$1" "$remote/$ref"
exit 0
}
__sndev__pr_detach() {
refspec="+refs/pull/$1/head:refs/remotes/pr/$1"
case $(git config --get remote.origin.url) in
"http"*) git fetch https://github.com/stackernews/stacker.news.git "$refspec" ;;
"http"*) git fetch https://github.com/stackernews/stacker.news.git "$refspec" ;;
*) git fetch git@github.com:stackernews/stacker.news.git "$refspec" ;;
esac
git checkout "pr/$1"
exit 0
}
sndev__pr() {
shift
case $1 in
-t|--track)
call "__sndev__pr_track" "$2" ;;
*)
call "__sndev__pr_detach" "$1" ;;
esac
}
sndev__help_pr() {
@ -320,7 +353,11 @@ sndev__help_pr() {
fetch and checkout a pr
USAGE
$ sndev pr <pr number>
$ sndev pr [OPTIONS] <pr number>
OPTIONS
-t, --track track the pr in a new branch, creating a remote if necessary
defaults to checking out the pr in a detached state
"
echo "$help"