Compare commits
17 Commits
32ea514286
...
76f90e0691
Author | SHA1 | Date |
---|---|---|
Keyan | 76f90e0691 | |
abhiShandy | 2a08abd90c | |
Ben Allen | 5fa7fd9a83 | |
keyan | a0b402b4d6 | |
keyan | f0334b6719 | |
Keyan | 5c18ef2317 | |
keyan | e7fec21375 | |
keyan | b995035f46 | |
Keyan | 2ee730f8b5 | |
Felipe Bueno | a0e705b1c0 | |
Keyan | c2d207fbbb | |
Keyan | 168fd95bf0 | |
Keyan | fa237d98c9 | |
keyan | 2bf11dc848 | |
keyan | a785f907cb | |
ekzyis | 9d897e9bf7 | |
ekzyis | 6e75b9d274 |
|
@ -32,6 +32,9 @@ envbak
|
|||
.env*
|
||||
!.env.sample
|
||||
|
||||
# local settings
|
||||
.vscode/settings.json
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
|
|
20
awards.csv
20
awards.csv
|
@ -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
|
||||
|
|
|
|
@ -25,4 +25,4 @@
|
|||
font-size: 75%;
|
||||
margin-top: .15rem;
|
||||
color: var(--theme-grey) !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
.footer {
|
||||
position: sticky;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--bs-body-bg);
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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' />
|
||||
</>
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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?.()))
|
||||
|
|
|
@ -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
45
sndev
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue