auto-populate link title

This commit is contained in:
keyan 2021-08-22 10:25:17 -05:00
parent f523088531
commit ce0e3dac45
6 changed files with 67 additions and 4 deletions

View File

@ -2,6 +2,8 @@ import { UserInputError, AuthenticationError } from 'apollo-server-micro'
import { ensureProtocol } from '../../lib/url'
import serialize from './serial'
import { decodeCursor, LIMIT, nextCursorEncoded } from './cursor'
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
import domino from 'domino'
async function comments (models, id) {
const flat = await models.$queryRaw(`
@ -96,6 +98,17 @@ export default {
FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL
ORDER BY created_at DESC`, Number(userId))
},
pageTitle: async (parent, { url }, { models }) => {
try {
const response = await fetch(ensureProtocol(url), { redirect: 'follow' })
const html = await response.text()
const doc = domino.createWindow(html).document
const metadata = getMetadata(doc, url, { title: metadataRuleSets.title })
return metadata?.title
} catch (e) {
return null
}
}
},

View File

@ -6,6 +6,7 @@ export default gql`
moreFlatComments(cursor: String, userId: ID): Comments
item(id: ID!): Item
userComments(userId: ID!): [Item!]
pageTitle(url: String!): String
}
extend type Mutation {

View File

@ -108,9 +108,16 @@ function FormGroup ({ className, label, children }) {
)
}
function InputInner ({ prepend, append, hint, showValid, ...props }) {
const [field, meta] = props.readOnly ? [{}, {}] : useField(props)
function InputInner ({ prepend, append, hint, showValid, onChange, overrideValue, ...props }) {
const [field, meta, helpers] = props.readOnly ? [{}, {}, {}] : useField(props)
const formik = props.readOnly ? null : useFormikContext()
useEffect(() => {
if (overrideValue) {
helpers.setValue(overrideValue)
}
}, [overrideValue])
return (
<>
<InputGroup hasValidation>
@ -126,6 +133,12 @@ function InputInner ({ prepend, append, hint, showValid, ...props }) {
}
}}
{...field} {...props}
onChange={(e) => {
field.onChange(e)
if (onChange) {
onChange(formik, e)
}
}}
isInvalid={meta.touched && meta.error}
isValid={showValid && meta.touched && !meta.error}
/>

View File

@ -1,7 +1,7 @@
import { Form, Input, SubmitButton } from '../components/form'
import { useRouter } from 'next/router'
import * as Yup from 'yup'
import { gql, useMutation } from '@apollo/client'
import { gql, useLazyQuery, useMutation } from '@apollo/client'
import { ensureProtocol } from '../lib/url'
import ActionTooltip from '../components/action-tooltip'
import Countdown from './countdown'
@ -24,6 +24,10 @@ export const LinkSchema = Yup.object({
})
export function LinkForm ({ item, editThreshold }) {
const [getPageTitle, { data }] = useLazyQuery(gql`
query PageTitle($url: String!) {
pageTitle(url: $url)
}`)
const router = useRouter()
const [createLink] = useMutation(
gql`
@ -81,6 +85,7 @@ export function LinkForm ({ item, editThreshold }) {
<Input
label='title'
name='title'
overrideValue={data?.pageTitle}
required
autoFocus
/>
@ -91,6 +96,13 @@ export function LinkForm ({ item, editThreshold }) {
hint={editThreshold
? <Countdown date={editThreshold} />
: null}
onChange={async (formik, e) => {
if ((/^ *$/).test(formik?.values.title)) {
getPageTitle({
variables: { url: e.target.value }
})
}
}}
/>
<ActionTooltip>
<SubmitButton variant='secondary' className='mt-2'>{item ? 'save' : 'post'}</SubmitButton>

22
package-lock.json generated
View File

@ -15,6 +15,7 @@
"bech32": "^2.0.0",
"bootstrap": "^4.6.0",
"clipboard-copy": "^4.0.1",
"domino": "^2.1.6",
"formik": "^2.2.6",
"graphql": "^15.5.0",
"ln-service": "^51.7.0",
@ -23,6 +24,7 @@
"next-auth": "^3.13.3",
"next-plausible": "^2.0.0",
"next-seo": "^4.24.0",
"page-metadata-parser": "^1.1.4",
"pageres": "^6.2.3",
"prisma": "^2.25.0",
"qrcode.react": "^1.0.1",
@ -3287,6 +3289,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domino": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"node_modules/domutils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
@ -7660,6 +7667,11 @@
"node": ">=4"
}
},
"node_modules/page-metadata-parser": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/page-metadata-parser/-/page-metadata-parser-1.1.4.tgz",
"integrity": "sha512-TbPNw7GddbHs4c2DyYinFvh51BVsaMfdrweeylzGlg8qeuzALGxq2NF+6jbmeKc7DnU2BZRDOuWNnEjDwUSqRQ=="
},
"node_modules/pageres": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz",
@ -14030,6 +14042,11 @@
"domelementtype": "^2.2.0"
}
},
"domino": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"domutils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
@ -17312,6 +17329,11 @@
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"page-metadata-parser": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/page-metadata-parser/-/page-metadata-parser-1.1.4.tgz",
"integrity": "sha512-TbPNw7GddbHs4c2DyYinFvh51BVsaMfdrweeylzGlg8qeuzALGxq2NF+6jbmeKc7DnU2BZRDOuWNnEjDwUSqRQ=="
},
"pageres": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz",

View File

@ -17,6 +17,7 @@
"bech32": "^2.0.0",
"bootstrap": "^4.6.0",
"clipboard-copy": "^4.0.1",
"domino": "^2.1.6",
"formik": "^2.2.6",
"graphql": "^15.5.0",
"ln-service": "^51.7.0",
@ -25,6 +26,7 @@
"next-auth": "^3.13.3",
"next-plausible": "^2.0.0",
"next-seo": "^4.24.0",
"page-metadata-parser": "^1.1.4",
"pageres": "^6.2.3",
"prisma": "^2.25.0",
"qrcode.react": "^1.0.1",
@ -63,4 +65,4 @@
"eslint-plugin-compat": "^3.9.0",
"standard": "^16.0.3"
}
}
}