upgrade react-bootstrap

This commit is contained in:
keyan 2023-07-24 13:35:05 -05:00
parent 54d69489b9
commit 6407455def
89 changed files with 633 additions and 495 deletions

View File

@ -1,30 +1,30 @@
import { Accordion } from 'react-bootstrap' import Accordion from 'react-bootstrap/Accordion'
import AccordionContext from 'react-bootstrap/AccordionContext'
import { useAccordionButton } from 'react-bootstrap/AccordionButton'
import ArrowRight from '../svgs/arrow-right-s-fill.svg' import ArrowRight from '../svgs/arrow-right-s-fill.svg'
import ArrowDown from '../svgs/arrow-down-s-fill.svg' import ArrowDown from '../svgs/arrow-down-s-fill.svg'
import { useEffect, useState } from 'react' import { useContext } from 'react'
export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) { function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', eventKey }) {
const [open, setOpen] = useState(show) const { activeEventKey } = useContext(AccordionContext)
const decoratedOnClick = useAccordionButton(eventKey)
useEffect(() => { const isCurrentEventKey = activeEventKey === eventKey
setOpen(show)
}, [])
return ( return (
<Accordion <div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }} onClick={decoratedOnClick}>
defaultActiveKey={show ? '0' : undefined} {isCurrentEventKey
>
<Accordion.Toggle
as={props => <div {...props} />}
eventKey='0'
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
onClick={() => setOpen(!open)}
>
{open
? <ArrowDown style={{ fill: headerColor }} height={20} width={20} /> ? <ArrowDown style={{ fill: headerColor }} height={20} width={20} />
: <ArrowRight style={{ fill: headerColor }} height={20} width={20} />} : <ArrowRight style={{ fill: headerColor }} height={20} width={20} />}
<div style={{ color: headerColor }}>{header}</div> {children}
</Accordion.Toggle> </div>
)
}
export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) {
return (
<Accordion defaultActiveKey={show ? '0' : undefined}>
<ContextAwareToggle eventKey='0'><div style={{ color: headerColor }}>{header}</div></ContextAwareToggle>
<Accordion.Collapse eventKey='0' className='mt-2'> <Accordion.Collapse eventKey='0' className='mt-2'>
<div>{body}</div> <div>{body}</div>
</Accordion.Collapse> </Accordion.Collapse>

View File

@ -1,5 +1,6 @@
import { useFormikContext } from 'formik' import { useFormikContext } from 'formik'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
import Tooltip from 'react-bootstrap/Tooltip'
export default function ActionTooltip ({ children, notForm, disable, overlayText, placement }) { export default function ActionTooltip ({ children, notForm, disable, overlayText, placement }) {
// if we're in a form, we want to hide tooltip on submit // if we're in a form, we want to hide tooltip on submit
@ -21,7 +22,9 @@ export default function ActionTooltip ({ children, notForm, disable, overlayText
trigger={['hover', 'focus']} trigger={['hover', 'focus']}
show={formik?.isSubmitting ? false : undefined} show={formik?.isSubmitting ? false : undefined}
> >
<span>
{children} {children}
</span>
</OverlayTrigger> </OverlayTrigger>
) )
} }

View File

@ -1,6 +1,6 @@
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
import { Input, InputUserSuggest } from './form' import { Input, InputUserSuggest } from './form'
import { InputGroup } from 'react-bootstrap' import InputGroup from 'react-bootstrap/InputGroup'
import { BOOST_MIN } from '../lib/constants' import { BOOST_MIN } from '../lib/constants'
import Info from './info' import Info from './info'
@ -21,7 +21,7 @@ export default function AdvPostForm ({ edit }) {
label={ label={
<div className='d-flex align-items-center'>{edit ? 'add boost' : 'boost'} <div className='d-flex align-items-center'>{edit ? 'add boost' : 'boost'}
<Info> <Info>
<ol className='font-weight-bold'> <ol className='fw-bold'>
<li>Boost ranks posts higher temporarily based on the amount</li> <li>Boost ranks posts higher temporarily based on the amount</li>
<li>The minimum boost is {BOOST_MIN} sats</li> <li>The minimum boost is {BOOST_MIN} sats</li>
<li>Each {BOOST_MIN} sats of boost is equivalent to one trusted upvote <li>Each {BOOST_MIN} sats of boost is equivalent to one trusted upvote

View File

@ -1,6 +1,7 @@
import { useRef, useState } from 'react' import { useRef, useState } from 'react'
import AvatarEditor from 'react-avatar-editor' import AvatarEditor from 'react-avatar-editor'
import { Button, Form as BootstrapForm } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import BootstrapForm from 'react-bootstrap/Form'
import Upload from './upload' import Upload from './upload'
import EditImage from '../svgs/image-edit-fill.svg' import EditImage from '../svgs/image-edit-fill.svg'
import Moon from '../svgs/moon-fill.svg' import Moon from '../svgs/moon-fill.svg'
@ -28,7 +29,7 @@ export default function Avatar ({ onSuccess }) {
<BootstrapForm.Control <BootstrapForm.Control
type='range' onChange={e => setScale(parseFloat(e.target.value))} type='range' onChange={e => setScale(parseFloat(e.target.value))}
min={1} max={2} step='0.05' min={1} max={2} step='0.05'
defaultValue={scale} custom defaultValue={scale}
/> />
</BootstrapForm.Group> </BootstrapForm.Group>
<Button onClick={() => { <Button onClick={() => {

View File

@ -1,6 +1,6 @@
import { useMutation } from '@apollo/client' import { useMutation } from '@apollo/client'
import { gql } from 'graphql-tag' import { gql } from 'graphql-tag'
import { Dropdown } from 'react-bootstrap' import Dropdown from 'react-bootstrap/Dropdown'
export default function BookmarkDropdownItem ({ item: { id, meBookmark } }) { export default function BookmarkDropdownItem ({ item: { id, meBookmark } }) {
const [bookmarkItem] = useMutation( const [bookmarkItem] = useMutation(

View File

@ -4,7 +4,7 @@ import { gql, useApolloClient, useMutation } from '@apollo/client'
import Countdown from './countdown' import Countdown from './countdown'
import AdvPostForm, { AdvPostInitial } from './adv-post-form' import AdvPostForm, { AdvPostInitial } from './adv-post-form'
import FeeButton, { EditFeeButton } from './fee-button' import FeeButton, { EditFeeButton } from './fee-button'
import { InputGroup } from 'react-bootstrap' import InputGroup from 'react-bootstrap/InputGroup'
import { bountySchema } from '../lib/validate' import { bountySchema } from '../lib/validate'
import { SubSelectInitial } from './sub-select-form' import { SubSelectInitial } from './sub-select-form'
import CancelButton from './cancel-button' import CancelButton from './cancel-button'
@ -95,7 +95,7 @@ export function BountyForm ({
topLevel topLevel
label={ label={
<> <>
{textLabel} <small className='text-muted ml-2'>optional</small> {textLabel} <small className='text-muted ms-2'>optional</small>
</> </>
} }
name='text' name='text'
@ -103,7 +103,7 @@ export function BountyForm ({
hint={ hint={
editThreshold editThreshold
? ( ? (
<div className='text-muted font-weight-bold'> <div className='text-muted fw-bold'>
<Countdown date={editThreshold} /> <Countdown date={editThreshold} />
</div> </div>
) )

View File

@ -1,9 +1,9 @@
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
export default function CancelButton ({ onClick }) { export default function CancelButton ({ onClick }) {
const router = useRouter() const router = useRouter()
return ( return (
<Button className='mr-3 text-muted nav-link font-weight-bold' variant='link' onClick={onClick || (() => router.back())}>cancel</Button> <Button className='me-3 text-muted nav-link fw-bold' variant='link' onClick={onClick || (() => router.back())}>cancel</Button>
) )
} }

View File

@ -2,7 +2,7 @@ import { Form, MarkdownInput, SubmitButton } from '../components/form'
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import styles from './reply.module.css' import styles from './reply.module.css'
import { EditFeeButton } from './fee-button' import { EditFeeButton } from './fee-button'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import Delete from './delete' import Delete from './delete'
import { commentSchema } from '../lib/validate' import { commentSchema } from '../lib/validate'

View File

@ -18,7 +18,7 @@ import Flag from '../svgs/flag-fill.svg'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
import Share from './share' import Share from './share'
import ItemInfo from './item-info' import ItemInfo from './item-info'
import { Badge } from 'react-bootstrap' import Badge from 'react-bootstrap/Badge'
import { RootProvider, useRoot } from './root' import { RootProvider, useRoot } from './root'
import { useMe } from './me' import { useMe } from './me'
@ -43,7 +43,7 @@ function Parent ({ item, rootText }) {
</Link> </Link>
{root.subName && {root.subName &&
<Link href={`/~${root.subName}`}> <Link href={`/~${root.subName}`}>
{' '}<Badge className={itemStyles.newComment} variant={null}>{root.subName}</Badge> {' '}<Badge className={itemStyles.newComment} bg={null}>{root.subName}</Badge>
</Link>} </Link>}
</> </>
) )
@ -143,7 +143,7 @@ export default function Comment ({
pendingSats={pendingSats} pendingSats={pendingSats}
commentsText='replies' commentsText='replies'
className={`${itemStyles.other} ${styles.other}`} className={`${itemStyles.other} ${styles.other}`}
embellishUser={op && <span className='text-boost font-weight-bold ml-1'>OP</span>} embellishUser={op && <Badge bg='boost' className={`ms-1 ${styles.op} bg-opacity-75`}>OP</Badge>}
extraInfo={ extraInfo={
<> <>
{includeParent && <Parent item={item} rootText={rootText} />} {includeParent && <Parent item={item} rootText={rootText} />}
@ -170,7 +170,7 @@ export default function Comment ({
}} }}
/>)} />)}
{topLevel && ( {topLevel && (
<span className='d-flex ml-auto align-items-center'> <span className='d-flex ms-auto align-items-center'>
<Share item={item} /> <Share item={item} />
</span> </span>
)} )}
@ -203,7 +203,7 @@ export default function Comment ({
{root.bounty && !bountyPaid && <PayBounty item={item} />} {root.bounty && !bountyPaid && <PayBounty item={item} />}
</Reply>} </Reply>}
{children} {children}
<div className={`${styles.comments} ml-sm-1 ml-md-3`}> <div className={`${styles.comments} ms-sm-1 ms-md-3`}>
{item.comments && !noComments {item.comments && !noComments
? item.comments.map((item) => ( ? item.comments.map((item) => (
<Comment depth={depth + 1} key={item.id} item={item} /> <Comment depth={depth + 1} key={item.id} item={item} />
@ -220,7 +220,7 @@ export default function Comment ({
function DepthLimit ({ item }) { function DepthLimit ({ item }) {
if (item.ncomments > 0) { if (item.ncomments > 0) {
return ( return (
<Link href={`/items/${item.id}`} className='d-block p-3 font-weight-bold text-muted w-100 text-center'> <Link href={`/items/${item.id}`} className='d-block p-3 fw-bold text-muted w-100 text-center'>
view replies view replies
</Link> </Link>
) )
@ -252,7 +252,7 @@ export function CommentSkeleton ({ skeletonChildren }) {
<div className={styles.replyPadder}> <div className={styles.replyPadder}>
<div className={`${itemStyles.other} ${styles.reply} clouds`} /> <div className={`${itemStyles.other} ${styles.reply} clouds`} />
</div> </div>
<div className={`${styles.comments} ml-sm-1 ml-md-3`}> <div className={`${styles.comments} ms-sm-1 ms-md-3`}>
{skeletonChildren {skeletonChildren
? <CommentSkeleton skeletonChildren={skeletonChildren - 1} /> ? <CommentSkeleton skeletonChildren={skeletonChildren - 1} />
: null} : null}

View File

@ -26,6 +26,11 @@
cursor: pointer; cursor: pointer;
} }
.op {
margin-top: -1px;
vertical-align: text-top;
}
.collapsed .hunk { .collapsed .hunk {
margin-bottom: .5rem; margin-bottom: .5rem;
} }

View File

@ -2,7 +2,8 @@ import { gql, useApolloClient, useLazyQuery } from '@apollo/client'
import { useState } from 'react' import { useState } from 'react'
import Comment, { CommentSkeleton } from './comment' import Comment, { CommentSkeleton } from './comment'
import styles from './header.module.css' import styles from './header.module.css'
import { Nav, Navbar } from 'react-bootstrap' import Nav from 'react-bootstrap/Nav'
import Navbar from 'react-bootstrap/Navbar'
import { COMMENTS_QUERY } from '../fragments/items' import { COMMENTS_QUERY } from '../fragments/items'
import { COMMENTS } from '../fragments/comments' import { COMMENTS } from '../fragments/comments'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
@ -19,7 +20,7 @@ export function CommentsHeader ({ handleSort, pinned, bio, parentCreatedAt, comm
} }
return ( return (
<Navbar className='pt-1 pb-0'> <Navbar className='pt-1 pb-0 px-3'>
<Nav <Nav
className={styles.navbarNav} className={styles.navbarNav}
activeKey={sort} activeKey={sort}
@ -27,7 +28,7 @@ export function CommentsHeader ({ handleSort, pinned, bio, parentCreatedAt, comm
<Nav.Item className='text-muted'> <Nav.Item className='text-muted'>
{abbrNum(commentSats)} sats {abbrNum(commentSats)} sats
</Nav.Item> </Nav.Item>
<div className='ml-auto d-flex'> <div className='ms-auto d-flex'>
<Nav.Item> <Nav.Item>
<Nav.Link <Nav.Link
eventKey='hot' eventKey='hot'

View File

@ -1,7 +1,9 @@
import { Badge, OverlayTrigger, Tooltip } from 'react-bootstrap' import Badge from 'react-bootstrap/Badge'
import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
import Tooltip from 'react-bootstrap/Tooltip'
import CowboyHatIcon from '../svgs/cowboy.svg' import CowboyHatIcon from '../svgs/cowboy.svg'
export default function CowboyHat ({ user, badge, className = 'ml-1', height = 16, width = 16 }) { export default function CowboyHat ({ user, badge, className = 'ms-1', height = 16, width = 16 }) {
if (user?.streak === null || user.hideCowboyHat) { if (user?.streak === null || user.hideCowboyHat) {
return null return null
} }
@ -11,11 +13,11 @@ export default function CowboyHat ({ user, badge, className = 'ml-1', height = 1
<HatTooltip overlayText={streak ? `${streak} days` : 'new'}> <HatTooltip overlayText={streak ? `${streak} days` : 'new'}>
{badge {badge
? ( ? (
<Badge variant='grey-medium' className='ml-2 d-inline-flex align-items-center'> <Badge bg='grey-medium' className='ms-2 d-inline-flex align-items-center'>
<CowboyHatIcon className={className} height={height} width={width} /> <CowboyHatIcon className={className} height={height} width={width} />
<span className='ml-1'>{streak || 'new'}</span> <span className='ms-1 text-dark'>{streak || 'new'}</span>
</Badge>) </Badge>)
: <CowboyHatIcon className={className} height={height} width={width} />} : <span><CowboyHatIcon className={className} height={height} width={width} /></span>}
</HatTooltip> </HatTooltip>
) )
} }

View File

@ -1,7 +1,9 @@
import { useMutation } from '@apollo/client' import { useMutation } from '@apollo/client'
import { gql } from 'graphql-tag' import { gql } from 'graphql-tag'
import { useState } from 'react' import { useState } from 'react'
import { Alert, Button, Dropdown } from 'react-bootstrap' import Alert from 'react-bootstrap/Alert'
import Button from 'react-bootstrap/Button'
import Dropdown from 'react-bootstrap/Dropdown'
import { useShowModal } from './modal' import { useShowModal } from './modal'
export default function Delete ({ itemId, children, onDelete }) { export default function Delete ({ itemId, children, onDelete }) {
@ -63,7 +65,7 @@ function DeleteConfirm ({ onConfirm }) {
return ( return (
<> <>
{error && <Alert variant='danger' onClose={() => setError(undefined)} dismissible>{error}</Alert>} {error && <Alert variant='danger' onClose={() => setError(undefined)} dismissible>{error}</Alert>}
<p className='font-weight-bolder'>Are you sure? This is a gone forever kind of delete.</p> <p className='fw-bolder'>Are you sure? This is a gone forever kind of delete.</p>
<div className='d-flex justify-content-end'> <div className='d-flex justify-content-end'>
<Button <Button
variant='danger' onClick={async () => { variant='danger' onClick={async () => {

View File

@ -8,7 +8,7 @@ import { ITEM_FIELDS } from '../fragments/items'
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
import Item from './item' import Item from './item'
import Delete from './delete' import Delete from './delete'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { discussionSchema } from '../lib/validate' import { discussionSchema } from '../lib/validate'
import { SubSelectInitial } from './sub-select-form' import { SubSelectInitial } from './sub-select-form'
import CancelButton from './cancel-button' import CancelButton from './cancel-button'
@ -91,11 +91,11 @@ export function DiscussionForm ({
/> />
<MarkdownInput <MarkdownInput
topLevel topLevel
label={<>{textLabel} <small className='text-muted ml-2'>optional</small></>} label={<>{textLabel} <small className='text-muted ms-2'>optional</small></>}
name='text' name='text'
minRows={6} minRows={6}
hint={editThreshold hint={editThreshold
? <div className='text-muted font-weight-bold'><Countdown date={editThreshold} /></div> ? <div className='text-muted fw-bold'><Countdown date={editThreshold} /></div>
: null} : null}
/> />
<AdvPostForm edit={!!item} /> <AdvPostForm edit={!!item} />

View File

@ -1,5 +1,5 @@
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import { Dropdown } from 'react-bootstrap' import Dropdown from 'react-bootstrap/Dropdown'
import FundError from './fund-error' import FundError from './fund-error'
import { useShowModal } from './modal' import { useShowModal } from './modal'

View File

@ -1,4 +1,4 @@
import { Table } from 'react-bootstrap' import Table from 'react-bootstrap/Table'
import ActionTooltip from './action-tooltip' import ActionTooltip from './action-tooltip'
import Info from './info' import Info from './info'
import styles from './fee-button.module.css' import styles from './fee-button.module.css'
@ -31,7 +31,7 @@ function Receipt ({ cost, repetition, hasImgLink, baseFee, parentId, boost }) {
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td className='font-weight-bold'>{cost} sats</td> <td className='fw-bold'>{cost} sats</td>
<td align='right' className='font-weight-light'>total fee</td> <td align='right' className='font-weight-light'>total fee</td>
</tr> </tr>
</tfoot> </tfoot>
@ -90,7 +90,7 @@ function EditReceipt ({ cost, paidSats, addImgLink, boost, parentId }) {
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td className='font-weight-bold'>{cost} sats</td> <td className='fw-bold'>{cost} sats</td>
<td align='right' className='font-weight-light'>total fee</td> <td align='right' className='font-weight-light'>total fee</td>
</tr> </tr>
</tfoot> </tfoot>

View File

@ -1,4 +1,6 @@
import { Container, OverlayTrigger, Popover } from 'react-bootstrap' import Container from 'react-bootstrap/Container'
import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
import Popover from 'react-bootstrap/Popover'
import { CopyInput } from './form' import { CopyInput } from './form'
import styles from './footer.module.css' import styles from './footer.module.css'
import Texas from '../svgs/texas.svg' import Texas from '../svgs/texas.svg'
@ -15,7 +17,7 @@ import useDarkMode from './dark-mode'
const RssPopover = ( const RssPopover = (
<Popover> <Popover>
<Popover.Content style={{ fontWeight: 500, fontSize: '.9rem' }}> <Popover.Body style={{ fontWeight: 500, fontSize: '.9rem' }}>
<div className='d-flex justify-content-center'> <div className='d-flex justify-content-center'>
<a href='/rss' className='nav-link p-0 d-inline-flex'> <a href='/rss' className='nav-link p-0 d-inline-flex'>
home home
@ -38,13 +40,13 @@ const RssPopover = (
jobs jobs
</a> </a>
</div> </div>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
) )
const SocialsPopover = ( const SocialsPopover = (
<Popover> <Popover>
<Popover.Content style={{ fontWeight: 500, fontSize: '.9rem' }}> <Popover.Body style={{ fontWeight: 500, fontSize: '.9rem' }}>
<a <a
href='https://snort.social/p/npub1jfujw6llhq7wuvu5detycdsq5v5yqf56sgrdq8wlgrryx2a2p09svwm0gx' className='nav-link p-0 d-inline-flex' href='https://snort.social/p/npub1jfujw6llhq7wuvu5detycdsq5v5yqf56sgrdq8wlgrryx2a2p09svwm0gx' className='nav-link p-0 d-inline-flex'
target='_blank' rel='noreferrer' target='_blank' rel='noreferrer'
@ -72,13 +74,13 @@ const SocialsPopover = (
> >
pod pod
</a> </a>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
) )
const ChatPopover = ( const ChatPopover = (
<Popover> <Popover>
<Popover.Content style={{ fontWeight: 500, fontSize: '.9rem' }}> <Popover.Body style={{ fontWeight: 500, fontSize: '.9rem' }}>
<a <a
href='https://tribes.sphinx.chat/t/stackerzchat' className='nav-link p-0 d-inline-flex' href='https://tribes.sphinx.chat/t/stackerzchat' className='nav-link p-0 d-inline-flex'
target='_blank' rel='noreferrer' target='_blank' rel='noreferrer'
@ -99,13 +101,13 @@ const ChatPopover = (
> >
simplex simplex
</a> </a>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
) )
const AnalyticsPopover = ( const AnalyticsPopover = (
<Popover> <Popover>
<Popover.Content style={{ fontWeight: 500, fontSize: '.9rem' }}> <Popover.Body style={{ fontWeight: 500, fontSize: '.9rem' }}>
<a <a
href='https://plausible.io/stacker.news' className='nav-link p-0 d-inline-flex' href='https://plausible.io/stacker.news' className='nav-link p-0 d-inline-flex'
target='_blank' rel='noreferrer' target='_blank' rel='noreferrer'
@ -116,7 +118,7 @@ const AnalyticsPopover = (
<Link href='/stackers/day' className='nav-link p-0 d-inline-flex'> <Link href='/stackers/day' className='nav-link p-0 d-inline-flex'>
stackers stackers
</Link> </Link>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
) )
@ -151,7 +153,7 @@ export default function Footer ({ links = true }) {
<> <>
<div className='mb-1'> <div className='mb-1'>
<DarkModeIcon onClick={darkModeToggle} width={20} height={20} className='fill-grey theme' suppressHydrationWarning /> <DarkModeIcon onClick={darkModeToggle} width={20} height={20} className='fill-grey theme' suppressHydrationWarning />
<LnIcon onClick={toggleLightning} width={20} height={20} className='ml-2 fill-grey theme' suppressHydrationWarning /> <LnIcon onClick={toggleLightning} width={20} height={20} className='ms-2 fill-grey theme' suppressHydrationWarning />
</div> </div>
<div className='mb-0' style={{ fontWeight: 500 }}> <div className='mb-0' style={{ fontWeight: 500 }}>
<Rewards /> <Rewards />
@ -207,7 +209,7 @@ export default function Footer ({ links = true }) {
<div <div
className={`text-small mx-auto mb-2 ${styles.connect}`} className={`text-small mx-auto mb-2 ${styles.connect}`}
> >
<span className='nav-item text-muted mr-2'>connect:</span> <span className='nav-item text-muted me-2'>connect:</span>
<CopyInput <CopyInput
size='sm' size='sm'
groupClassName='mb-0 w-100' groupClassName='mb-0 w-100'
@ -219,23 +221,23 @@ export default function Footer ({ links = true }) {
href='https://amboss.space/node/03cc1d0932bb99b0697f5b5e5961b83ab7fd66f1efc4c9f5c7bad66c1bcbe78f02' href='https://amboss.space/node/03cc1d0932bb99b0697f5b5e5961b83ab7fd66f1efc4c9f5c7bad66c1bcbe78f02'
target='_blank' rel='noreferrer' target='_blank' rel='noreferrer'
> >
<Amboss className='ml-2 theme' width={20} height={20} /> <Amboss className='ms-2 theme' width={20} height={20} />
</a> </a>
</div>} </div>}
<small className='d-flex justify-content-center align-items-center text-muted flex-wrap'> <small className='d-flex justify-content-center align-items-center text-muted flex-wrap'>
<a className={`${styles.contrastLink} d-flex align-items-center`} href='https://github.com/stackernews/stacker.news' target='_blank' rel='noreferrer'> <a className={`${styles.contrastLink} d-flex align-items-center`} href='https://github.com/stackernews/stacker.news' target='_blank' rel='noreferrer'>
FOSS <Github width={20} height={20} className='mx-1' /> FOSS <Github width={20} height={20} className='mx-1' />
</a> </a>
made in Austin<Texas className='ml-1' width={20} height={20} /> made in Austin<Texas className='ms-1' width={20} height={20} />
<span className='ml-1'>by</span> <span className='ms-1'>by</span>
<span> <span>
<Link href='/k00b' className='ml-1'> <Link href='/k00b' className='ms-1'>
@k00b @k00b
</Link> </Link>
<Link href='/kr' className='ml-1'> <Link href='/kr' className='ms-1'>
@kr @kr
</Link> </Link>
<Link href='/ekzyis' className='ml-1'> <Link href='/ekzyis' className='ms-1'>
@ekzyis @ekzyis
</Link> </Link>
</span> </span>

View File

@ -6,7 +6,10 @@ import { Formik, Form as FormikForm, useFormikContext, useField, FieldArray } fr
import React, { createContext, useContext, useEffect, useRef, useState } from 'react' import React, { createContext, useContext, useEffect, useRef, useState } from 'react'
import copy from 'clipboard-copy' import copy from 'clipboard-copy'
import Thumb from '../svgs/thumb-up-fill.svg' import Thumb from '../svgs/thumb-up-fill.svg'
import { Col, Dropdown as BootstrapDropdown, Nav } from 'react-bootstrap' import Col from 'react-bootstrap/Col'
import Dropdown from 'react-bootstrap/Dropdown'
import Nav from 'react-bootstrap/Nav'
import Row from 'react-bootstrap/Row'
import Markdown from '../svgs/markdown-line.svg' import Markdown from '../svgs/markdown-line.svg'
import styles from './form.module.css' import styles from './form.module.css'
import Text from '../components/text' import Text from '../components/text'
@ -53,6 +56,7 @@ export function CopyInput (props) {
onClick={handleClick} onClick={handleClick}
append={ append={
<Button <Button
className={styles.appendButton}
size={props.size} size={props.size}
onClick={handleClick} onClick={handleClick}
> >
@ -109,7 +113,7 @@ export function MarkdownInput ({ label, topLevel, groupClassName, onChange, setH
<Nav.Link eventKey='preview' disabled={!meta.value}>preview</Nav.Link> <Nav.Link eventKey='preview' disabled={!meta.value}>preview</Nav.Link>
</Nav.Item> </Nav.Item>
<a <a
className='ml-auto text-muted d-flex align-items-center' className='ms-auto text-muted d-flex align-items-center'
href='https://guides.github.com/features/mastering-markdown/' target='_blank' rel='noreferrer' href='https://guides.github.com/features/mastering-markdown/' target='_blank' rel='noreferrer'
> >
<Markdown width={18} height={18} /> <Markdown width={18} height={18} />
@ -206,7 +210,7 @@ const insertMarkdownItalicFormatting = insertMarkdownFormatting(
function FormGroup ({ className, label, children }) { function FormGroup ({ className, label, children }) {
return ( return (
<BootstrapForm.Group className={className}> <BootstrapForm.Group className={`form-group ${className}`}>
{label && <BootstrapForm.Label>{label}</BootstrapForm.Label>} {label && <BootstrapForm.Label>{label}</BootstrapForm.Label>}
{children} {children}
</BootstrapForm.Group> </BootstrapForm.Group>
@ -244,11 +248,7 @@ function InputInner ({
return ( return (
<> <>
<InputGroup hasValidation> <InputGroup hasValidation>
{prepend && (
<InputGroup.Prepend>
{prepend} {prepend}
</InputGroup.Prepend>
)}
<BootstrapForm.Control <BootstrapForm.Control
onKeyDown={(e) => { onKeyDown={(e) => {
const metaOrCtrl = e.metaKey || e.ctrlKey const metaOrCtrl = e.metaKey || e.ctrlKey
@ -274,8 +274,6 @@ function InputInner ({
isInvalid={invalid} isInvalid={invalid}
isValid={showValid && meta.initialValue !== meta.value && meta.touched && !meta.error} isValid={showValid && meta.initialValue !== meta.value && meta.touched && !meta.error}
/> />
{(append || (clear && field.value)) && (
<InputGroup.Append>
{(clear && field.value) && {(clear && field.value) &&
<Button <Button
variant={null} variant={null}
@ -288,12 +286,10 @@ function InputInner ({
onChange(formik, { target: { value: '' } }) onChange(formik, { target: { value: '' } })
} }
}} }}
className={`${styles.clearButton} ${invalid ? styles.isInvalid : ''}`} className={`${styles.clearButton} ${styles.appendButton} ${invalid ? styles.isInvalid : ''}`}
><CloseIcon className='fill-grey' height={20} width={20} /> ><CloseIcon className='fill-grey' height={20} width={20} />
</Button>} </Button>}
{append} {append}
</InputGroup.Append>
)}
<BootstrapForm.Control.Feedback type='invalid'> <BootstrapForm.Control.Feedback type='invalid'>
{meta.touched && meta.error} {meta.touched && meta.error}
</BootstrapForm.Control.Feedback> </BootstrapForm.Control.Feedback>
@ -357,10 +353,10 @@ export function InputUserSuggest ({ label, groupClassName, ...props }) {
} }
}} }}
/> />
<BootstrapDropdown show={suggestions.array.length > 0}> <Dropdown show={suggestions.array.length > 0}>
<BootstrapDropdown.Menu className={styles.suggestionsMenu}> <Dropdown.Menu className={styles.suggestionsMenu}>
{suggestions.array.map((v, i) => {suggestions.array.map((v, i) =>
<BootstrapDropdown.Item <Dropdown.Item
key={v.name} key={v.name}
active={suggestions.index === i} active={suggestions.index === i}
onClick={() => { onClick={() => {
@ -369,9 +365,9 @@ export function InputUserSuggest ({ label, groupClassName, ...props }) {
}} }}
> >
{v.name} {v.name}
</BootstrapDropdown.Item>)} </Dropdown.Item>)}
</BootstrapDropdown.Menu> </Dropdown.Menu>
</BootstrapDropdown> </Dropdown>
</FormGroup> </FormGroup>
) )
} }
@ -394,14 +390,14 @@ export function VariableInput ({ label, groupClassName, name, hint, max, min, re
<> <>
{options?.map((_, i) => ( {options?.map((_, i) => (
<div key={i}> <div key={i}>
<BootstrapForm.Row className='mb-2'> <Row className='mb-2'>
<Col> <Col>
<InputInner name={`${name}[${i}]`} {...props} readOnly={i < readOnlyLen} placeholder={i >= min ? 'optional' : undefined} /> <InputInner name={`${name}[${i}]`} {...props} readOnly={i < readOnlyLen} placeholder={i >= min ? 'optional' : undefined} />
</Col> </Col>
{options.length - 1 === i && options.length !== max {options.length - 1 === i && options.length !== max
? <AddIcon className='fill-grey align-self-center pointer mx-2' onClick={() => fieldArrayHelpers.push('')} /> ? <Col className='d-flex ps-0' xs='auto'><AddIcon className='fill-grey align-self-center justify-self-center pointer' onClick={() => fieldArrayHelpers.push('')} /></Col>
: null} : null}
</BootstrapForm.Row> </Row>
</div> </div>
))} ))}
</> </>
@ -423,10 +419,9 @@ export function Checkbox ({ children, label, groupClassName, hiddenLabel, extra,
// return the correct bag of props for you // return the correct bag of props for you
const [field,, helpers] = useField({ ...props, type: 'checkbox' }) const [field,, helpers] = useField({ ...props, type: 'checkbox' })
return ( return (
<BootstrapForm.Group className={groupClassName}> <FormGroup className={groupClassName}>
{hiddenLabel && <BootstrapForm.Label className='invisible'>{label}</BootstrapForm.Label>} {hiddenLabel && <BootstrapForm.Label className='invisible'>{label}</BootstrapForm.Label>}
<BootstrapForm.Check <BootstrapForm.Check
custom
id={props.id || props.name} id={props.id || props.name}
inline={inline} inline={inline}
> >
@ -444,7 +439,7 @@ export function Checkbox ({ children, label, groupClassName, hiddenLabel, extra,
</div>} </div>}
</BootstrapForm.Check.Label> </BootstrapForm.Check.Label>
</BootstrapForm.Check> </BootstrapForm.Check>
</BootstrapForm.Group> </FormGroup>
) )
} }
@ -497,8 +492,7 @@ export function Select ({ label, items, groupClassName, onChange, noForm, overri
return ( return (
<FormGroup label={label} className={groupClassName}> <FormGroup label={label} className={groupClassName}>
<BootstrapForm.Control <BootstrapForm.Select
as='select'
{...field} {...props} {...field} {...props}
onChange={(e) => { onChange={(e) => {
if (field?.onChange) { if (field?.onChange) {
@ -509,11 +503,10 @@ export function Select ({ label, items, groupClassName, onChange, noForm, overri
onChange(formik, e) onChange(formik, e)
} }
}} }}
custom
isInvalid={invalid} isInvalid={invalid}
> >
{items.map(item => <option key={item}>{item}</option>)} {items.map(item => <option key={item}>{item}</option>)}
</BootstrapForm.Control> </BootstrapForm.Select>
<BootstrapForm.Control.Feedback type='invalid'> <BootstrapForm.Control.Feedback type='invalid'>
{meta.touched && meta.error} {meta.touched && meta.error}
</BootstrapForm.Control.Feedback> </BootstrapForm.Control.Feedback>

View File

@ -19,15 +19,23 @@
height: auto; height: auto;
} }
.appendButton {
border-left: 0 !important;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.clearButton { .clearButton {
background-color: var(--theme-inputBg); background-color: var(--theme-inputBg);
border: 1px solid var(--theme-borderColor); border: 1px solid var(--theme-borderColor);
padding: 0rem 0.5rem; padding: 0rem 0.5rem;
border-left: 0;
display: flex;
align-items: center;
} }
.clearButton:hover, .clearButton:active {
background-color: var(--theme-inputBg) !important;
border: 1px solid var(--theme-borderColor) !important;
border-left: 0 !important;
}
.clearButton.isInvalid { .clearButton.isInvalid {
border-color: #c03221; border-color: #c03221;
} }

View File

@ -1,10 +1,10 @@
import Link from 'next/link' import Link from 'next/link'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
export default function FundError ({ onClose }) { export default function FundError ({ onClose }) {
return ( return (
<> <>
<p className='font-weight-bolder'>you need more sats</p> <p className='fw-bolder'>you need more sats</p>
<div className='d-flex justify-content-end'> <div className='d-flex justify-content-end'>
<Link href='/wallet?type=fund'> <Link href='/wallet?type=fund'>
<Button variant='success' onClick={onClose}>fund</Button> <Button variant='success' onClick={onClose}>fund</Button>

View File

@ -3,7 +3,9 @@ import Nav from 'react-bootstrap/Nav'
import Link from 'next/link' import Link from 'next/link'
import styles from './header.module.css' import styles from './header.module.css'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { Button, Container, NavDropdown } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import Container from 'react-bootstrap/Container'
import NavDropdown from 'react-bootstrap/NavDropdown'
import Price from './price' import Price from './price'
import { useMe } from './me' import { useMe } from './me'
import Head from 'next/head' import Head from 'next/head'
@ -37,7 +39,7 @@ function Back () {
}, [router.asPath]) }, [router.asPath])
if (show) { if (show) {
return <a role='button' tabIndex='0' className='nav-link standalone p-0' onClick={() => router.back()}><BackArrow className='theme mr-1 mr-md-2' width={22} height={22} /></a> return <a role='button' tabIndex='0' className='nav-link standalone p-0' onClick={() => router.back()}><BackArrow className='theme me-1 me-md-2' width={22} height={22} /></a>
} }
return null return null
} }
@ -54,8 +56,8 @@ function NotificationBell () {
<link rel='shortcut icon' href={data?.hasNewNotes ? '/favicon-notify.png' : '/favicon.png'} /> <link rel='shortcut icon' href={data?.hasNewNotes ? '/favicon-notify.png' : '/favicon.png'} />
</Head> </Head>
<Link href='/notifications' passHref legacyBehavior> <Link href='/notifications' passHref legacyBehavior>
<Nav.Link eventKey='notifications' className='pl-0 position-relative'> <Nav.Link eventKey='notifications' className='ps-0 position-relative'>
<NoteIcon height={22} width={22} className='theme' /> <NoteIcon height={22} width={22} className='theme' style={{ marginTop: '-4px' }} />
{data?.hasNewNotes && {data?.hasNewNotes &&
<span className={styles.notification}> <span className={styles.notification}>
<span className='invisible'>{' '}</span> <span className='invisible'>{' '}</span>
@ -70,23 +72,23 @@ function StackerCorner ({ dropNavKey }) {
const me = useMe() const me = useMe()
return ( return (
<div className='d-flex align-items-center ml-auto'> <div className='d-flex ms-auto'>
<NotificationBell /> <NotificationBell />
<div className='position-relative'> <div className='position-relative'>
<NavDropdown <NavDropdown
className={styles.dropdown} className={styles.dropdown}
title={ title={
<Nav.Link eventKey={me.name} as='span' className='p-0 d-flex align-items-center' onClick={e => e.preventDefault()}> <Nav.Link eventKey={me.name} as='span' className='p-0' onClick={e => e.preventDefault()}>
{`@${me.name}`}<CowboyHat user={me} /> {`@${me.name}`}<CowboyHat user={me} />
</Nav.Link> </Nav.Link>
} }
alignRight align='end'
> >
<Link href={'/' + me.name} passHref legacyBehavior> <Link href={'/' + me.name} passHref legacyBehavior>
<NavDropdown.Item active={me.name === dropNavKey}> <NavDropdown.Item active={me.name === dropNavKey}>
profile profile
{me && !me.bioId && {me && !me.bioId &&
<div className='p-1 d-inline-block bg-secondary ml-1'> <div className='p-1 d-inline-block bg-secondary ms-1'>
<span className='invisible'>{' '}</span> <span className='invisible'>{' '}</span>
</div>} </div>}
</NavDropdown.Item> </NavDropdown.Item>
@ -147,9 +149,9 @@ function LurkerCorner ({ path }) {
}), [router]) }), [router])
return path !== '/login' && path !== '/signup' && !path.startsWith('/invites') && return path !== '/login' && path !== '/signup' && !path.startsWith('/invites') &&
<div className='ml-auto'> <div className='ms-auto'>
<Button <Button
className='align-items-center px-3 py-1 mr-2' className='align-items-center px-3 py-1 me-2'
id='signup' id='signup'
style={{ borderWidth: '2px' }} style={{ borderWidth: '2px' }}
variant='outline-grey-darkmode' variant='outline-grey-darkmode'
@ -158,7 +160,7 @@ function LurkerCorner ({ path }) {
login login
</Button> </Button>
<Button <Button
className='align-items-center pl-2 py-1 pr-3' className='align-items-center ps-2 py-1 pe-3'
style={{ borderWidth: '2px' }} style={{ borderWidth: '2px' }}
id='login' id='login'
onClick={() => handleLogin('/signup')} onClick={() => handleLogin('/signup')}
@ -166,7 +168,7 @@ function LurkerCorner ({ path }) {
<LightningIcon <LightningIcon
width={17} width={17}
height={17} height={17}
className='mr-1' className='me-1'
/>sign up />sign up
</Button> </Button>
</div> </div>
@ -232,7 +234,7 @@ export default function Header ({ sub }) {
const me = useMe() const me = useMe()
return ( return (
<Container as='header' className='px-0'> <Container as='header'>
<Navbar className='pb-0 pb-lg-2'> <Navbar className='pb-0 pb-lg-2'>
<Nav <Nav
className={styles.navbarNav} className={styles.navbarNav}
@ -249,11 +251,11 @@ export default function Header ({ sub }) {
<NavItems className='d-none d-lg-flex mx-2' prefix={prefix} sub={sub} /> <NavItems className='d-none d-lg-flex mx-2' prefix={prefix} sub={sub} />
<PostItem className='d-none d-lg-flex mx-2' prefix={prefix} /> <PostItem className='d-none d-lg-flex mx-2' prefix={prefix} />
<Link href={prefix + '/search'} passHref legacyBehavior> <Link href={prefix + '/search'} passHref legacyBehavior>
<Nav.Link eventKey='search' className='position-relative d-none d-lg-flex align-items-center pr-0 ml-2'> <Nav.Link eventKey='search' className='position-relative d-none d-lg-flex align-items-center pe-0 ms-2'>
<SearchIcon className='theme' width={22} height={22} /> <SearchIcon className='theme' width={22} height={22} />
</Nav.Link> </Nav.Link>
</Link> </Link>
<Nav.Item className={`${styles.price} ml-auto align-items-center ${me?.name.length > 10 ? 'd-none d-lg-flex' : ''}`}> <Nav.Item className={`${styles.price} ms-auto align-items-center ${me?.name.length > 10 ? 'd-none d-lg-flex' : ''}`}>
<Price className='nav-link text-monospace' /> <Price className='nav-link text-monospace' />
</Nav.Item> </Nav.Item>
{me ? <StackerCorner dropNavKey={dropNavKey} /> : <LurkerCorner path={path} />} {me ? <StackerCorner dropNavKey={dropNavKey} /> : <LurkerCorner path={path} />}
@ -264,13 +266,13 @@ export default function Header ({ sub }) {
className={`${styles.navbarNav}`} className={`${styles.navbarNav}`}
activeKey={topNavKey} activeKey={topNavKey}
> >
<NavItems className='mr-1' prefix={prefix} sub={sub} /> <NavItems className='me-1' prefix={prefix} sub={sub} />
<Link href={prefix + '/search'} passHref legacyBehavior> <Link href={prefix + '/search'} passHref legacyBehavior>
<Nav.Link eventKey='search' className='position-relative ml-auto d-flex mr-1'> <Nav.Link eventKey='search' className='position-relative ms-auto d-flex me-1'>
<SearchIcon className='theme' width={22} height={22} /> <SearchIcon className='theme' width={22} height={22} />
</Nav.Link> </Nav.Link>
</Link> </Link>
<PostItem className='mr-0 pr-0' prefix={prefix} /> <PostItem className='me-0' prefix={prefix} />
</Nav> </Nav>
</Navbar> </Navbar>
</Container> </Container>

View File

@ -5,7 +5,7 @@
line-height: 100%; line-height: 100%;
margin-bottom: -.3rem; margin-bottom: -.3rem;
margin-right: 0; margin-right: 0;
text-shadow: 0 0 10px var(--primary); text-shadow: 0 0 10px var(--bs-primary);
color: var(--theme-brandColor) !important; color: var(--theme-brandColor) !important;
} }
@ -28,10 +28,14 @@
fill: var(--theme-navLinkActive); fill: var(--theme-navLinkActive);
} }
.dropdown svg {
vertical-align: text-top;
}
.jobIndicator { .jobIndicator {
position: absolute; position: absolute;
padding: .25rem; padding: .25rem;
background-color: var(--primary); background-color: var(--bs-primary);
top: 3px; top: 3px;
right: 0px; right: 0px;
border: 1px solid var(--theme-body); border: 1px solid var(--theme-body);
@ -40,8 +44,8 @@
.notification { .notification {
position: absolute; position: absolute;
padding: .25rem; padding: .25rem;
background-color: var(--danger); background-color: var(--bs-danger);
top: 3px; top: 1px;
right: 8px; right: 8px;
border: 1px solid var(--theme-body); border: 1px solid var(--theme-body);
} }

View File

@ -6,7 +6,7 @@ export default function Info ({ children, iconClassName = 'fill-theme-color' })
return ( return (
<InfoIcon <InfoIcon
width={18} height={18} className={`${iconClassName} pointer ml-1`} width={18} height={18} className={`${iconClassName} pointer ms-1`}
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
showModal(onClose => children) showModal(onClose => children)

View File

@ -6,7 +6,7 @@ function InvoiceDefaultStatus ({ status }) {
return ( return (
<div className='d-flex mt-2 justify-content-center'> <div className='d-flex mt-2 justify-content-center'>
<Moon className='spin fill-grey' /> <Moon className='spin fill-grey' />
<div className='ml-3 text-muted' style={{ fontWeight: '600' }}>{status}</div> <div className='ms-3 text-muted' style={{ fontWeight: '600' }}>{status}</div>
</div> </div>
) )
} }
@ -15,7 +15,7 @@ function InvoiceConfirmedStatus ({ status }) {
return ( return (
<div className='d-flex mt-2 justify-content-center'> <div className='d-flex mt-2 justify-content-center'>
<Check className='fill-success' /> <Check className='fill-success' />
<div className='ml-3 text-success' style={{ fontWeight: '600' }}>{status}</div> <div className='ms-3 text-success' style={{ fontWeight: '600' }}>{status}</div>
</div> </div>
) )
} }
@ -24,7 +24,7 @@ function InvoiceFailedStatus ({ status }) {
return ( return (
<div className='d-flex mt-2 justify-content-center'> <div className='d-flex mt-2 justify-content-center'>
<ThumbDown className='fill-danger' /> <ThumbDown className='fill-danger' />
<div className='ml-3 text-danger' style={{ fontWeight: '600' }}>{status}</div> <div className='ms-3 text-danger' style={{ fontWeight: '600' }}>{status}</div>
</div> </div>
) )
} }

View File

@ -1,4 +1,5 @@
import { Button, InputGroup } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import InputGroup from 'react-bootstrap/InputGroup'
import React, { useState, useRef, useEffect } from 'react' import React, { useState, useRef, useEffect } from 'react'
import { Form, Input, SubmitButton } from './form' import { Form, Input, SubmitButton } from './form'
import { useMe } from './me' import { useMe } from './me'
@ -12,12 +13,12 @@ const Tips = ({ setOValue }) => {
return tips.map(num => return tips.map(num =>
<Button <Button
size='sm' size='sm'
className={`${num > 1 ? 'ml-2' : ''} mb-2`} className={`${num > 1 ? 'ms-2' : ''} mb-2`}
key={num} key={num}
onClick={() => { setOValue(num) }} onClick={() => { setOValue(num) }}
> >
<UpBolt <UpBolt
className='mr-1' className='me-1'
width={14} width={14}
height={14} height={14}
/>{num} />{num}
@ -77,7 +78,7 @@ export default function ItemAct ({ onClose, itemId, act, strike }) {
<Tips setOValue={setOValue} /> <Tips setOValue={setOValue} />
</div> </div>
<div className='d-flex'> <div className='d-flex'>
<SubmitButton variant='success' className='ml-auto mt-1 px-4' value='TIP'>zap</SubmitButton> <SubmitButton variant='success' className='ms-auto mt-1 px-4' value='TIP'>zap</SubmitButton>
</div> </div>
</Form> </Form>
) )

View File

@ -8,7 +8,7 @@ import styles from '../styles/item.module.css'
import itemStyles from './item.module.css' import itemStyles from './item.module.css'
import { NOFOLLOW_LIMIT } from '../lib/constants' import { NOFOLLOW_LIMIT } from '../lib/constants'
import { useMe } from './me' import { useMe } from './me'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { TwitterTweetEmbed } from 'react-twitter-embed' import { TwitterTweetEmbed } from 'react-twitter-embed'
import YouTube from 'react-youtube' import YouTube from 'react-youtube'
import useDarkMode from './dark-mode' import useDarkMode from './dark-mode'
@ -134,7 +134,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
{item.url && <ItemEmbed item={item} />} {item.url && <ItemEmbed item={item} />}
{item.poll && <Poll item={item} />} {item.poll && <Poll item={item} />}
{item.bounty && {item.bounty &&
<div className='font-weight-bold mt-2'> <div className='fw-bold mt-2'>
{item.bountyPaidTo?.length {item.bountyPaidTo?.length
? ( ? (
<div className='px-3 py-1 d-inline-block bg-grey-medium rounded text-success'> <div className='px-3 py-1 d-inline-block bg-grey-medium rounded text-success'>

View File

@ -1,7 +1,8 @@
import Link from 'next/link' import Link from 'next/link'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Badge, Dropdown } from 'react-bootstrap' import Badge from 'react-bootstrap/Badge'
import Dropdown from 'react-bootstrap/Dropdown'
import Countdown from './countdown' import Countdown from './countdown'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
import { newComments } from '../lib/new-comments' import { newComments } from '../lib/new-comments'
@ -43,12 +44,12 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, class
</>} </>}
<Link href={`/items/${item.id}`} title={`${item.commentSats} sats`} className='text-reset'> <Link href={`/items/${item.id}`} title={`${item.commentSats} sats`} className='text-reset'>
{item.ncomments} {commentsText || 'comments'} {item.ncomments} {commentsText || 'comments'}
{hasNewComments && <>{' '}<Badge className={styles.newComment} variant={null}>new</Badge></>} {hasNewComments && <>{' '}<Badge className={styles.newComment} bg={null}>new</Badge></>}
</Link> </Link>
<span> \ </span> <span> \ </span>
<span> <span>
<Link href={`/${item.user.name}`} className='d-inline-flex align-items-center'> <Link href={`/${item.user.name}`}>
@{item.user.name}<CowboyHat className='ml-1 fill-grey' user={item.user} height={12} width={12} /> @{item.user.name}<CowboyHat className='ms-1 fill-grey' user={item.user} height={12} width={12} />
{embellishUser} {embellishUser}
</Link> </Link>
<span> </span> <span> </span>
@ -65,15 +66,15 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, class
</span> </span>
{item.subName && {item.subName &&
<Link href={`/~${item.subName}`}> <Link href={`/~${item.subName}`}>
{' '}<Badge className={styles.newComment} variant={null}>{item.subName}</Badge> {' '}<Badge className={styles.newComment} bg={null}>{item.subName}</Badge>
</Link>} </Link>}
{(item.outlawed && !item.mine && {(item.outlawed && !item.mine &&
<Link href='/recent/outlawed'> <Link href='/recent/outlawed'>
{' '}<Badge className={styles.newComment} variant={null}>outlawed</Badge> {' '}<Badge className={styles.newComment} bg={null}>outlawed</Badge>
</Link>) || </Link>) ||
(item.freebie && (item.freebie &&
<Link href='/recent/freebies'> <Link href='/recent/freebies'>
{' '}<Badge className={styles.newComment} variant={null}>freebie</Badge> {' '}<Badge className={styles.newComment} bg={null}>freebie</Badge>
</Link> </Link>
)} )}
{canEdit && !item.deletedAt && {canEdit && !item.deletedAt &&
@ -112,9 +113,9 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, class
export function ItemDropdown ({ children }) { export function ItemDropdown ({ children }) {
return ( return (
<Dropdown className='pointer' as='span'> <Dropdown className={`pointer ${styles.dropdown}`} as='span'>
<Dropdown.Toggle variant='success' id='dropdown-basic' as='a'> <Dropdown.Toggle variant='success' as='a'>
<MoreIcon className='fill-grey ml-1' height={16} width={16} /> <MoreIcon className='fill-grey ms-1' height={16} width={16} />
</Dropdown.Toggle> </Dropdown.Toggle>
<Dropdown.Menu> <Dropdown.Menu>
{children} {children}

View File

@ -1,6 +1,8 @@
import * as Yup from 'yup' import * as Yup from 'yup'
import Toc from './table-of-contents' import Toc from './table-of-contents'
import { Badge, Button, Image } from 'react-bootstrap' import Badge from 'react-bootstrap/Badge'
import Button from 'react-bootstrap/Button'
import Image from 'react-bootstrap/Image'
import { SearchTitle } from './item' import { SearchTitle } from './item'
import styles from './item.module.css' import styles from './item.module.css'
import Link from 'next/link' import Link from 'next/link'
@ -28,7 +30,7 @@ export default function ItemJob ({ item, toc, rank, children }) {
</Link> </Link>
<div className={`${styles.hunk} align-self-center mb-0`}> <div className={`${styles.hunk} align-self-center mb-0`}>
<div className={`${styles.main} flex-wrap d-inline`}> <div className={`${styles.main} flex-wrap d-inline`}>
<Link href={`/items/${item.id}`} className={`${styles.title} text-reset mr-2`}> <Link href={`/items/${item.id}`} className={`${styles.title} text-reset me-2`}>
{item.searchTitle {item.searchTitle
? <SearchTitle title={item.searchTitle} /> ? <SearchTitle title={item.searchTitle} />
: ( : (
@ -49,7 +51,7 @@ export default function ItemJob ({ item, toc, rank, children }) {
<span> \ </span> <span> \ </span>
<span> <span>
<Link href={`/${item.user.name}`} className='d-inline-flex align-items-center'> <Link href={`/${item.user.name}`} className='d-inline-flex align-items-center'>
@{item.user.name}<CowboyHat className='ml-1 fill-grey' user={item.user} height={12} width={12} /> @{item.user.name}<CowboyHat className='ms-1 fill-grey' user={item.user} height={12} width={12} />
</Link> </Link>
<span> </span> <span> </span>
<Link href={`/items/${item.id}`} title={item.createdAt} className='text-reset'> <Link href={`/items/${item.id}`} title={item.createdAt} className='text-reset'>
@ -64,9 +66,9 @@ export default function ItemJob ({ item, toc, rank, children }) {
<Link href={`/items/${item.id}/edit`} className='text-reset'> <Link href={`/items/${item.id}/edit`} className='text-reset'>
edit edit
</Link> </Link>
{item.status !== 'ACTIVE' && <span className='ml-1 font-weight-bold text-boost'> {item.status}</span>} {item.status !== 'ACTIVE' && <span className='ms-1 fw-bold text-boost'> {item.status}</span>}
</>)} </>)}
{item.maxBid > 0 && item.status === 'ACTIVE' && <Badge className={`${styles.newComment} ml-1`}>PROMOTED</Badge>} {item.maxBid > 0 && item.status === 'ACTIVE' && <Badge className={`${styles.newComment} ms-1`}>PROMOTED</Badge>}
</div> </div>
</div> </div>
{toc && {toc &&
@ -81,9 +83,9 @@ export default function ItemJob ({ item, toc, rank, children }) {
<Button <Button
target='_blank' href={isEmail ? `mailto:${item.url}?subject=${encodeURIComponent(item.title)} via Stacker News` : item.url} target='_blank' href={isEmail ? `mailto:${item.url}?subject=${encodeURIComponent(item.title)} via Stacker News` : item.url}
> >
apply {isEmail && <EmailIcon className='ml-1' />} apply {isEmail && <EmailIcon className='ms-1' />}
</Button> </Button>
{isEmail && <div className='ml-3 align-self-center text-muted font-weight-bold'>{item.url}</div>} {isEmail && <div className='ms-3 align-self-center text-muted fw-bold'>{item.url}</div>}
</div> </div>
{children} {children}
</div> </div>

View File

@ -39,22 +39,22 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
: item.meDontLike ? <Flag width={24} height={24} className={`${styles.dontLike}`} /> : <UpVote item={item} className={styles.upvote} pendingSats={pendingSats} setPendingSats={setPendingSats} />} : item.meDontLike ? <Flag width={24} height={24} className={`${styles.dontLike}`} /> : <UpVote item={item} className={styles.upvote} pendingSats={pendingSats} setPendingSats={setPendingSats} />}
<div className={styles.hunk}> <div className={styles.hunk}>
<div className={`${styles.main} flex-wrap`}> <div className={`${styles.main} flex-wrap`}>
<Link href={`/items/${item.id}`} ref={titleRef} className={`${styles.title} text-reset mr-2`}> <Link href={`/items/${item.id}`} ref={titleRef} className={`${styles.title} text-reset me-2`}>
{item.searchTitle ? <SearchTitle title={item.searchTitle} /> : item.title} {item.searchTitle ? <SearchTitle title={item.searchTitle} /> : item.title}
{item.pollCost && <span className={styles.icon}> <PollIcon className='fill-grey ml-1' height={14} width={14} /></span>} {item.pollCost && <span className={styles.icon}> <PollIcon className='fill-grey ms-1' height={14} width={14} /></span>}
{item.bounty > 0 && {item.bounty > 0 &&
<span className={styles.icon}> <span className={styles.icon}>
<ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaidTo?.length ? 'sats paid' : 'sats bounty'}`}> <ActionTooltip notForm overlayText={`${abbrNum(item.bounty)} ${item.bountyPaidTo?.length ? 'sats paid' : 'sats bounty'}`}>
<BountyIcon className={`${styles.bountyIcon} ${item.bountyPaidTo?.length ? 'fill-success' : 'fill-grey'}`} height={16} width={16} /> <BountyIcon className={`${styles.bountyIcon} ${item.bountyPaidTo?.length ? 'fill-success' : 'fill-grey'}`} height={16} width={16} />
</ActionTooltip> </ActionTooltip>
</span>} </span>}
{image && <span className={styles.icon}><ImageIcon className='fill-grey ml-2' height={16} width={16} /></span>} {image && <span className={styles.icon}><ImageIcon className='fill-grey ms-2' height={16} width={16} /></span>}
</Link> </Link>
{item.url && !image && {item.url && !image &&
<> <>
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
<a <a
className={`${styles.link} py-half py-md-0`} target='_blank' href={item.url} className={`${styles.link}`} target='_blank' href={item.url}
rel={item.sats + item.boost >= NOFOLLOW_LIMIT ? null : 'nofollow'} rel={item.sats + item.boost >= NOFOLLOW_LIMIT ? null : 'nofollow'}
> >
{item.url.replace(/(^https?:|^)\/\//, '')} {item.url.replace(/(^https?:|^)\/\//, '')}
@ -88,7 +88,7 @@ export function ItemSkeleton ({ rank, children }) {
<UpVote className={styles.upvote} /> <UpVote className={styles.upvote} />
<div className={styles.hunk}> <div className={styles.hunk}>
<div className={`${styles.main} flex-wrap flex-md-nowrap`}> <div className={`${styles.main} flex-wrap flex-md-nowrap`}>
<span className={`${styles.title} clouds text-reset flex-md-fill flex-md-shrink-0 mr-2`} /> <span className={`${styles.title} clouds text-reset flex-md-fill flex-md-shrink-0 me-2`} />
<span className={`${styles.link} clouds`} /> <span className={`${styles.link} clouds`} />
</div> </div>
<div className={styles.other}> <div className={styles.other}>

View File

@ -4,6 +4,7 @@
max-width: 100%; max-width: 100%;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
padding-bottom: .15rem;
} }
.icon { .icon {
@ -24,12 +25,15 @@ a.title:visited {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
flex: 1 0 128px; flex: 1 0 128px;
padding-bottom: .15rem;
} }
.newComment { .newComment {
color: var(--theme-grey) !important; color: var(--theme-grey) !important;
background: var(--theme-clickToContextColor) !important; background: var(--theme-clickToContextColor) !important;
vertical-align: middle; vertical-align: middle;
margin-top: -1px;
margin-left: 0.1rem;
} }
.pin { .pin {
@ -64,6 +68,16 @@ a.link:visited {
.other { .other {
font-size: 80%; font-size: 80%;
color: var(--theme-grey); color: var(--theme-grey);
vertical-align: text-top;
margin-bottom: .125rem;
}
.other svg {
vertical-align: text-top;
}
.other .dropdown svg {
margin-top: -1px;
} }
.item { .item {

View File

@ -1,5 +1,10 @@
import { Checkbox, Form, Input, MarkdownInput, SubmitButton } from './form' import { Checkbox, Form, Input, MarkdownInput, SubmitButton } from './form'
import { InputGroup, Form as BForm, Col, Image } from 'react-bootstrap' import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import InputGroup from 'react-bootstrap/InputGroup'
import Image from 'react-bootstrap/Image'
import BootstrapForm from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Info from './info' import Info from './info'
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
@ -9,8 +14,6 @@ import { useRouter } from 'next/router'
import Link from 'next/link' import Link from 'next/link'
import { usePrice } from './price' import { usePrice } from './price'
import Avatar from './avatar' import Avatar from './avatar'
import BootstrapForm from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert'
import ActionTooltip from './action-tooltip' import ActionTooltip from './action-tooltip'
import { jobSchema } from '../lib/validate' import { jobSchema } from '../lib/validate'
import CancelButton from './cancel-button' import CancelButton from './cancel-button'
@ -115,7 +118,7 @@ export default function JobForm ({ item, sub }) {
required required
clear clear
/> />
<BForm.Row className='mr-0'> <Row className='me-0'>
<Col> <Col>
<Input <Input
label='location' label='location'
@ -124,10 +127,10 @@ export default function JobForm ({ item, sub }) {
/> />
</Col> </Col>
<Checkbox <Checkbox
label={<div className='font-weight-bold'>remote</div>} name='remote' hiddenLabel label={<div className='fw-bold'>remote</div>} name='remote' hiddenLabel
groupClassName={styles.inlineCheckGroup} groupClassName={styles.inlineCheckGroup}
/> />
</BForm.Row> </Row>
<MarkdownInput <MarkdownInput
topLevel topLevel
label='description' label='description'
@ -136,7 +139,7 @@ export default function JobForm ({ item, sub }) {
required required
/> />
<Input <Input
label={<>how to apply <small className='text-muted ml-2'>url or email address</small></>} label={<>how to apply <small className='text-muted ms-2'>url or email address</small></>}
name='url' name='url'
required required
clear clear
@ -187,14 +190,14 @@ function PromoteJob ({ item, sub, storageKeyPrefix }) {
label={ label={
<div className='d-flex align-items-center'>bid <div className='d-flex align-items-center'>bid
<Info> <Info>
<ol className='font-weight-bold'> <ol className='fw-bold'>
<li>The higher your bid the higher your job will rank</li> <li>The higher your bid the higher your job will rank</li>
<li>You can increase, decrease, or remove your bid at anytime</li> <li>You can increase, decrease, or remove your bid at anytime</li>
<li>You can edit or stop your job at anytime</li> <li>You can edit or stop your job at anytime</li>
<li>If you run out of sats, your job will stop being promoted until you fill your wallet again</li> <li>If you run out of sats, your job will stop being promoted until you fill your wallet again</li>
</ol> </ol>
</Info> </Info>
<small className='text-muted ml-2'>optional</small> <small className='text-muted ms-2'>optional</small>
</div> </div>
} }
name='maxBid' name='maxBid'
@ -210,7 +213,7 @@ function PromoteJob ({ item, sub, storageKeyPrefix }) {
hint={<PriceHint monthly={monthly} />} hint={<PriceHint monthly={monthly} />}
storageKeyPrefix={storageKeyPrefix} storageKeyPrefix={storageKeyPrefix}
/> />
<><div className='font-weight-bold text-muted'>This bid puts your job in position: {position}</div></> <><div className='fw-bold text-muted'>This bid puts your job in position: {position}</div></>
</> </>
} }
/> />
@ -227,10 +230,10 @@ function StatusControl ({ item }) {
<AccordianItem <AccordianItem
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to stop my job</div>} header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to stop my job</div>}
headerColor='var(--danger)' headerColor='var(--bs-danger)'
body={ body={
<Checkbox <Checkbox
label={<div className='font-weight-bold text-danger'>stop my job</div>} name='stop' inline label={<div className='fw-bold text-danger'>stop my job</div>} name='stop' inline
/> />
} }
/> />
@ -242,10 +245,10 @@ function StatusControl ({ item }) {
return ( return (
<AccordianItem <AccordianItem
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to resume my job</div>} header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>I want to resume my job</div>}
headerColor='var(--success)' headerColor='var(--bs-success)'
body={ body={
<Checkbox <Checkbox
label={<div className='font-weight-bold text-success'>resume my job</div>} name='start' inline label={<div className='fw-bold text-success'>resume my job</div>} name='start' inline
/> />
} }
/> />

View File

@ -1,7 +1,9 @@
import { gql, useMutation, useQuery } from '@apollo/client' import { gql, useMutation, useQuery } from '@apollo/client'
import { signIn } from 'next-auth/client' import { signIn } from 'next-auth/client'
import { useEffect } from 'react' import { useEffect } from 'react'
import { Col, Container, Row } from 'react-bootstrap' import Col from 'react-bootstrap/Col'
import Container from 'react-bootstrap/Container'
import Row from 'react-bootstrap/Row'
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
import Qr, { QrSkeleton } from './qr' import Qr, { QrSkeleton } from './qr'
import styles from './lightning-auth.module.css' import styles from './lightning-auth.module.css'
@ -37,9 +39,9 @@ function LightningExplainer ({ text, children }) {
<h3 className='w-100 pb-2'> <h3 className='w-100 pb-2'>
{text || 'Login'} with Lightning {text || 'Login'} with Lightning
</h3> </h3>
<div className='font-weight-bold text-muted pb-4'>This is the most private way to use Stacker News. Just open your Lightning wallet and scan the QR code.</div> <div className='fw-bold text-muted pb-4'>This is the most private way to use Stacker News. Just open your Lightning wallet and scan the QR code.</div>
<Row className='w-100 text-muted'> <Row className='w-100 text-muted'>
<Col className='pl-0 mb-4' md> <Col className='ps-0 mb-4' md>
<AccordianItem <AccordianItem
header={`Which wallets can I use to ${(text || 'Login').toLowerCase()}?`} header={`Which wallets can I use to ${(text || 'Login').toLowerCase()}?`}
body={ body={

View File

@ -9,7 +9,7 @@ import Item from './item'
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
import FeeButton, { EditFeeButton } from './fee-button' import FeeButton, { EditFeeButton } from './fee-button'
import Delete from './delete' import Delete from './delete'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { linkSchema } from '../lib/validate' import { linkSchema } from '../lib/validate'
import Moon from '../svgs/moon-fill.svg' import Moon from '../svgs/moon-fill.svg'
import { SubSelectInitial } from './sub-select-form' import { SubSelectInitial } from './sub-select-form'
@ -143,7 +143,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
autoComplete='off' autoComplete='off'
overrideValue={data?.pageTitleAndUnshorted?.unshorted} overrideValue={data?.pageTitleAndUnshorted?.unshorted}
hint={editThreshold hint={editThreshold
? <div className='text-muted font-weight-bold'><Countdown date={editThreshold} /></div> ? <div className='text-muted fw-bold'><Countdown date={editThreshold} /></div>
: null} : null}
onChange={async (formik, e) => { onChange={async (formik, e) => {
if ((/^ *$/).test(formik?.values.title)) { if ((/^ *$/).test(formik?.values.title)) {
@ -191,9 +191,9 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
ChildButton={SubmitButton} variant='secondary' ChildButton={SubmitButton} variant='secondary'
/> />
{dupesLoading && {dupesLoading &&
<div className='d-flex ml-3 justify-content-center'> <div className='d-flex ms-3 justify-content-center'>
<Moon className='spin fill-grey' /> <Moon className='spin fill-grey' />
<div className='ml-2 text-muted' style={{ fontWeight: '600' }}>searching for dupes</div> <div className='ms-2 text-muted' style={{ fontWeight: '600' }}>searching for dupes</div>
</div>} </div>}
</div> </div>
)} )}

View File

@ -2,7 +2,7 @@ import GithubIcon from '../svgs/github-fill.svg'
import TwitterIcon from '../svgs/twitter-fill.svg' import TwitterIcon from '../svgs/twitter-fill.svg'
import LightningIcon from '../svgs/bolt.svg' import LightningIcon from '../svgs/bolt.svg'
import SlashtagsIcon from '../svgs/slashtags.svg' import SlashtagsIcon from '../svgs/slashtags.svg'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
export default function LoginButton ({ text, type, className, onClick }) { export default function LoginButton ({ text, type, className, onClick }) {
let Icon, variant let Icon, variant
@ -32,7 +32,7 @@ export default function LoginButton ({ text, type, className, onClick }) {
<Button className={className} variant={variant} onClick={onClick}> <Button className={className} variant={variant} onClick={onClick}>
<Icon <Icon
width={20} width={20}
height={20} className='mr-3' height={20} className='me-3'
/> />
{text} {name} {text} {name}
</Button> </Button>

View File

@ -71,7 +71,7 @@ export default function Login ({ providers, callbackUrl, error, text, Header, Fo
case 'Email': case 'Email':
return ( return (
<div className='w-100' key={provider.name}> <div className='w-100' key={provider.name}>
<div className='mt-2 text-center text-muted font-weight-bold'>or</div> <div className='mt-2 text-center text-muted fw-bold'>or</div>
<EmailLoginForm text={text} callbackUrl={callbackUrl} /> <EmailLoginForm text={text} callbackUrl={callbackUrl} />
</div> </div>
) )

View File

@ -1,5 +1,5 @@
import { createContext, useCallback, useContext, useMemo, useState } from 'react' import { createContext, useCallback, useContext, useMemo, useState } from 'react'
import { Modal } from 'react-bootstrap' import Modal from 'react-bootstrap/Modal'
export const ShowModalContext = createContext(() => null) export const ShowModalContext = createContext(() => null)

View File

@ -1,4 +1,4 @@
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { useState } from 'react' import { useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
@ -40,7 +40,7 @@ export function NavigateFooter ({ cursor, count, fetchMore, href, text, noMoreTe
let Footer let Footer
if (cursor) { if (cursor) {
Footer = () => ( Footer = () => (
<Link href={href} className='text-reset text-muted font-weight-bold'>{text}</Link> <Link href={href} className='text-reset text-muted fw-bold'>{text}</Link>
) )
} else { } else {
Footer = () => ( Footer = () => (

View File

@ -15,7 +15,7 @@ import { COMMENT_DEPTH_LIMIT } from '../lib/constants'
import CowboyHatIcon from '../svgs/cowboy.svg' import CowboyHatIcon from '../svgs/cowboy.svg'
import BaldIcon from '../svgs/bald.svg' import BaldIcon from '../svgs/bald.svg'
import { RootProvider } from './root' import { RootProvider } from './root'
import { Alert } from 'react-bootstrap' import Alert from 'react-bootstrap/Alert'
import styles from './notifications.module.css' import styles from './notifications.module.css'
import { useServiceWorker } from './serviceworker' import { useServiceWorker } from './serviceworker'
import { Checkbox, Form } from './form' import { Checkbox, Form } from './form'
@ -110,9 +110,9 @@ function Streak ({ n }) {
} }
return ( return (
<div className='d-flex font-weight-bold ml-2 py-1'> <div className='d-flex fw-bold ms-2 py-1'>
<div style={{ fontSize: '2rem' }}>{n.days ? <BaldIcon className='fill-grey' height={40} width={40} /> : <CowboyHatIcon className='fill-grey' height={40} width={40} />}</div> <div style={{ fontSize: '2rem' }}>{n.days ? <BaldIcon className='fill-grey' height={40} width={40} /> : <CowboyHatIcon className='fill-grey' height={40} width={40} />}</div>
<div className='ml-1 p-1'> <div className='ms-1 p-1'>
you {n.days ? 'lost your' : 'found a'} cowboy hat you {n.days ? 'lost your' : 'found a'} cowboy hat
<div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div> <div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div>
</div> </div>
@ -122,11 +122,11 @@ function Streak ({ n }) {
function EarnNotification ({ n }) { function EarnNotification ({ n }) {
return ( return (
<div className='d-flex ml-2 py-1'> <div className='d-flex ms-2 py-1'>
<HandCoin className='align-self-center fill-boost mx-1' width={24} height={24} style={{ flex: '0 0 24px', transform: 'rotateY(180deg)' }} /> <HandCoin className='align-self-center fill-boost mx-1' width={24} height={24} style={{ flex: '0 0 24px', transform: 'rotateY(180deg)' }} />
<div className='ml-2'> <div className='ms-2'>
<div className='font-weight-bold text-boost'> <div className='fw-bold text-boost'>
you stacked {n.earnedSats} sats in rewards<small className='text-muted ml-1'>{timeSince(new Date(n.sortTime))}</small> you stacked {n.earnedSats} sats in rewards<small className='text-muted ms-1'>{timeSince(new Date(n.sortTime))}</small>
</div> </div>
{n.sources && {n.sources &&
<div style={{ fontSize: '80%', color: 'var(--theme-grey)' }}> <div style={{ fontSize: '80%', color: 'var(--theme-grey)' }}>
@ -146,10 +146,10 @@ function EarnNotification ({ n }) {
function Invitification ({ n }) { function Invitification ({ n }) {
return ( return (
<NotificationLayout href='/invites'> <NotificationLayout href='/invites'>
<small className='font-weight-bold text-secondary ml-2'> <small className='fw-bold text-secondary ms-2'>
your invite has been redeemed by {n.invite.invitees.length} stackers your invite has been redeemed by {n.invite.invitees.length} stackers
</small> </small>
<div className='ml-4 mr-2 mt-1'> <div className='ms-4 me-2 mt-1'>
<Invite <Invite
invite={n.invite} active={ invite={n.invite} active={
!n.invite.revoked && !n.invite.revoked &&
@ -164,9 +164,9 @@ function Invitification ({ n }) {
function InvoicePaid ({ n }) { function InvoicePaid ({ n }) {
return ( return (
<NotificationLayout href={`/invoices/${n.invoice.id}`}> <NotificationLayout href={`/invoices/${n.invoice.id}`}>
<div className='font-weight-bold text-info ml-2 py-1'> <div className='fw-bold text-info ms-2 py-1'>
<Check className='fill-info mr-1' />{n.earnedSats} sats were deposited in your account <Check className='fill-info me-1' />{n.earnedSats} sats were deposited in your account
<small className='text-muted ml-1'>{timeSince(new Date(n.sortTime))}</small> <small className='text-muted ms-1'>{timeSince(new Date(n.sortTime))}</small>
</div> </div>
</NotificationLayout> </NotificationLayout>
) )
@ -175,9 +175,9 @@ function InvoicePaid ({ n }) {
function Referral ({ n }) { function Referral ({ n }) {
return ( return (
<NotificationLayout> <NotificationLayout>
<small className='font-weight-bold text-secondary ml-2'> <small className='fw-bold text-secondary ms-2'>
someone joined via one of your <Link href='/referrals/month' className='text-reset'>referral links</Link> someone joined via one of your <Link href='/referrals/month' className='text-reset'>referral links</Link>
<small className='text-muted ml-1'>{timeSince(new Date(n.sortTime))}</small> <small className='text-muted ms-1'>{timeSince(new Date(n.sortTime))}</small>
</small> </small>
</NotificationLayout> </NotificationLayout>
) )
@ -186,7 +186,7 @@ function Referral ({ n }) {
function Votification ({ n }) { function Votification ({ n }) {
return ( return (
<NotificationLayout {...defaultOnClick(n)}> <NotificationLayout {...defaultOnClick(n)}>
<small className='font-weight-bold text-success ml-2'> <small className='fw-bold text-success ms-2'>
your {n.item.title ? 'post' : 'reply'} {n.item.fwdUser ? 'forwarded' : 'stacked'} {n.earnedSats} sats{n.item.fwdUser && ` to @${n.item.fwdUser.name}`} your {n.item.title ? 'post' : 'reply'} {n.item.fwdUser ? 'forwarded' : 'stacked'} {n.earnedSats} sats{n.item.fwdUser && ` to @${n.item.fwdUser.name}`}
</small> </small>
<div> <div>
@ -207,7 +207,7 @@ function Votification ({ n }) {
function Mention ({ n }) { function Mention ({ n }) {
return ( return (
<NotificationLayout {...defaultOnClick(n)}> <NotificationLayout {...defaultOnClick(n)}>
<small className='font-weight-bold text-info ml-2'> <small className='fw-bold text-info ms-2'>
you were mentioned in you were mentioned in
</small> </small>
<div> <div>
@ -227,7 +227,7 @@ function Mention ({ n }) {
function JobChanged ({ n }) { function JobChanged ({ n }) {
return ( return (
<NotificationLayout {...defaultOnClick(n)}> <NotificationLayout {...defaultOnClick(n)}>
<small className={`font-weight-bold text-${n.item.status === 'ACTIVE' ? 'success' : 'boost'} ml-1`}> <small className={`fw-bold text-${n.item.status === 'ACTIVE' ? 'success' : 'boost'} ms-1`}>
{n.item.status === 'ACTIVE' {n.item.status === 'ACTIVE'
? 'your job is active again' ? 'your job is active again'
: (n.item.status === 'NOSATS' : (n.item.status === 'NOSATS'
@ -306,7 +306,7 @@ function NotificationAlert () {
<Form className={`d-flex justify-content-end ${supported ? 'visible' : 'invisible'}`} initial={{ pushNotify: hasSubscription }}> <Form className={`d-flex justify-content-end ${supported ? 'visible' : 'invisible'}`} initial={{ pushNotify: hasSubscription }}>
<Checkbox <Checkbox
name='pushNotify' label={<span className='text-muted'>push notifications</span>} name='pushNotify' label={<span className='text-muted'>push notifications</span>}
groupClassName={`${styles.subFormGroup} mb-1 mr-sm-3 mr-0`} groupClassName={`${styles.subFormGroup} mb-1 me-sm-3 me-0`}
inline checked={hasSubscription} handleChange={async () => { inline checked={hasSubscription} handleChange={async () => {
await sw.togglePushSubscription().catch(setError) await sw.togglePushSubscription().catch(setError)
}} }}

View File

@ -14,7 +14,7 @@ export default function PastBounties ({ item }) {
return ( return (
<AccordianItem <AccordianItem
header={<div className='font-weight-bold'>{item.user.name}'s bounties</div>} header={<div className='fw-bold'>{item.user.name}'s bounties</div>}
body={ body={
<Items <Items
variables={variables} variables={variables}

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import styles from './pay-bounty.module.css' import styles from './pay-bounty.module.css'
import ActionTooltip from './action-tooltip' import ActionTooltip from './action-tooltip'
import { useMutation, gql } from '@apollo/client' import { useMutation, gql } from '@apollo/client'
@ -96,7 +96,7 @@ export default function PayBounty ({ children, item }) {
className={styles.pay} onClick={() => { className={styles.pay} onClick={() => {
showModal(onClose => ( showModal(onClose => (
<> <>
<div className='text-center font-weight-bold text-muted'> <div className='text-center fw-bold text-muted'>
Pay this bounty to {item.user.name}? Pay this bounty to {item.user.name}?
</div> </div>
<div className='text-center'> <div className='text-center'>

View File

@ -1,4 +1,4 @@
.pay { .pay {
color: var(--success); color: var(--bs-success);
margin-left: 1rem; margin-left: 1rem;
} }

View File

@ -6,7 +6,7 @@ import AdvPostForm, { AdvPostInitial } from './adv-post-form'
import { MAX_POLL_NUM_CHOICES } from '../lib/constants' import { MAX_POLL_NUM_CHOICES } from '../lib/constants'
import FeeButton, { EditFeeButton } from './fee-button' import FeeButton, { EditFeeButton } from './fee-button'
import Delete from './delete' import Delete from './delete'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { pollSchema } from '../lib/validate' import { pollSchema } from '../lib/validate'
import { SubSelectInitial } from './sub-select-form' import { SubSelectInitial } from './sub-select-form'
import CancelButton from './cancel-button' import CancelButton from './cancel-button'
@ -71,7 +71,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
/> />
<MarkdownInput <MarkdownInput
topLevel topLevel
label={<>text <small className='text-muted ml-2'>optional</small></>} label={<>text <small className='text-muted ms-2'>optional</small></>}
name='text' name='text'
minRows={2} minRows={2}
/> />
@ -82,7 +82,7 @@ export function PollForm ({ item, sub, editThreshold, children }) {
max={MAX_POLL_NUM_CHOICES} max={MAX_POLL_NUM_CHOICES}
min={2} min={2}
hint={editThreshold hint={editThreshold
? <div className='text-muted font-weight-bold'><Countdown date={editThreshold} /></div> ? <div className='text-muted fw-bold'><Countdown date={editThreshold} /></div>
: null} : null}
/> />
<AdvPostForm edit={!!item} /> <AdvPostForm edit={!!item} />

View File

@ -1,5 +1,5 @@
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { fixedDecimal } from '../lib/format' import { fixedDecimal } from '../lib/format'
import { timeLeft } from '../lib/time' import { timeLeft } from '../lib/time'
import { useMe } from './me' import { useMe } from './me'
@ -94,8 +94,8 @@ export default function Poll ({ item }) {
function PollResult ({ v, progress }) { function PollResult ({ v, progress }) {
return ( return (
<div className={styles.pollResult}> <div className={styles.pollResult}>
<span className={styles.pollOption}>{v.option}{v.meVoted && <Check className='fill-grey ml-1 align-self-center' width={18} height={18} />}</span> <span className={styles.pollOption}>{v.option}{v.meVoted && <Check className='fill-grey ms-1 align-self-center' width={18} height={18} />}</span>
<span className='ml-auto mr-2 align-self-center'>{progress}%</span> <span className='ms-auto me-2 align-self-center'>{progress}%</span>
<div className={styles.pollProgress} style={{ width: `${progress}%` }} /> <div className={styles.pollProgress} style={{ width: `${progress}%` }} />
</div> </div>
) )

View File

@ -1,7 +1,7 @@
.pollButton { .pollButton {
margin-top: .25rem; margin-top: .25rem;
display: block; display: block;
border: 2px solid var(--info); border: 2px solid var(--bs-info);
border-radius: 2rem; border-radius: 2rem;
width: 100%; width: 100%;
max-width: 600px; max-width: 600px;

View File

@ -1,6 +1,6 @@
import JobForm from './job-form' import JobForm from './job-form'
import Link from 'next/link' import Link from 'next/link'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import AccordianItem from './accordian-item' import AccordianItem from './accordian-item'
import { useMe } from './me' import { useMe } from './me'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
@ -16,7 +16,7 @@ function FreebieDialog () {
<div className='text-center mb-4 text-muted'> <div className='text-center mb-4 text-muted'>
you have no sats, so this one is on us you have no sats, so this one is on us
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>Free posts have limited visibility and are hidden on the recent tab until other stackers zap them.</li> <li>Free posts have limited visibility and are hidden on the recent tab until other stackers zap them.</li>
<li>Free posts will not cover posts that cost more than 1 sat.</li> <li>Free posts will not cover posts that cost more than 1 sat.</li>
<li>To get fully visibile and unrestricted posts right away, fund your account with a few sats or earn some on Stacker News.</li> <li>To get fully visibile and unrestricted posts right away, fund your account with a few sats or earn some on Stacker News.</li>
@ -39,20 +39,20 @@ export function PostForm ({ type, sub, children }) {
<Link href={prefix + '/post?type=link'}> <Link href={prefix + '/post?type=link'}>
<Button variant='secondary'>link</Button> <Button variant='secondary'>link</Button>
</Link> </Link>
<span className='mx-3 font-weight-bold text-muted'>or</span> <span className='mx-3 fw-bold text-muted'>or</span>
<Link href={prefix + '/post?type=discussion'}> <Link href={prefix + '/post?type=discussion'}>
<Button variant='secondary'>discussion</Button> <Button variant='secondary'>discussion</Button>
</Link> </Link>
<div className='d-flex mt-4'> <div className='d-flex mt-4'>
<AccordianItem <AccordianItem
headerColor='#6c757d' headerColor='#6c757d'
header={<div className='font-weight-bold text-muted'>more types</div>} header={<div className='fw-bold text-muted'>more types</div>}
body={ body={
<div className='align-items-center'> <div className='align-items-center'>
<Link href={prefix + '/post?type=poll'}> <Link href={prefix + '/post?type=poll'}>
<Button variant='info'>poll</Button> <Button variant='info'>poll</Button>
</Link> </Link>
<span className='mx-3 font-weight-bold text-muted'>or</span> <span className='mx-3 fw-bold text-muted'>or</span>
<Link href={prefix + '/post?type=bounty'}> <Link href={prefix + '/post?type=bounty'}>
<Button variant='info'>bounty</Button> <Button variant='info'>bounty</Button>
</Link> </Link>

View File

@ -14,9 +14,9 @@ export default function RecentHeader ({ type, sub }) {
type: router.query.type || type || 'posts' type: router.query.type || type || 'posts'
}} }}
> >
<div className='text-muted font-weight-bold mt-0 mb-3 d-flex justify-content-end align-items-center'> <div className='text-muted fw-bold mt-0 mb-3 d-flex justify-content-end align-items-center'>
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
className='w-auto' className='w-auto'
name='type' name='type'
size='sm' size='sm'

View File

@ -10,7 +10,7 @@ export default function Related ({ title, itemId }) {
return ( return (
<AccordianItem <AccordianItem
header={<div className='font-weight-bold'>related</div>} header={<div className='fw-bold'>related</div>}
body={ body={
<Items <Items
query={RELATED_ITEMS} query={RELATED_ITEMS}

View File

@ -23,7 +23,7 @@ function FreebieDialog () {
<div className='text-muted'> <div className='text-muted'>
you have no sats, so this one is on us you have no sats, so this one is on us
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>Free comments have limited visibility and are listed at the bottom of the comment section until other stackers zap them.</li> <li>Free comments have limited visibility and are listed at the bottom of the comment section until other stackers zap them.</li>
<li>Free comments will not cover comments that cost more than 1 sat.</li> <li>Free comments will not cover comments that cost more than 1 sat.</li>
<li>To get fully visibile and unrestricted comments right away, fund your account with a few sats or earn some on Stacker News.</li> <li>To get fully visibile and unrestricted comments right away, fund your account with a few sats or earn some on Stacker News.</li>

View File

@ -11,7 +11,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
padding-bottom: .5rem; padding: .25rem 0 .5rem 0;
line-height: 1rem; line-height: 1rem;
vertical-align: middle; vertical-align: middle;
} }

View File

@ -1,4 +1,5 @@
import { Button, Container } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import Container from 'react-bootstrap/Container'
import styles from './search.module.css' import styles from './search.module.css'
import SearchIcon from '../svgs/search-line.svg' import SearchIcon from '../svgs/search-line.svg'
import CloseIcon from '../svgs/close-line.svg' import CloseIcon from '../svgs/close-line.svg'
@ -57,7 +58,7 @@ export default function Search ({ sub }) {
return ( return (
<> <>
<div className={`${styles.searchSection} ${showSearch ? styles.solid : styles.hidden}`}> <div className={`${styles.searchSection} ${showSearch ? styles.solid : styles.hidden}`}>
<Container className={`px-sm-0 ${styles.searchContainer} ${filter ? styles.leaveRoom : ''}`}> <Container className={`px-md-0 ${styles.searchContainer} ${filter ? styles.leaveRoom : ''}`}>
{showSearch {showSearch
? ( ? (
<Form <Form
@ -71,9 +72,9 @@ export default function Search ({ sub }) {
onSubmit={search} onSubmit={search}
> >
{filter && {filter &&
<div className='text-muted font-weight-bold my-3 d-flex align-items-center'> <div className='text-muted fw-bold my-3 d-flex align-items-center'>
<Select <Select
groupClassName='mr-2 mb-0' groupClassName='me-2 mb-0'
onChange={(formik, e) => search({ ...formik?.values, what: e.target.value })} onChange={(formik, e) => search({ ...formik?.values, what: e.target.value })}
name='what' name='what'
size='sm' size='sm'
@ -91,7 +92,7 @@ export default function Search ({ sub }) {
/> />
for for
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
onChange={(formik, e) => search({ ...formik?.values, when: e.target.value })} onChange={(formik, e) => search({ ...formik?.values, when: e.target.value })}
name='when' name='when'
size='sm' size='sm'
@ -105,7 +106,7 @@ export default function Search ({ sub }) {
name='q' name='q'
required required
autoFocus autoFocus
groupClassName='mr-3 mb-0 flex-grow-1' groupClassName='me-3 mb-0 flex-grow-1'
className='flex-grow-1' className='flex-grow-1'
clear clear
onChange={async (formik, e) => { onChange={async (formik, e) => {

View File

@ -1,4 +1,4 @@
import { Dropdown } from 'react-bootstrap' import Dropdown from 'react-bootstrap/Dropdown'
import ShareIcon from '../svgs/share-fill.svg' import ShareIcon from '../svgs/share-fill.svg'
import copy from 'clipboard-copy' import copy from 'clipboard-copy'
import { useMe } from './me' import { useMe } from './me'
@ -9,7 +9,7 @@ export default function Share ({ item }) {
return typeof window !== 'undefined' && navigator?.share return typeof window !== 'undefined' && navigator?.share
? ( ? (
<div className='ml-auto pointer d-flex align-items-center'> <div className='ms-auto pointer d-flex align-items-center'>
<ShareIcon <ShareIcon
width={20} height={20} width={20} height={20}
className='mx-2 fill-grey theme' className='mx-2 fill-grey theme'
@ -28,7 +28,7 @@ export default function Share ({ item }) {
/> />
</div>) </div>)
: ( : (
<Dropdown alignRight className='ml-auto pointer d-flex align-items-center' as='span'> <Dropdown align='end' className='ms-auto pointer d-flex align-items-center' as='span'>
<Dropdown.Toggle variant='success' id='dropdown-basic' as='a'> <Dropdown.Toggle variant='success' id='dropdown-basic' as='a'>
<ShareIcon width={20} height={20} className='mx-2 fill-grey theme' /> <ShareIcon width={20} height={20} className='mx-2 fill-grey theme' />
</Dropdown.Toggle> </Dropdown.Toggle>

View File

@ -1,4 +1,4 @@
import { Alert } from 'react-bootstrap' import Alert from 'react-bootstrap/Alert'
import YouTube from '../svgs/youtube-line.svg' import YouTube from '../svgs/youtube-line.svg'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { gql, useQuery } from '@apollo/client' import { gql, useQuery } from '@apollo/client'
@ -24,7 +24,7 @@ export default function Snl ({ ignorePreference }) {
return ( return (
<div className='d-flex'> <div className='d-flex'>
<Alert <Alert
variant='info' className='font-weight-bold mb-3 d-flex py-2 flex-shrink align-items-center' variant='info' className='fw-bold mb-3 d-flex py-2 flex-shrink align-items-center'
onClose={() => { onClose={() => {
setShow(undefined) setShow(undefined)
localStorage.setItem('snl', new Date()) localStorage.setItem('snl', new Date())
@ -32,7 +32,7 @@ export default function Snl ({ ignorePreference }) {
dismissible dismissible
> >
<a href='https://www.youtube.com/@stackernews/live'> <a href='https://www.youtube.com/@stackernews/live'>
<YouTube width={24} height={24} className='mr-2 fill-info' />Stacker News Live is streaming this week's top stories <YouTube width={24} height={24} className='me-2 fill-info' />Stacker News Live is streaming this week's top stories
</a> </a>
</Alert> </Alert>
</div> </div>

View File

@ -18,7 +18,7 @@ export default function SubSelect ({ label, sub, setSub, item, ...props }) {
const SubInfo = () => ( const SubInfo = () => (
<Info> <Info>
<div> <div>
<div className='font-weight-bold'>The sub your post will go in ...</div> <div className='fw-bold'>The sub your post will go in ...</div>
<ul> <ul>
<li>If it's bitcoin related, put it in the bitcoin sub.</li> <li>If it's bitcoin related, put it in the bitcoin sub.</li>
<li>If it's nostr related, put it in the nostr sub.</li> <li>If it's nostr related, put it in the nostr sub.</li>

View File

@ -1,6 +1,6 @@
import { useMutation } from '@apollo/client' import { useMutation } from '@apollo/client'
import { gql } from 'graphql-tag' import { gql } from 'graphql-tag'
import { Dropdown } from 'react-bootstrap' import Dropdown from 'react-bootstrap/Dropdown'
export default function SubscribeDropdownItem ({ item: { id, meSubscription } }) { export default function SubscribeDropdownItem ({ item: { id, meSubscription } }) {
const [subscribeItem] = useMutation( const [subscribeItem] = useMutation(

View File

@ -1,5 +1,6 @@
import React, { useMemo, useState } from 'react' import React, { useMemo, useState } from 'react'
import { Dropdown, FormControl } from 'react-bootstrap' import Dropdown from 'react-bootstrap/Dropdown'
import FormControl from 'react-bootstrap/FormControl'
import TocIcon from '../svgs/list-unordered.svg' import TocIcon from '../svgs/list-unordered.svg'
import { fromMarkdown } from 'mdast-util-from-markdown' import { fromMarkdown } from 'mdast-util-from-markdown'
import { visit } from 'unist-util-visit' import { visit } from 'unist-util-visit'
@ -27,7 +28,7 @@ export default function Toc ({ text }) {
} }
return ( return (
<Dropdown alignRight className='d-flex align-items-center'> <Dropdown align='end' className='d-flex align-items-center'>
<Dropdown.Toggle as={CustomToggle} id='dropdown-custom-components'> <Dropdown.Toggle as={CustomToggle} id='dropdown-custom-components'>
<TocIcon width={20} height={20} className='mx-2 fill-grey theme' /> <TocIcon width={20} height={20} className='mx-2 fill-grey theme' />
</Dropdown.Toggle> </Dropdown.Toggle>
@ -36,7 +37,7 @@ export default function Toc ({ text }) {
{toc.map(v => { {toc.map(v => {
return ( return (
<Dropdown.Item <Dropdown.Item
className={v.depth === 1 ? 'font-weight-bold' : ''} className={v.depth === 1 ? 'fw-bold' : ''}
style={{ style={{
marginLeft: `${(v.depth - 1) * 5}px` marginLeft: `${(v.depth - 1) * 5}px`
}} }}

View File

@ -201,8 +201,8 @@ export function ZoomableImage ({ src, topLevel, ...props }) {
if (!src) return null if (!src) return null
if (err) { if (err) {
return ( return (
<span className='d-flex align-items-center text-warning font-weight-bold pb-1'> <span className='d-flex align-items-center text-warning fw-bold pb-1'>
<FileMissing width={18} height={18} className='fill-warning mr-1' /> <FileMissing width={18} height={18} className='fill-warning me-1' />
broken image <small>stacker probably used an unreliable host</small> broken image <small>stacker probably used an unreliable host</small>
</span> </span>
) )

View File

@ -34,7 +34,7 @@ export default function TopHeader ({ sub, cat }) {
return ( return (
<div className='d-flex'> <div className='d-flex'>
<Form <Form
className='mr-auto' className='me-auto'
initial={{ initial={{
what: cat, what: cat,
by: router.query.by || '', by: router.query.by || '',
@ -42,7 +42,7 @@ export default function TopHeader ({ sub, cat }) {
}} }}
onSubmit={top} onSubmit={top}
> >
<div className='text-muted font-weight-bold my-3 d-flex align-items-center'> <div className='text-muted fw-bold my-3 d-flex align-items-center'>
top top
<Select <Select
groupClassName='mx-2 mb-0' groupClassName='mx-2 mb-0'
@ -63,7 +63,7 @@ export default function TopHeader ({ sub, cat }) {
/> />
for for
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
onChange={(formik, e) => top({ ...formik?.values, when: e.target.value })} onChange={(formik, e) => top({ ...formik?.values, when: e.target.value })}
name='when' name='when'
size='sm' size='sm'

View File

@ -8,14 +8,15 @@ import { useMe } from './me'
import Rainbow from '../lib/rainbow' import Rainbow from '../lib/rainbow'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import LongPressable from 'react-longpressable' import LongPressable from 'react-longpressable'
import { Overlay, Popover } from 'react-bootstrap' import Overlay from 'react-bootstrap/Overlay'
import Popover from 'react-bootstrap/Popover'
import { useShowModal } from './modal' import { useShowModal } from './modal'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { LightningConsumer } from './lightning' import { LightningConsumer } from './lightning'
const getColor = (meSats) => { const getColor = (meSats) => {
if (!meSats || meSats <= 10) { if (!meSats || meSats <= 10) {
return 'var(--secondary)' return 'var(--bs-secondary)'
} }
const idx = Math.min( const idx = Math.min(
@ -33,13 +34,13 @@ const UpvotePopover = ({ target, show, handleClose }) => {
placement='right' placement='right'
> >
<Popover id='popover-basic'> <Popover id='popover-basic'>
<Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Zapping <Popover.Body className='d-flex justify-content-between alert-dismissible' as='h3'>Zapping
<button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button> <button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button>
</Popover.Title> </Popover.Body>
<Popover.Content> <Popover.Body>
<div className='mb-2'>Press the bolt again to zap {me?.tipDefault || 1} more sat{me?.tipDefault > 1 ? 's' : ''}.</div> <div className='mb-2'>Press the bolt again to zap {me?.tipDefault || 1} more sat{me?.tipDefault > 1 ? 's' : ''}.</div>
<div>Repeatedly press the bolt to zap more sats.</div> <div>Repeatedly press the bolt to zap more sats.</div>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
</Overlay> </Overlay>
) )
@ -52,13 +53,13 @@ const TipPopover = ({ target, show, handleClose }) => (
placement='right' placement='right'
> >
<Popover id='popover-basic'> <Popover id='popover-basic'>
<Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Press and hold <Popover.Body className='d-flex justify-content-between alert-dismissible' as='h3'>Press and hold
<button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button> <button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button>
</Popover.Title> </Popover.Body>
<Popover.Content> <Popover.Body>
<div className='mb-2'>Press and hold bolt to zap a custom amount.</div> <div className='mb-2'>Press and hold bolt to zap a custom amount.</div>
<div>As you zap more, the bolt color follows the rainbow.</div> <div>As you zap more, the bolt color follows the rainbow.</div>
</Popover.Content> </Popover.Body>
</Popover> </Popover>
</Overlay> </Overlay>
) )

View File

@ -11,10 +11,10 @@ export function UsageHeader () {
when: router.query.when || 'day' when: router.query.when || 'day'
}} }}
> >
<div className='text-muted font-weight-bold my-3 d-flex align-items-center'> <div className='text-muted fw-bold my-3 d-flex align-items-center'>
stacker analytics for stacker analytics for
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
className='w-auto' className='w-auto'
name='when' name='when'
size='sm' size='sm'

View File

@ -1,4 +1,6 @@
import { Button, InputGroup, Image } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import InputGroup from 'react-bootstrap/InputGroup'
import Image from 'react-bootstrap/Image'
import Link from 'next/link' import Link from 'next/link'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import Nav from 'react-bootstrap/Nav' import Nav from 'react-bootstrap/Nav'
@ -158,23 +160,23 @@ function HeaderHeader ({ user }) {
const showModal = useShowModal() const showModal = useShowModal()
const isMe = me?.name === user.name const isMe = me?.name === user.name
const Satistics = () => <div className={`mb-2 ml-0 ml-sm-1 ${styles.username} text-success`}>{user.stacked} stacked</div> const Satistics = () => <div className={`mb-2 ms-0 ms-sm-1 ${styles.username} text-success`}>{user.stacked} stacked</div>
const lnurlp = encodeLNUrl(new URL(`https://stacker.news/.well-known/lnurlp/${user.name}`)) const lnurlp = encodeLNUrl(new URL(`https://stacker.news/.well-known/lnurlp/${user.name}`))
return ( return (
<div className='d-flex mt-2 flex-wrap flex-column flex-sm-row'> <div className='d-flex mt-2 flex-wrap flex-column flex-sm-row'>
<HeaderPhoto user={user} isMe={isMe} /> <HeaderPhoto user={user} isMe={isMe} />
<div className='ml-0 ml-sm-3 mt-3 mt-sm-0 justify-content-center align-self-sm-center'> <div className='ms-0 ms-sm-3 mt-3 mt-sm-0 justify-content-center align-self-sm-center'>
<HeaderNym user={user} isMe={isMe} /> <HeaderNym user={user} isMe={isMe} />
<Satistics user={user} /> <Satistics user={user} />
<Button <Button
className='font-weight-bold ml-0' onClick={() => { className='fw-bold ms-0' onClick={() => {
showModal(({ onClose }) => ( showModal(({ onClose }) => (
<> <>
<a className='d-flex m-auto p-3' style={{ background: 'white', width: 'fit-content' }} href={`lightning:${lnurlp}`}> <a className='d-flex m-auto p-3' style={{ background: 'white', width: 'fit-content' }} href={`lightning:${lnurlp}`}>
<QRCode className='d-flex m-auto' value={lnurlp} renderAs='svg' size={300} /> <QRCode className='d-flex m-auto' value={lnurlp} renderAs='svg' size={300} />
</a> </a>
<div className='text-center font-weight-bold text-muted mt-3'>click or scan</div> <div className='text-center fw-bold text-muted mt-3'>click or scan</div>
</> </>
)) ))
}} }}
@ -182,12 +184,12 @@ function HeaderHeader ({ user }) {
<LightningIcon <LightningIcon
width={20} width={20}
height={20} height={20}
className='mr-1' className='me-1'
/>{user.name}@stacker.news />{user.name}@stacker.news
</Button> </Button>
<div className='d-flex flex-column mt-1 ml-0'> <div className='d-flex flex-column mt-1 ms-0'>
<small className='text-muted d-flex-inline'>stacking since: {user.since <small className='text-muted d-flex-inline'>stacking since: {user.since
? <Link href={`/items/${user.since}`} className='ml-1'>#{user.since}</Link> ? <Link href={`/items/${user.since}`} className='ms-1'>#{user.since}</Link>
: <span>never</span>} : <span>never</span>}
</small> </small>
<small className='text-muted d-flex-inline'>longest cowboy streak: {user.maxStreak !== null ? user.maxStreak : 'none'}</small> <small className='text-muted d-flex-inline'>longest cowboy streak: {user.maxStreak !== null ? user.maxStreak : 'none'}</small>

View File

@ -18,7 +18,7 @@
} }
.nav :global .active { .nav :global .active {
border-bottom: 2px solid var(--primary); border-bottom: 2px solid var(--bs-primary);
} }
.userimg { .userimg {

View File

@ -1,5 +1,5 @@
import Link from 'next/link' import Link from 'next/link'
import { Image } from 'react-bootstrap' import Image from 'react-bootstrap/Image'
import { abbrNum } from '../lib/format' import { abbrNum } from '../lib/format'
import CowboyHat from './cowboy-hat' import CowboyHat from './cowboy-hat'
import styles from './item.module.css' import styles from './item.module.css'
@ -67,12 +67,12 @@ export default function UserList ({ ssrData, query, variables, destructureData }
<Link href={`/${user.name}`}> <Link href={`/${user.name}`}>
<Image <Image
src={user.photoId ? `https://${process.env.NEXT_PUBLIC_AWS_UPLOAD_BUCKET}.s3.amazonaws.com/${user.photoId}` : '/dorian400.jpg'} width='32' height='32' src={user.photoId ? `https://${process.env.NEXT_PUBLIC_AWS_UPLOAD_BUCKET}.s3.amazonaws.com/${user.photoId}` : '/dorian400.jpg'} width='32' height='32'
className={`${userStyles.userimg} mr-2`} className={`${userStyles.userimg} me-2`}
/> />
</Link> </Link>
<div className={styles.hunk}> <div className={styles.hunk}>
<Link href={`/${user.name}`} className={`${styles.title} d-inline-flex align-items-center text-reset`}> <Link href={`/${user.name}`} className={`${styles.title} d-inline-flex align-items-center text-reset`}>
@{user.name}<CowboyHat className='ml-1 fill-grey' height={14} width={14} user={user} /> @{user.name}<CowboyHat className='ms-1 fill-grey' height={14} width={14} user={user} />
</Link> </Link>
<div className={styles.other}> <div className={styles.other}>
{statComps.map((Comp, i) => <Comp key={i} user={user} />)} {statComps.map((Comp, i) => <Comp key={i} user={user} />)}
@ -94,7 +94,7 @@ export function UsersSkeleton () {
<Image <Image
src={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/clouds.jpeg`} src={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/clouds.jpeg`}
width='32' height='32' width='32' height='32'
className={`${userStyles.userimg} clouds mr-2`} className={`${userStyles.userimg} clouds me-2`}
/> />
<div className={styles.hunk}> <div className={styles.hunk}>
<div className={`${styles.name} clouds text-reset`} /> <div className={`${styles.name} clouds text-reset`} />

View File

@ -42,12 +42,12 @@ const transformData = data => {
} }
const COLORS = [ const COLORS = [
'var(--secondary)', 'var(--bs-secondary)',
'var(--info)', 'var(--bs-info)',
'var(--success)', 'var(--bs-success)',
'var(--boost)', 'var(--bs-boost)',
'var(--theme-grey)', 'var(--theme-grey)',
'var(--danger)' 'var(--bs-danger)'
] ]
export function WhenAreaChart ({ data }) { export function WhenAreaChart ({ data }) {
@ -150,7 +150,7 @@ export function WhenComposedChart ({ data, lineNames, areaNames, barNames }) {
<Tooltip labelFormatter={dateFormatter(when)} contentStyle={{ color: 'var(--theme-color)', backgroundColor: 'var(--theme-body)' }} /> <Tooltip labelFormatter={dateFormatter(when)} contentStyle={{ color: 'var(--theme-color)', backgroundColor: 'var(--theme-body)' }} />
<Legend /> <Legend />
{barNames?.map((v, i) => {barNames?.map((v, i) =>
<Bar yAxisId='right' key={v} type='monotone' dataKey={v} name={v} stroke='var(--info)' fill='var(--info)' />)} <Bar yAxisId='right' key={v} type='monotone' dataKey={v} name={v} stroke='var(--bs-info)' fill='var(--bs-info)' />)}
{areaNames?.map((v, i) => {areaNames?.map((v, i) =>
<Area yAxisId='left' key={v} type='monotone' dataKey={v} name={v} stackId='1' stroke={COLORS[i]} fill={COLORS[i]} />)} <Area yAxisId='left' key={v} type='monotone' dataKey={v} name={v} stackId='1' stroke={COLORS[i]} fill={COLORS[i]} />)}
{lineNames?.map((v, i) => {lineNames?.map((v, i) =>

256
package-lock.json generated
View File

@ -23,7 +23,7 @@
"babel-plugin-inline-react-svg": "^2.0.1", "babel-plugin-inline-react-svg": "^2.0.1",
"bech32": "^2.0.0", "bech32": "^2.0.0",
"bolt11": "^1.4.0", "bolt11": "^1.4.0",
"bootstrap": "^4.6.2", "bootstrap": "^5.3.0",
"browserslist": "^4.21.4", "browserslist": "^4.21.4",
"canonical-json": "0.0.4", "canonical-json": "0.0.4",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
@ -58,7 +58,7 @@
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-avatar-editor": "^13.0.0", "react-avatar-editor": "^13.0.0",
"react-bootstrap": "^1.6.6", "react-bootstrap": "^2.8.0",
"react-countdown": "^2.3.3", "react-countdown": "^2.3.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-longpressable": "^1.1.1", "react-longpressable": "^1.1.1",
@ -2859,9 +2859,9 @@
} }
}, },
"node_modules/@popperjs/core": { "node_modules/@popperjs/core": {
"version": "2.11.6", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
@ -2952,6 +2952,17 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
}, },
"node_modules/@react-aria/ssr": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.0.tgz",
"integrity": "sha512-bfufjg4ESE5giN+Fxj1XIzS5f/YIhqcGc+Ve+vUUKU8xZ8t/Xtjlv8F3kjqDBQdk//n3mluFY7xG1wQVB9rMLQ==",
"dependencies": {
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
}
},
"node_modules/@remusao/guess-url-type": { "node_modules/@remusao/guess-url-type": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@remusao/guess-url-type/-/guess-url-type-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@remusao/guess-url-type/-/guess-url-type-1.2.1.tgz",
@ -2989,25 +3000,45 @@
"resolved": "https://registry.npmjs.org/@remusao/trie/-/trie-1.4.1.tgz", "resolved": "https://registry.npmjs.org/@remusao/trie/-/trie-1.4.1.tgz",
"integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q==" "integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q=="
}, },
"node_modules/@restart/context": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
"integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==",
"peerDependencies": {
"react": ">=16.3.2"
}
},
"node_modules/@restart/hooks": { "node_modules/@restart/hooks": {
"version": "0.4.7", "version": "0.4.11",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz",
"integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==", "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==",
"dependencies": { "dependencies": {
"dequal": "^2.0.2" "dequal": "^2.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0" "react": ">=16.8.0"
} }
}, },
"node_modules/@restart/ui": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz",
"integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"@popperjs/core": "^2.11.6",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.4.9",
"@types/warning": "^3.0.0",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^8.0.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
}
},
"node_modules/@restart/ui/node_modules/uncontrollable": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.2.tgz",
"integrity": "sha512-/GDx+K1STGtpgTsj5Dj3J51YaKxZDblbCQHTH1zHLuoBEWodj6MjtRVv3TUijj1JYLRLSFsFzN8NV4M3QV4d9w==",
"peerDependencies": {
"react": ">=16.14.0"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -3475,11 +3506,6 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
}, },
"node_modules/@types/invariant": {
"version": "2.2.35",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
"integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.12", "version": "7.0.12",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
@ -4903,9 +4929,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"node_modules/bootstrap": { "node_modules/bootstrap": {
"version": "4.6.2", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz",
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -4917,8 +4943,7 @@
} }
], ],
"peerDependencies": { "peerDependencies": {
"jquery": "1.9.1 - 3", "@popperjs/core": "^2.11.7"
"popper.js": "^1.16.1"
} }
}, },
"node_modules/bplist-parser": { "node_modules/bplist-parser": {
@ -14227,31 +14252,42 @@
} }
}, },
"node_modules/react-bootstrap": { "node_modules/react-bootstrap": {
"version": "1.6.6", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.6.tgz", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz",
"integrity": "sha512-pSzYyJT5u4rc8+5myM8Vid2JG52L8AmYSkpznReH/GM4+FhLqEnxUa0+6HRTaGwjdEixQNGchwY+b3xCdYWrDA==", "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.14.0", "@babel/runtime": "^7.21.0",
"@restart/context": "^2.1.4", "@restart/hooks": "^0.4.9",
"@restart/hooks": "^0.4.7", "@restart/ui": "^1.6.3",
"@types/invariant": "^2.2.33", "@types/react-transition-group": "^4.4.5",
"@types/prop-types": "^15.7.3", "classnames": "^2.3.2",
"@types/react": ">=16.14.8",
"@types/react-transition-group": "^4.4.1",
"@types/warning": "^3.0.0",
"classnames": "^2.3.1",
"dom-helpers": "^5.2.1", "dom-helpers": "^5.2.1",
"invariant": "^2.2.4", "invariant": "^2.2.4",
"prop-types": "^15.7.2", "prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0", "prop-types-extra": "^1.1.0",
"react-overlays": "^5.1.2", "react-transition-group": "^4.4.5",
"react-transition-group": "^4.4.1",
"uncontrollable": "^7.2.1", "uncontrollable": "^7.2.1",
"warning": "^4.0.3" "warning": "^4.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0", "@types/react": ">=16.14.8",
"react-dom": ">=16.8.0" "react": ">=16.14.0",
"react-dom": ">=16.14.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-bootstrap/node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
} }
}, },
"node_modules/react-countdown": { "node_modules/react-countdown": {
@ -14337,25 +14373,6 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
}, },
"node_modules/react-overlays": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
"integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
"dependencies": {
"@babel/runtime": "^7.13.8",
"@popperjs/core": "^2.11.6",
"@restart/hooks": "^0.4.7",
"@types/warning": "^3.0.0",
"dom-helpers": "^5.2.0",
"prop-types": "^15.7.2",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"react": ">=16.3.0",
"react-dom": ">=16.3.0"
}
},
"node_modules/react-resize-detector": { "node_modules/react-resize-detector": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz",
@ -20797,9 +20814,9 @@
} }
}, },
"@popperjs/core": { "@popperjs/core": {
"version": "2.11.6", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
}, },
"@prisma/client": { "@prisma/client": {
"version": "2.30.3", "version": "2.30.3",
@ -20873,6 +20890,14 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
}, },
"@react-aria/ssr": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.0.tgz",
"integrity": "sha512-bfufjg4ESE5giN+Fxj1XIzS5f/YIhqcGc+Ve+vUUKU8xZ8t/Xtjlv8F3kjqDBQdk//n3mluFY7xG1wQVB9rMLQ==",
"requires": {
"@swc/helpers": "^0.5.0"
}
},
"@remusao/guess-url-type": { "@remusao/guess-url-type": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@remusao/guess-url-type/-/guess-url-type-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@remusao/guess-url-type/-/guess-url-type-1.2.1.tgz",
@ -20910,17 +20935,35 @@
"resolved": "https://registry.npmjs.org/@remusao/trie/-/trie-1.4.1.tgz", "resolved": "https://registry.npmjs.org/@remusao/trie/-/trie-1.4.1.tgz",
"integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q==" "integrity": "sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q=="
}, },
"@restart/context": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
"integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q=="
},
"@restart/hooks": { "@restart/hooks": {
"version": "0.4.7", "version": "0.4.11",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz",
"integrity": "sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A==", "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==",
"requires": { "requires": {
"dequal": "^2.0.2" "dequal": "^2.0.3"
}
},
"@restart/ui": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz",
"integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==",
"requires": {
"@babel/runtime": "^7.21.0",
"@popperjs/core": "^2.11.6",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.4.9",
"@types/warning": "^3.0.0",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^8.0.1",
"warning": "^4.0.3"
},
"dependencies": {
"uncontrollable": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.2.tgz",
"integrity": "sha512-/GDx+K1STGtpgTsj5Dj3J51YaKxZDblbCQHTH1zHLuoBEWodj6MjtRVv3TUijj1JYLRLSFsFzN8NV4M3QV4d9w=="
}
} }
}, },
"@rollup/plugin-babel": { "@rollup/plugin-babel": {
@ -21347,11 +21390,6 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
}, },
"@types/invariant": {
"version": "2.2.35",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
"integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
},
"@types/json-schema": { "@types/json-schema": {
"version": "7.0.12", "version": "7.0.12",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
@ -22521,9 +22559,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"bootstrap": { "bootstrap": {
"version": "4.6.2", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz",
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==" "integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw=="
}, },
"bplist-parser": { "bplist-parser": {
"version": "0.2.0", "version": "0.2.0",
@ -29311,27 +29349,34 @@
} }
}, },
"react-bootstrap": { "react-bootstrap": {
"version": "1.6.6", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.6.tgz", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz",
"integrity": "sha512-pSzYyJT5u4rc8+5myM8Vid2JG52L8AmYSkpznReH/GM4+FhLqEnxUa0+6HRTaGwjdEixQNGchwY+b3xCdYWrDA==", "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==",
"requires": { "requires": {
"@babel/runtime": "^7.14.0", "@babel/runtime": "^7.21.0",
"@restart/context": "^2.1.4", "@restart/hooks": "^0.4.9",
"@restart/hooks": "^0.4.7", "@restart/ui": "^1.6.3",
"@types/invariant": "^2.2.33", "@types/react-transition-group": "^4.4.5",
"@types/prop-types": "^15.7.3", "classnames": "^2.3.2",
"@types/react": ">=16.14.8",
"@types/react-transition-group": "^4.4.1",
"@types/warning": "^3.0.0",
"classnames": "^2.3.1",
"dom-helpers": "^5.2.1", "dom-helpers": "^5.2.1",
"invariant": "^2.2.4", "invariant": "^2.2.4",
"prop-types": "^15.7.2", "prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0", "prop-types-extra": "^1.1.0",
"react-overlays": "^5.1.2", "react-transition-group": "^4.4.5",
"react-transition-group": "^4.4.1",
"uncontrollable": "^7.2.1", "uncontrollable": "^7.2.1",
"warning": "^4.0.3" "warning": "^4.0.3"
},
"dependencies": {
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
}
} }
}, },
"react-countdown": { "react-countdown": {
@ -29400,21 +29445,6 @@
} }
} }
}, },
"react-overlays": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
"integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
"requires": {
"@babel/runtime": "^7.13.8",
"@popperjs/core": "^2.11.6",
"@restart/hooks": "^0.4.7",
"@types/warning": "^3.0.0",
"dom-helpers": "^5.2.0",
"prop-types": "^15.7.2",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
}
},
"react-resize-detector": { "react-resize-detector": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz",

View File

@ -24,7 +24,7 @@
"babel-plugin-inline-react-svg": "^2.0.1", "babel-plugin-inline-react-svg": "^2.0.1",
"bech32": "^2.0.0", "bech32": "^2.0.0",
"bolt11": "^1.4.0", "bolt11": "^1.4.0",
"bootstrap": "^4.6.2", "bootstrap": "^5.3.0",
"browserslist": "^4.21.4", "browserslist": "^4.21.4",
"canonical-json": "0.0.4", "canonical-json": "0.0.4",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
@ -59,7 +59,7 @@
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-avatar-editor": "^13.0.0", "react-avatar-editor": "^13.0.0",
"react-bootstrap": "^1.6.6", "react-bootstrap": "^2.8.0",
"react-countdown": "^2.3.3", "react-countdown": "^2.3.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-longpressable": "^1.1.1", "react-longpressable": "^1.1.1",

View File

@ -1,4 +1,4 @@
import { Image } from 'react-bootstrap' import Image from 'react-bootstrap/Image'
import { StaticLayout } from '../components/layout' import { StaticLayout } from '../components/layout'
import styles from '../styles/404.module.css' import styles from '../styles/404.module.css'

View File

@ -1,4 +1,4 @@
import { Image } from 'react-bootstrap' import Image from 'react-bootstrap/Image'
import { StaticLayout } from '../components/layout' import { StaticLayout } from '../components/layout'
import styles from '../styles/404.module.css' import styles from '../styles/404.module.css'

View File

@ -60,9 +60,9 @@ function UserItemsHeader ({ type, name }) {
}} }}
onSubmit={select} onSubmit={select}
> >
<div className='text-muted font-weight-bold mt-0 mb-3 d-flex justify-content-start align-items-center'> <div className='text-muted fw-bold mt-0 mb-3 d-flex justify-content-start align-items-center'>
<Select <Select
groupClassName='mb-0 mr-2' groupClassName='mb-0 me-2'
className='w-auto' className='w-auto'
name='type' name='type'
size='sm' size='sm'
@ -80,7 +80,7 @@ function UserItemsHeader ({ type, name }) {
/> />
for for
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
className='w-auto' className='w-auto'
name='when' name='when'
size='sm' size='sm'

View File

@ -1,7 +1,7 @@
import Layout from '../../components/layout' import Layout from '../../components/layout'
import { gql, useMutation, useQuery } from '@apollo/client' import { gql, useMutation, useQuery } from '@apollo/client'
import UserHeader from '../../components/user-header' import UserHeader from '../../components/user-header'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import styles from '../../styles/user.module.css' import styles from '../../styles/user.module.css'
import { useState } from 'react' import { useState } from 'react'
import ItemFull from '../../components/item-full' import ItemFull from '../../components/item-full'

View File

@ -14,14 +14,16 @@ import ErrorBoundary from '../components/error-boundary'
import { LightningProvider } from '../components/lightning' import { LightningProvider } from '../components/lightning'
import { ServiceWorkerProvider } from '../components/serviceworker' import { ServiceWorkerProvider } from '../components/serviceworker'
const SSR = typeof window === 'undefined'
function writeQuery (client, apollo, data) { function writeQuery (client, apollo, data) {
if (apollo && data) { if (apollo && data) {
client.writeQuery({ client.writeQuery({
query: gql`${apollo.query}`, query: gql`${apollo.query}`,
data: data, data: data,
variables: apollo.variables, variables: apollo.variables,
broadcast: false, broadcast: !SSR,
overwrite: true overwrite: SSR
}) })
} }
} }
@ -54,7 +56,7 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
*/ */
const { apollo, ssrData, me, price, ...otherProps } = props const { apollo, ssrData, me, price, ...otherProps } = props
// if we are on the server, useEffect won't run // if we are on the server, useEffect won't run
if (typeof window === 'undefined' && client) { if (SSR && client) {
writeQuery(client, apollo, ssrData) writeQuery(client, apollo, ssrData)
} }
useEffect(() => { useEffect(() => {
@ -64,7 +66,7 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
return ( return (
<> <>
<NextNProgress <NextNProgress
color='var(--primary)' color='var(--bs-primary)'
startPosition={0.3} startPosition={0.3}
stopDelayMs={200} stopDelayMs={200}
height={2} height={2}

View File

@ -1,4 +1,4 @@
import { Image } from 'react-bootstrap' import Image from 'react-bootstrap/Image'
import { StaticLayout } from '../components/layout' import { StaticLayout } from '../components/layout'
export default function Email () { export default function Email () {

View File

@ -1,6 +1,6 @@
import Layout from '../../components/layout' import Layout from '../../components/layout'
import { Form, Input, SubmitButton } from '../../components/form' import { Form, Input, SubmitButton } from '../../components/form'
import { InputGroup } from 'react-bootstrap' import InputGroup from 'react-bootstrap/InputGroup'
import { gql, useMutation, useQuery } from '@apollo/client' import { gql, useMutation, useQuery } from '@apollo/client'
import { INVITE_FIELDS } from '../../fragments/invites' import { INVITE_FIELDS } from '../../fragments/invites'
import AccordianItem from '../../components/accordian-item' import AccordianItem from '../../components/accordian-item'
@ -58,7 +58,7 @@ function InviteForm () {
required required
/> />
<Input <Input
label={<>invitee limit <small className='text-muted ml-2'>optional</small></>} label={<>invitee limit <small className='text-muted ms-2'>optional</small></>}
name='limit' name='limit'
/> />
@ -113,7 +113,7 @@ export default function Invites () {
<h2 className='mt-3 mb-0'> <h2 className='mt-3 mb-0'>
invite links invite links
</h2> </h2>
<small className='d-block text-muted font-weight-bold mx-5'>send these to people you trust, e.g. group chats or DMs</small> <small className='d-block text-muted fw-bold mx-5'>send these to people you trust, e.g. group chats or DMs</small>
</div> </div>
<InviteForm /> <InviteForm />
{active.length > 0 && <InviteList name='active' invites={active} />} {active.length > 0 && <InviteList name='active' invites={active} />}

View File

@ -2,7 +2,7 @@ import Layout from '../../../components/layout'
import { ITEM_OTS } from '../../../fragments/items' import { ITEM_OTS } from '../../../fragments/items'
import { getGetServerSideProps } from '../../../api/ssrApollo' import { getGetServerSideProps } from '../../../api/ssrApollo'
import stringifyCanon from 'canonical-json' import stringifyCanon from 'canonical-json'
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { useQuery } from '@apollo/client' import { useQuery } from '@apollo/client'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import PageLoading from '../../../components/page-loading' import PageLoading from '../../../components/page-loading'

View File

@ -23,7 +23,7 @@ export default function Related ({ ssrData }) {
return ( return (
<Layout> <Layout>
<Item item={item} /> <Item item={item} />
<div className='font-weight-bold my-2'>related</div> <div className='fw-bold my-2'>related</div>
<Items <Items
ssrData={ssrData} ssrData={ssrData}
query={RELATED_ITEMS} query={RELATED_ITEMS}

View File

@ -1,4 +1,4 @@
import { Button } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import { CenterLayout } from '../components/layout' import { CenterLayout } from '../components/layout'
import Snl from '../components/snl' import Snl from '../components/snl'
import { gql } from 'graphql-tag' import { gql } from 'graphql-tag'

View File

@ -39,7 +39,7 @@ export async function getServerSideProps ({ req, res, query: { callbackUrl, erro
function LoginFooter ({ callbackUrl }) { function LoginFooter ({ callbackUrl }) {
return ( return (
<small className='font-weight-bold text-muted pt-4'>Don't have an account? <Link href={{ pathname: '/signup', query: { callbackUrl } }}>sign up</Link></small> <small className='fw-bold text-muted pt-4'>Don't have an account? <Link href={{ pathname: '/signup', query: { callbackUrl } }}>sign up</Link></small>
) )
} }

View File

@ -1,4 +1,4 @@
import { Image } from 'react-bootstrap' import Image from 'react-bootstrap/Image'
import { StaticLayout } from '../components/layout' import { StaticLayout } from '../components/layout'
import styles from '../styles/404.module.css' import styles from '../styles/404.module.css'

View File

@ -35,7 +35,7 @@ export default function Referrals ({ ssrData }) {
const { data } = useQuery(REFERRALS, { variables: { when: router.query.when } }) const { data } = useQuery(REFERRALS, { variables: { when: router.query.when } })
if (!data && !ssrData) return <PageLoading /> if (!data && !ssrData) return <PageLoading />
const { referrals: { totalSats, totalReferrals, stats } } = data const { referrals: { totalSats, totalReferrals, stats } } = data || ssrData
return ( return (
<CenterLayout footerLinks> <CenterLayout footerLinks>
@ -44,10 +44,10 @@ export default function Referrals ({ ssrData }) {
when: router.query.when when: router.query.when
}} }}
> >
<h4 className='font-weight-bold text-muted text-center pt-5 pb-3 d-flex align-items-center justify-content-center'> <h4 className='fw-bold text-muted text-center pt-5 pb-3 d-flex align-items-center justify-content-center'>
{totalReferrals} referrals & {totalSats} sats in the last {totalReferrals} referrals & {totalSats} sats in the last
<Select <Select
groupClassName='mb-0 ml-2' groupClassName='mb-0 ms-2'
className='w-auto' className='w-auto'
name='when' name='when'
size='sm' size='sm'
@ -61,7 +61,7 @@ export default function Referrals ({ ssrData }) {
<div <div
className='text-small pt-5 px-3 d-flex w-100 align-items-center' className='text-small pt-5 px-3 d-flex w-100 align-items-center'
> >
<div className='nav-item text-muted pr-2' style={{ 'white-space': 'nowrap' }}>referral link:</div> <div className='nav-item text-muted pe-2' style={{ 'white-space': 'nowrap' }}>referral link:</div>
<CopyInput <CopyInput
size='sm' size='sm'
groupClassName='mb-0 w-100' groupClassName='mb-0 w-100'

View File

@ -1,6 +1,7 @@
import { gql } from 'graphql-tag' import { gql } from 'graphql-tag'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Button, InputGroup } from 'react-bootstrap' import Button from 'react-bootstrap/Button'
import InputGroup from 'react-bootstrap/InputGroup'
import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts' import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts'
import { getGetServerSideProps } from '../api/ssrApollo' import { getGetServerSideProps } from '../api/ssrApollo'
import { Form, Input, SubmitButton } from '../components/form' import { Form, Input, SubmitButton } from '../components/form'
@ -68,7 +69,7 @@ export default function Rewards ({ ssrData }) {
return ( return (
<CenterLayout footerLinks> <CenterLayout footerLinks>
<h4 className='font-weight-bold text-muted text-center'> <h4 className='fw-bold text-muted text-center'>
<div> <div>
<RewardLine total={total} /> <RewardLine total={total} />
</div> </div>
@ -85,11 +86,11 @@ export default function Rewards ({ ssrData }) {
} }
const COLORS = [ const COLORS = [
'var(--secondary)', 'var(--bs-secondary)',
'var(--info)', 'var(--bs-info)',
'var(--success)', 'var(--bs-success)',
'var(--boost)', 'var(--bs-boost)',
'var(--grey)' 'var(--bs-grey)'
] ]
function GrowthPieChart ({ data }) { function GrowthPieChart ({ data }) {
@ -103,7 +104,7 @@ function GrowthPieChart ({ data }) {
cx='50%' cx='50%'
cy='50%' cy='50%'
outerRadius={80} outerRadius={80}
fill='var(--secondary)' fill='var(--bs-secondary)'
label label
> >
{ {
@ -151,7 +152,7 @@ export function DonateButton () {
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>} append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
/> />
<div className='d-flex'> <div className='d-flex'>
<SubmitButton variant='success' className='ml-auto mt-1 px-4' value='TIP'>donate</SubmitButton> <SubmitButton variant='success' className='ms-auto mt-1 px-4' value='TIP'>donate</SubmitButton>
</div> </div>
</Form> </Form>
))} ))}

View File

@ -79,7 +79,7 @@ function Satus ({ status }) {
return ( return (
<div> <div>
<Icon /><small className={`text-${color} font-weight-bold ml-2`}>{desc}</small> <Icon /><small className={`text-${color} fw-bold ms-2`}>{desc}</small>
</div> </div>
) )
} }

View File

@ -1,5 +1,7 @@
import { Checkbox, Form, Input, SubmitButton, Select, VariableInput } from '../components/form' import { Checkbox, Form, Input, SubmitButton, Select, VariableInput } from '../components/form'
import { Alert, Button, InputGroup } from 'react-bootstrap' import Alert from 'react-bootstrap/Alert'
import Button from 'react-bootstrap/Button'
import InputGroup from 'react-bootstrap/InputGroup'
import { CenterLayout } from '../components/layout' import { CenterLayout } from '../components/layout'
import { useState } from 'react' import { useState } from 'react'
import { gql, useMutation, useQuery } from '@apollo/client' import { gql, useMutation, useQuery } from '@apollo/client'
@ -113,7 +115,7 @@ export default function Settings ({ ssrData }) {
label={ label={
<div className='d-flex align-items-center'>turbo zapping <div className='d-flex align-items-center'>turbo zapping
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>Makes every additional bolt click raise your total zap to another 10x multiple of your default zap</li> <li>Makes every additional bolt click raise your total zap to another 10x multiple of your default zap</li>
<li>e.g. if your zap default is 10 sats <li>e.g. if your zap default is 10 sats
<ul> <ul>
@ -188,7 +190,7 @@ export default function Settings ({ ssrData }) {
label={ label={
<div className='d-flex align-items-center'>hide invoice descriptions <div className='d-flex align-items-center'>hide invoice descriptions
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>Use this if you don't want funding sources to be linkable to your SN identity.</li> <li>Use this if you don't want funding sources to be linkable to your SN identity.</li>
<li>It makes your invoice descriptions blank.</li> <li>It makes your invoice descriptions blank.</li>
<li>This only applies to invoices you create <li>This only applies to invoices you create
@ -217,7 +219,7 @@ export default function Settings ({ ssrData }) {
label={ label={
<div className='d-flex align-items-center'>wild west mode <div className='d-flex align-items-center'>wild west mode
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>don't hide flagged content</li> <li>don't hide flagged content</li>
<li>don't down rank flagged content</li> <li>don't down rank flagged content</li>
</ul> </ul>
@ -231,7 +233,7 @@ export default function Settings ({ ssrData }) {
label={ label={
<div className='d-flex align-items-center'>greeter mode <div className='d-flex align-items-center'>greeter mode
<Info> <Info>
<ul className='font-weight-bold'> <ul className='fw-bold'>
<li>see and screen free posts and comments</li> <li>see and screen free posts and comments</li>
<li>help onboard new stackers to SN and Lightning</li> <li>help onboard new stackers to SN and Lightning</li>
<li>you might be subject to more spam</li> <li>you might be subject to more spam</li>
@ -244,16 +246,16 @@ export default function Settings ({ ssrData }) {
<AccordianItem <AccordianItem
headerColor='var(--theme-color)' headerColor='var(--theme-color)'
show={settings?.nostrPubkey} show={settings?.nostrPubkey}
header={<h4 className='mb-2 text-left'>nostr <small><a href='https://github.com/nostr-protocol/nips/blob/master/05.md' target='_blank' rel='noreferrer'>NIP-05</a></small></h4>} header={<h4 className='text-left'>nostr <small><a href='https://github.com/nostr-protocol/nips/blob/master/05.md' target='_blank' rel='noreferrer'>NIP-05</a></small></h4>}
body={ body={
<> <>
<Input <Input
label={<>pubkey <small className='text-muted ml-2'>optional</small></>} label={<>pubkey <small className='text-muted ms-2'>optional</small></>}
name='nostrPubkey' name='nostrPubkey'
clear clear
/> />
<VariableInput <VariableInput
label={<>relays <small className='text-muted ml-2'>optional</small></>} label={<>relays <small className='text-muted ms-2'>optional</small></>}
name='nostrRelays' name='nostrRelays'
clear clear
min={0} min={0}
@ -263,7 +265,7 @@ export default function Settings ({ ssrData }) {
} }
/> />
<div className='d-flex'> <div className='d-flex'>
<SubmitButton variant='info' className='ml-auto mt-1 px-4'>save</SubmitButton> <SubmitButton variant='info' className='ms-auto mt-1 px-4'>save</SubmitButton>
</div> </div>
</Form> </Form>
<div className='text-left w-100'> <div className='text-left w-100'>
@ -301,7 +303,7 @@ function UnlinkObstacle ({ onClose, type, unlinkAuth }) {
<div> <div>
You are removing your last auth method. It is recommended you link another auth method before removing You are removing your last auth method. It is recommended you link another auth method before removing
your last auth method. If you'd like to proceed anyway, type the following below your last auth method. If you'd like to proceed anyway, type the following below
<div className='text-danger font-weight-bold my-2'> <div className='text-danger fw-bold my-2'>
If I logout, even accidentally, I will never be able to access my account again If I logout, even accidentally, I will never be able to access my account again
</div> </div>
<Form <Form
@ -320,7 +322,7 @@ function UnlinkObstacle ({ onClose, type, unlinkAuth }) {
name='warning' name='warning'
required required
/> />
<SubmitButton className='d-flex ml-auto' variant='danger'>do it</SubmitButton> <SubmitButton className='d-flex ms-auto' variant='danger'>do it</SubmitButton>
</Form> </Form>
</div> </div>
) )
@ -379,7 +381,7 @@ function AuthMethods ({ methods }) {
noForm noForm
/> />
<Button <Button
className='ml-2' variant='secondary' onClick={ className='ms-2' variant='secondary' onClick={
async () => { async () => {
await unlink('email') await unlink('email')
} }
@ -448,7 +450,7 @@ export function EmailLinkForm ({ callbackUrl }) {
required required
groupClassName='mb-0' groupClassName='mb-0'
/> />
<SubmitButton className='ml-2' variant='secondary'>Link Email</SubmitButton> <SubmitButton className='ms-2' variant='secondary'>Link Email</SubmitButton>
</div> </div>
</Form> </Form>
) )

View File

@ -9,14 +9,14 @@ function SignUpHeader () {
<h3 className='w-100 pb-2'> <h3 className='w-100 pb-2'>
Sign up Sign up
</h3> </h3>
<div className='font-weight-bold text-muted pb-4'>Join 15,000+ bitcoiners and start stacking sats today</div> <div className='fw-bold text-muted pb-4'>Join 15,000+ bitcoiners and start stacking sats today</div>
</> </>
) )
} }
function SignUpFooter ({ callbackUrl }) { function SignUpFooter ({ callbackUrl }) {
return ( return (
<small className='font-weight-bold text-muted pt-4'>Already have an account? <Link href={{ pathname: '/login', query: { callbackUrl } }}>login</Link></small> <small className='fw-bold text-muted pt-4'>Already have an account? <Link href={{ pathname: '/login', query: { callbackUrl } }}>login</Link></small>
) )
} }

View File

@ -1,7 +1,8 @@
import { gql } from '@apollo/client' import { gql } from '@apollo/client'
import { getGetServerSideProps } from '../../api/ssrApollo' import { getGetServerSideProps } from '../../api/ssrApollo'
import Layout from '../../components/layout' import Layout from '../../components/layout'
import { Col, Row } from 'react-bootstrap' import Col from 'react-bootstrap/Col'
import Row from 'react-bootstrap/Row'
import { UsageHeader } from '../../components/usage-header' import { UsageHeader } from '../../components/usage-header'
import { WhenLineChart, WhenAreaChart } from '../../components/when-charts' import { WhenLineChart, WhenAreaChart } from '../../components/when-charts'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
@ -65,31 +66,31 @@ export default function Growth ({
<UsageHeader /> <UsageHeader />
<Row> <Row>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>{avg}stackers</div> <div className='text-center text-muted fw-bold'>{avg}stackers</div>
<WhenLineChart data={stackerGrowth} /> <WhenLineChart data={stackerGrowth} />
</Col> </Col>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>stacking</div> <div className='text-center text-muted fw-bold'>stacking</div>
<WhenAreaChart data={stackingGrowth} /> <WhenAreaChart data={stackingGrowth} />
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>{avg}spenders</div> <div className='text-center text-muted fw-bold'>{avg}spenders</div>
<WhenLineChart data={spenderGrowth} /> <WhenLineChart data={spenderGrowth} />
</Col> </Col>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>spending</div> <div className='text-center text-muted fw-bold'>spending</div>
<WhenAreaChart data={spendingGrowth} /> <WhenAreaChart data={spendingGrowth} />
</Col> </Col>
</Row> </Row>
<Row> <Row>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>registrations</div> <div className='text-center text-muted fw-bold'>registrations</div>
<WhenAreaChart data={registrationGrowth} /> <WhenAreaChart data={registrationGrowth} />
</Col> </Col>
<Col className='mt-3'> <Col className='mt-3'>
<div className='text-center text-muted font-weight-bold'>items</div> <div className='text-center text-muted fw-bold'>items</div>
<WhenAreaChart data={itemGrowth} /> <WhenAreaChart data={itemGrowth} />
</Col> </Col>
</Row> </Row>

View File

@ -10,7 +10,7 @@ import { WithdrawlSkeleton } from './withdrawals/[id]'
import { useMe } from '../components/me' import { useMe } from '../components/me'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { requestProvider } from 'webln' import { requestProvider } from 'webln'
import { Alert } from 'react-bootstrap' import Alert from 'react-bootstrap/Alert'
import { CREATE_WITHDRAWL, SEND_TO_LNADDR } from '../fragments/wallet' import { CREATE_WITHDRAWL, SEND_TO_LNADDR } from '../fragments/wallet'
import { getGetServerSideProps } from '../api/ssrApollo' import { getGetServerSideProps } from '../api/ssrApollo'
import { amountSchema, lnAddrSchema, withdrawlSchema } from '../lib/validate' import { amountSchema, lnAddrSchema, withdrawlSchema } from '../lib/validate'
@ -37,7 +37,7 @@ function YouHaveSats () {
function WalletHistory () { function WalletHistory () {
return ( return (
<div className='pt-4'> <div className='pt-4'>
<Link href='/satistics?inc=invoice,withdrawal' className='text-muted font-weight-bold text-underline'> <Link href='/satistics?inc=invoice,withdrawal' className='text-muted fw-bold text-underline'>
wallet history wallet history
</Link> </Link>
</div> </div>
@ -54,7 +54,7 @@ export function WalletForm () {
<Link href='/wallet?type=fund'> <Link href='/wallet?type=fund'>
<Button variant='success'>fund</Button> <Button variant='success'>fund</Button>
</Link> </Link>
<span className='mx-3 font-weight-bold text-muted'>or</span> <span className='mx-3 fw-bold text-muted'>or</span>
<Link href='/wallet?type=withdraw'> <Link href='/wallet?type=withdraw'>
<Button variant='success'>withdraw</Button> <Button variant='success'>withdraw</Button>
</Link> </Link>
@ -189,7 +189,7 @@ export function WithdrawlForm () {
/> />
<SubmitButton variant='success' className='mt-2'>withdraw</SubmitButton> <SubmitButton variant='success' className='mt-2'>withdraw</SubmitButton>
</Form> </Form>
<span className='my-3 font-weight-bold text-muted'>or via</span> <span className='my-3 fw-bold text-muted'>or via</span>
<Link href='/wallet?type=lnurl-withdraw'> <Link href='/wallet?type=lnurl-withdraw'>
<Button variant='grey'>QR code</Button> <Button variant='grey'>QR code</Button>
</Link> </Link>

View File

@ -43,7 +43,7 @@ function LoadWithdrawl () {
const TryMaxFee = () => const TryMaxFee = () =>
<Link href='/wallet?type=withdraw' className='text-reset text-underline'> <Link href='/wallet?type=withdraw' className='text-reset text-underline'>
<small className='ml-3'>try increasing max fee</small> <small className='ms-3'>try increasing max fee</small>
</Link> </Link>
let status = 'pending' let status = 'pending'
@ -54,7 +54,7 @@ function LoadWithdrawl () {
variant = 'confirmed' variant = 'confirmed'
break break
case 'INSUFFICIENT_BALANCE': case 'INSUFFICIENT_BALANCE':
status = <>insufficient balance <small className='ml-3'>contact keyan!</small></> status = <>insufficient balance <small className='ms-3'>contact keyan!</small></>
variant = 'failed' variant = 'failed'
break break
case 'INVALID_PAYMENT': case 'INVALID_PAYMENT':

View File

@ -25,7 +25,7 @@ export default function Index ({ ssrData }) {
/> />
: ( : (
<div className='text-muted text-center mt-5' style={{ fontFamily: 'lightning', fontSize: '2rem', opacity: '0.75' }}> <div className='text-muted text-center mt-5' style={{ fontFamily: 'lightning', fontSize: '2rem', opacity: '0.75' }}>
<Down width={22} height={22} className='mr-2' />search for something<Down width={22} height={22} className='ml-2' /> <Down width={22} height={22} className='me-2' />search for something<Down width={22} height={22} className='ms-2' />
</div>)} </div>)}
</SearchLayout> </SearchLayout>
) )

View File

@ -37,7 +37,7 @@ const COLORS = {
commentBg: 'rgba(255, 255, 255, 0.04)', commentBg: 'rgba(255, 255, 255, 0.04)',
clickToContextColor: 'rgba(255, 255, 255, 0.2)', clickToContextColor: 'rgba(255, 255, 255, 0.2)',
color: '#f8f9fa', color: '#f8f9fa',
brandColor: 'var(--primary)', brandColor: 'var(--bs-primary)',
grey: '#969696', grey: '#969696',
link: '#2e99d1', link: '#2e99d1',
toolbarActive: 'rgba(255, 255, 255, 0.10)', toolbarActive: 'rgba(255, 255, 255, 0.10)',

View File

@ -1,3 +1,13 @@
$primary: #FADA5E;
$secondary: #F6911D;
$danger: #c03221;
$info: #007cbe;
$success: #5c8001;
$twitter: #1da1f2;
$boost: #8c25f4;
$light: #f8f9fa;
$dark: #212529;
$theme-colors: ( $theme-colors: (
"primary" : #FADA5E, "primary" : #FADA5E,
"secondary" : #F6911D, "secondary" : #F6911D,
@ -6,9 +16,11 @@ $theme-colors: (
"success" : #5c8001, "success" : #5c8001,
"twitter" : #1da1f2, "twitter" : #1da1f2,
"boost" : #8c25f4, "boost" : #8c25f4,
"light": #f8f9fa,
"dark": #212529,
"grey" : #e9ecef, "grey" : #e9ecef,
"grey-medium" : #d2d2d2, "grey-medium" : #d2d2d2,
"grey-darkmode": #8c8c8c "grey-darkmode": #8c8c8c,
); );
$body-bg: #f5f5f7; $body-bg: #f5f5f7;
@ -20,6 +32,7 @@ $btn-transition: none;
$form-feedback-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M2 9h3v12H2a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1zm5.293-1.293l6.4-6.4a.5.5 0 0 1 .654-.047l.853.64a1.5 1.5 0 0 1 .553 1.57L14.6 8H21a2 2 0 0 1 2 2v2.104a2 2 0 0 1-.15.762l-3.095 7.515a1 1 0 0 1-.925.619H8a1 1 0 0 1-1-1V8.414a1 1 0 0 1 .293-.707z' fill='rgba(92, 128, 1, 1)'/%3E%3C/svg%3E"); $form-feedback-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M2 9h3v12H2a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1zm5.293-1.293l6.4-6.4a.5.5 0 0 1 .654-.047l.853.64a1.5 1.5 0 0 1 .553 1.57L14.6 8H21a2 2 0 0 1 2 2v2.104a2 2 0 0 1-.15.762l-3.095 7.515a1 1 0 0 1-.925.619H8a1 1 0 0 1-1-1V8.414a1 1 0 0 1 .293-.707z' fill='rgba(92, 128, 1, 1)'/%3E%3C/svg%3E");
$form-feedback-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M22 15h-3V3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-5.293 1.293l-6.4 6.4a.5.5 0 0 1-.654.047L8.8 22.1a1.5 1.5 0 0 1-.553-1.57L9.4 16H3a2 2 0 0 1-2-2v-2.104a2 2 0 0 1 .15-.762L4.246 3.62A1 1 0 0 1 5.17 3H16a1 1 0 0 1 1 1v11.586a1 1 0 0 1-.293.707z' fill='rgba(192,50,33,1)'/%3E%3C/svg%3E"); $form-feedback-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='12' height='12'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M22 15h-3V3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zm-5.293 1.293l-6.4 6.4a.5.5 0 0 1-.654.047L8.8 22.1a1.5 1.5 0 0 1-.553-1.57L9.4 16H3a2 2 0 0 1-2-2v-2.104a2 2 0 0 1 .15-.762L4.246 3.62A1 1 0 0 1 5.17 3H16a1 1 0 0 1 1 1v11.586a1 1 0 0 1-.293.707z' fill='rgba(192,50,33,1)'/%3E%3C/svg%3E");
$line-height-base: 1.75; $line-height-base: 1.75;
$input-placeholder-color: #6c757d;
$input-btn-padding-y: .42rem; $input-btn-padding-y: .42rem;
$input-btn-padding-x: .84rem; $input-btn-padding-x: .84rem;
$btn-padding-y: .42rem; $btn-padding-y: .42rem;
@ -33,6 +46,7 @@ $close-text-shadow: none;
$close-color: inherit; $close-color: inherit;
$alert-border-radius: #{33% 2%} / #{11% 74%}; $alert-border-radius: #{33% 2%} / #{11% 74%};
$link-color: #007cbe; $link-color: #007cbe;
$link-decoration: none;
$font-size-base: .9rem; $font-size-base: .9rem;
$enable-responsive-font-sizes: true; $enable-responsive-font-sizes: true;
$link-hover-decoration: none; $link-hover-decoration: none;
@ -42,6 +56,8 @@ $dropdown-link-hover-bg: transparent;
$dropdown-link-active-bg: transparent; $dropdown-link-active-bg: transparent;
$dropdown-link-color: rgba(0, 0, 0, 0.7); $dropdown-link-color: rgba(0, 0, 0, 0.7);
$dropdown-link-hover-color: rgba(0, 0, 0, 0.9); $dropdown-link-hover-color: rgba(0, 0, 0, 0.9);
$dropdown-item-padding-x: 1.5rem;
$modal-inner-padding: 2rem;
$container-max-widths: ( $container-max-widths: (
sm: 540px, sm: 540px,
md: 720px, md: 720px,
@ -51,9 +67,20 @@ $nav-link-padding-y: .1rem;
$nav-tabs-link-active-bg: #fff; $nav-tabs-link-active-bg: #fff;
$nav-tabs-link-hover-border-color: transparent; $nav-tabs-link-hover-border-color: transparent;
$nav-tabs-link-active-border-color: #ced4da #ced4da $nav-tabs-link-active-bg; $nav-tabs-link-active-border-color: #ced4da #ced4da $nav-tabs-link-active-bg;
$form-check-input-checked-color: var(--bs-primary);
$form-check-input-checked-bg-color: var(--bs-primary);
$popover-bg: var(--theme-body);
$form-check-input-checked-color: #000;
$tooltip-bg: #5c8001; $tooltip-bg: #5c8001;
$form-select-indicator-color: #808080;
$form-select-indicator: url("data:image/svg+xml, %3Csvg fill='#{$form-select-indicator-color}' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.0006L7.75732 10.758L9.17154 9.34375L12 12.1722L14.8284 9.34375L16.2426 10.758L12 15.0006Z'%3E%3C/path%3E%3C/svg%3E%0A");
$form-select-bg-position: right .25rem center;
$form-select-bg-size: 1.5rem;
$popover-body-padding-y: .5rem;
$popover-max-width: 320px !default;
$popover-border-color: var(--theme-borderColor);
@import "~bootstrap/scss/bootstrap"; @import '../node_modules/bootstrap/scss/bootstrap.scss';
@media screen and (min-width: 899px) { @media screen and (min-width: 899px) {
@ -135,8 +162,8 @@ mark {
width: 100% !important; width: 100% !important;
} }
.custom-checkbox.custom-control-inline { .btn-twitter, .btn-twitter:hover, .btn-twitter:active {
margin-right: .5rem; color: #ffffff !important;
} }
.table { .table {
@ -178,7 +205,7 @@ a:hover {
border-bottom-color: var(--theme-inputBg); border-bottom-color: var(--theme-inputBg);
} }
select.custom-select, select,
div[contenteditable], div[contenteditable],
.form-control { .form-control {
background-color: var(--theme-inputBg); background-color: var(--theme-inputBg);
@ -186,26 +213,38 @@ div[contenteditable],
border-color: var(--theme-borderColor); border-color: var(--theme-borderColor);
} }
.form-group {
margin-bottom: 1rem;
}
select.custom-select {
select.form-select {
background-color: var(--theme-clickToContextColor); background-color: var(--theme-clickToContextColor);
color: var(--theme-dropdownItemColor); color: var(--theme-dropdownItemColor);
font-weight: bold; font-weight: bold;
border: none; border: none;
background-image: url("data:image/svg+xml, %3Csvg fill='%23808080' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.0006L7.75732 10.758L9.17154 9.34375L12 12.1722L14.8284 9.34375L16.2426 10.758L12 15.0006Z'%3E%3C/path%3E%3C/svg%3E%0A"); display: inline-block;
background-repeat: no-repeat; width: 100%;
background-position: right .25rem center; height: calc(1.5em + 0.5rem + 2px);
background-size: 1.5rem; padding: 0.25rem 1.84rem 0.25rem 0.5rem;
font-size: .7875rem;
vertical-align: middle;
border-radius: 0.4rem;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
} }
select.custom-select option { select option {
// fix dropdown items not visible in darkmode on some browsers // fix dropdown items not visible in darkmode on some browsers
color: initial; color: initial;
} }
select.custom-select:focus { select:focus {
border-color: none !important; border-color: none !important;
box-shadow: none !important; box-shadow: none !important;
outline: none !important;
} }
@ -222,6 +261,7 @@ div[contenteditable]:disabled,
.form-control[readonly] { .form-control[readonly] {
background-color: var(--theme-inputDisabledBg); background-color: var(--theme-inputDisabledBg);
border-color: var(--theme-borderColor); border-color: var(--theme-borderColor);
color: var(--theme-color);
opacity: 1; opacity: 1;
} }
@ -296,7 +336,7 @@ div[contenteditable]:disabled,
.dropdown-item.active { .dropdown-item.active {
color: var(--theme-brandColor) !important; color: var(--theme-brandColor) !important;
text-shadow: 0 0 10px var(--primary); text-shadow: 0 0 10px var(--bs-primary);
} }
.dropdown-divider { .dropdown-divider {
@ -358,6 +398,12 @@ footer {
} }
} }
.input-group-text {
background-color: var(--theme-clickToContextColor);
border-color: var(--theme-borderColor);
color: var(--theme-color);
}
textarea.form-control, textarea.form-control,
div[contenteditable] { div[contenteditable] {
line-height: 1rem; line-height: 1rem;
@ -444,12 +490,12 @@ div[contenteditable] {
.dropdown-item.active { .dropdown-item.active {
text-shadow: 0 0 10px var(--primary); text-shadow: 0 0 10px var(--bs-primary);
} }
div[contenteditable]:focus, div[contenteditable]:focus,
.form-control:focus { .form-control:focus {
border-color: var(--primary); border-color: var(--bs-primary);
} }
.btn-secondary, .btn-secondary,
@ -491,11 +537,11 @@ div[contenteditable]:focus,
} }
.fill-secondary { .fill-secondary {
fill: var(--secondary); fill: var(--bs-secondary);
} }
.fill-boost { .fill-boost {
fill: var(--boost); fill: var(--bs-boost);
} }
.fill-white { .fill-white {
@ -503,15 +549,15 @@ div[contenteditable]:focus,
} }
.fill-success { .fill-success {
fill: var(--success); fill: var(--bs-success);
} }
.fill-info { .fill-info {
fill: var(--info); fill: var(--bs-info);
} }
.fill-danger { .fill-danger {
fill: var(--danger); fill: var(--bs-danger);
} }
.fill-theme-color { .fill-theme-color {

View File

@ -18,6 +18,7 @@
align-items: center; align-items: center;
margin-right: -15px; margin-right: -15px;
padding-right: 15px; padding-right: 15px;
font-family: monospace;
} }
.detail { .detail {
@ -31,6 +32,10 @@
text-align: center; text-align: center;
} }
.sats.head {
font-family: inherit;
}
.head { .head {
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;