Compare commits
4 Commits
050122c665
...
3d3dc52cec
Author | SHA1 | Date | |
---|---|---|---|
|
3d3dc52cec | ||
|
0576716c4a | ||
|
a7bc757514 | ||
|
d2a981ca5d |
@ -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`
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
@ -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) &&
|
||||||
|
@ -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 &&
|
||||||
<>
|
<>
|
||||||
|
@ -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
|
||||||
|
@ -34,6 +34,7 @@ export const COMMENT_FIELDS = gql`
|
|||||||
ncomments
|
ncomments
|
||||||
imgproxyUrls
|
imgproxyUrls
|
||||||
rel
|
rel
|
||||||
|
apiKey
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -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`
|
||||||
|
@ -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>
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Item" ADD COLUMN "apiKey" BOOLEAN NOT NULL DEFAULT false;
|
@ -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[]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user