Compare commits

...

4 Commits

Author SHA1 Message Date
Keyan
3d3dc52cec
Update awards.csv 2024-05-31 10:23:31 -05:00
Tom
0576716c4a
Close related section (#1207)
* Fix issue with closing accordian

* Use onSelect

* Remove change to AccoridanCard

* Fix className typo

* Restore conditional
2024-05-31 10:20:52 -05:00
ekzyis
a7bc757514
Use same button to generate and delete API keys (#1210) 2024-05-30 16:24:18 -05:00
ekzyis
d2a981ca5d
Add bot badge to items created with API key (#1209) 2024-05-30 16:23:07 -05:00
12 changed files with 56 additions and 21 deletions

View File

@ -1311,6 +1311,9 @@ export const createItem = async (parent, { forward, options, ...item }, { me, mo
item.url = removeTracking(item.url) item.url = removeTracking(item.url)
} }
// mark item as created with API key
item.apiKey = me?.apiKey
const uploadIds = uploadIdsFromText(item.text, { models }) const uploadIds = uploadIdsFromText(item.text, { models })
const { totalFees: imgFees } = await imageFeesInfo(uploadIds, { models, me }) const { totalFees: imgFees } = await imageFeesInfo(uploadIds, { models, me })
@ -1433,7 +1436,7 @@ export const SELECT =
"Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes", "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes",
"Item"."weightedDownVotes", "Item".freebie, "Item".bio, "Item"."otsHash", "Item"."bountyPaidTo", "Item"."weightedDownVotes", "Item".freebie, "Item".bio, "Item"."otsHash", "Item"."bountyPaidTo",
ltree2text("Item"."path") AS "path", "Item"."weightedComments", "Item"."imgproxyUrls", "Item".outlawed, ltree2text("Item"."path") AS "path", "Item"."weightedComments", "Item"."imgproxyUrls", "Item".outlawed,
"Item"."pollExpiresAt"` "Item"."pollExpiresAt", "Item"."apiKey"`
function topOrderByWeightedSats (me, models) { function topOrderByWeightedSats (me, models) {
return `ORDER BY ${orderByNumerator(models)} DESC NULLS LAST, "Item".id DESC` return `ORDER BY ${orderByNumerator(models)} DESC NULLS LAST, "Item".id DESC`

View File

@ -125,6 +125,7 @@ export default gql`
forwards: [ItemForward] forwards: [ItemForward]
imgproxyUrls: JSONObject imgproxyUrls: JSONObject
rel: String rel: String
apiKey: Boolean
} }
input ItemForwardInput { input ItemForwardInput {

View File

@ -104,3 +104,4 @@ benalleng,helpfulness,#1191,#134,medium,,,did most of this before,100k,benalleng
cointastical,issue,#1191,#134,medium,,,,22k,cointastical@stacker.news,2024-05-28 cointastical,issue,#1191,#134,medium,,,,22k,cointastical@stacker.news,2024-05-28
kravhen,pr,#1198,#1180,good-first-issue,,,required linting,18k,nichro@getalby.com,2024-05-28 kravhen,pr,#1198,#1180,good-first-issue,,,required linting,18k,nichro@getalby.com,2024-05-28
OneOneSeven117,issue,#1198,#1180,good-first-issue,,,required linting,2k,OneOneSeven@stacker.news,2024-05-28 OneOneSeven117,issue,#1198,#1180,good-first-issue,,,required linting,2k,OneOneSeven@stacker.news,2024-05-28
tsmith123,pr,#1207,#837,easy,high,1,,180k,stickymarch60@walletofsatoshi.com,2024-05-31

1 name type pr id issue ids difficulty priority changes requested notes amount receive method date paid
104 cointastical issue #1191 #134 medium 22k cointastical@stacker.news 2024-05-28
105 kravhen pr #1198 #1180 good-first-issue required linting 18k nichro@getalby.com 2024-05-28
106 OneOneSeven117 issue #1198 #1180 good-first-issue required linting 2k OneOneSeven@stacker.news 2024-05-28
107 tsmith123 pr #1207 #837 easy high 1 180k stickymarch60@walletofsatoshi.com 2024-05-31

View File

@ -3,7 +3,9 @@ import AccordionContext from 'react-bootstrap/AccordionContext'
import { useAccordionButton } from 'react-bootstrap/AccordionButton' 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 { useContext, useEffect } from 'react' import { useContext, useEffect, useState } from 'react'
const KEY_ID = '0'
function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', eventKey, show }) { function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', eventKey, show }) {
const { activeEventKey } = useContext(AccordionContext) const { activeEventKey } = useContext(AccordionContext)
@ -29,10 +31,20 @@ function ContextAwareToggle ({ children, headerColor = 'var(--theme-grey)', even
} }
export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) { export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) {
const [activeKey, setActiveKey] = useState()
useEffect(() => {
setActiveKey(show ? KEY_ID : null)
}, [show])
const handleOnSelect = () => {
setActiveKey(activeKey === KEY_ID ? null : KEY_ID)
}
return ( return (
<Accordion defaultActiveKey={show ? '0' : undefined}> <Accordion defaultActiveKey={activeKey} activeKey={activeKey} onSelect={handleOnSelect}>
<ContextAwareToggle show={show} eventKey='0'><div style={{ color: headerColor }}>{header}</div></ContextAwareToggle> <ContextAwareToggle show={show} eventKey={KEY_ID}><div style={{ color: headerColor }}>{header}</div></ContextAwareToggle>
<Accordion.Collapse eventKey='0' className='mt-2'> <Accordion.Collapse eventKey={KEY_ID} className='mt-2'>
<div>{body}</div> <div>{body}</div>
</Accordion.Collapse> </Accordion.Collapse>
</Accordion> </Accordion>

View File

@ -176,7 +176,14 @@ function TopLevelItem ({ item, noReply, ...props }) {
</article> </article>
{!noReply && {!noReply &&
<> <>
<Reply item={item} replyOpen placeholder={item.ncomments > 3 ? 'fractions of a penny for your thoughts?' : 'early comments get more zaps'} onCancelQuote={cancelQuote} onQuoteReply={quoteReply} quote={quote} /> <Reply
item={item}
replyOpen
placeholder={item.ncomments > 3 ? 'fractions of a penny for your thoughts?' : 'early comments get more zaps'}
onCancelQuote={cancelQuote}
onQuoteReply={quoteReply}
quote={quote}
/>
{ {
// Don't show related items for Saloon items (position is set but no subName) // Don't show related items for Saloon items (position is set but no subName)
(!item.position && item.subName) && (!item.position && item.subName) &&

View File

@ -136,6 +136,9 @@ export default function ItemInfo ({
{' '}<Badge className={styles.newComment} bg={null}>freebie</Badge> {' '}<Badge className={styles.newComment} bg={null}>freebie</Badge>
</Link> </Link>
)} )}
{(item.apiKey &&
<>{' '}<Badge className={styles.newComment} bg={null}>bot</Badge></>
)}
{extraBadges} {extraBadges}
{canEdit && !item.deletedAt && {canEdit && !item.deletedAt &&
<> <>

View File

@ -31,7 +31,15 @@ export function ReplyOnAnotherPage ({ item }) {
) )
} }
export default forwardRef(function Reply ({ item, onSuccess, replyOpen, children, placeholder, onQuoteReply, onCancelQuote, quote }, ref) { export default forwardRef(function Reply ({
item,
replyOpen,
children,
placeholder,
onQuoteReply,
onCancelQuote,
quote
}, ref) {
const [reply, setReply] = useState(replyOpen || quote) const [reply, setReply] = useState(replyOpen || quote)
const me = useMe() const me = useMe()
const parentId = item.id const parentId = item.id

View File

@ -34,6 +34,7 @@ export const COMMENT_FIELDS = gql`
ncomments ncomments
imgproxyUrls imgproxyUrls
rel rel
apiKey
} }
` `

View File

@ -59,6 +59,7 @@ export const ITEM_FIELDS = gql`
mine mine
imgproxyUrls imgproxyUrls
rel rel
apiKey
}` }`
export const ITEM_FULL_FIELDS = gql` export const ITEM_FULL_FIELDS = gql`

View File

@ -28,7 +28,6 @@ import { useServiceWorkerLogger } from '@/components/logger'
import { useMe } from '@/components/me' import { useMe } from '@/components/me'
import { INVOICE_RETENTION_DAYS, ZAP_UNDO_DELAY_MS } from '@/lib/constants' import { INVOICE_RETENTION_DAYS, ZAP_UNDO_DELAY_MS } from '@/lib/constants'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import DeleteIcon from '@/svgs/delete-bin-line.svg'
import { useField } from 'formik' import { useField } from 'formik'
import styles from './settings.module.css' import styles from './settings.module.css'
@ -859,22 +858,25 @@ I estimate that I will call the GraphQL API this many times (rough estimate is f
// link to DM with ek on SimpleX // link to DM with ek on SimpleX
const simplexLink = 'https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2F6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE%3D%40smp10.simplex.im%2FxNnPk9DkTbQJ6NckWom9mi5vheo_VPLm%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAnFUiU0M8jS1JY34LxUoPr7mdJlFZwf3pFkjRrhprdQs%253D%26srv%3Drb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion' const simplexLink = 'https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2F6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE%3D%40smp10.simplex.im%2FxNnPk9DkTbQJ6NckWom9mi5vheo_VPLm%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAnFUiU0M8jS1JY34LxUoPr7mdJlFZwf3pFkjRrhprdQs%253D%26srv%3Drb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion'
const disabled = !enabled || apiKey
return ( return (
<> <>
<div className='form-label mt-3'>api key</div> <div className='form-label mt-3'>api key</div>
<div className='mt-2 d-flex align-items-center'> <div className='mt-2 d-flex align-items-center'>
<OverlayTrigger <OverlayTrigger
placement='bottom' placement='bottom'
overlay={disabled ? <Tooltip>{apiKey ? 'you can have only one API key at a time' : 'request access to API keys in ~meta'}</Tooltip> : <></>} overlay={!enabled ? <Tooltip>{apiKey ? 'you can have only one API key at a time' : 'request access to API keys in ~meta'}</Tooltip> : <></>}
trigger={['hover', 'focus']} trigger={['hover', 'focus']}
> >
<div> <div>
<Button <Button
disabled={disabled} disabled={!enabled}
variant='secondary' variant={apiKey ? 'danger' : 'secondary'}
onClick={async () => { onClick={async () => {
if (apiKey) {
showModal((onClose) => <ApiKeyDeleteObstacle onClose={onClose} />)
return
}
try { try {
const { data } = await generateApiKey({ variables: { id: me.id } }) const { data } = await generateApiKey({ variables: { id: me.id } })
const { generateApiKey: apiKey } = data const { generateApiKey: apiKey } = data
@ -884,17 +886,10 @@ I estimate that I will call the GraphQL API this many times (rough estimate is f
toaster.danger('error generating api key') toaster.danger('error generating api key')
} }
}} }}
>Generate API key >{apiKey ? 'Delete' : 'Generate'} API key
</Button> </Button>
</div> </div>
</OverlayTrigger> </OverlayTrigger>
{apiKey &&
<DeleteIcon
style={{ cursor: 'pointer' }} className='fill-danger mx-1' width={24} height={24}
onClick={async () => {
showModal((onClose) => <ApiKeyDeleteObstacle onClose={onClose} />)
}}
/>}
<Info> <Info>
<ul className='fw-bold'> <ul className='fw-bold'>
<li>use API keys with our <Link target='_blank' href='/api/graphql'>GraphQL API</Link> for authentication</li> <li>use API keys with our <Link target='_blank' href='/api/graphql'>GraphQL API</Link> for authentication</li>

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Item" ADD COLUMN "apiKey" BOOLEAN NOT NULL DEFAULT false;

View File

@ -419,6 +419,7 @@ model Item {
ItemUpload ItemUpload[] ItemUpload ItemUpload[]
uploadId Int? uploadId Int?
outlawed Boolean @default(false) outlawed Boolean @default(false)
apiKey Boolean @default(false)
pollExpiresAt DateTime? pollExpiresAt DateTime?
Ancestors Reply[] @relation("AncestorReplyItem") Ancestors Reply[] @relation("AncestorReplyItem")
Replies Reply[] Replies Reply[]