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 { ensureProtocol } from '../../lib/url'
import serialize from './serial' import serialize from './serial'
import { decodeCursor, LIMIT, nextCursorEncoded } from './cursor' import { decodeCursor, LIMIT, nextCursorEncoded } from './cursor'
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
import domino from 'domino'
async function comments (models, id) { async function comments (models, id) {
const flat = await models.$queryRaw(` const flat = await models.$queryRaw(`
@ -96,6 +98,17 @@ export default {
FROM "Item" FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL WHERE "userId" = $1 AND "parentId" IS NOT NULL
ORDER BY created_at DESC`, Number(userId)) 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 moreFlatComments(cursor: String, userId: ID): Comments
item(id: ID!): Item item(id: ID!): Item
userComments(userId: ID!): [Item!] userComments(userId: ID!): [Item!]
pageTitle(url: String!): String
} }
extend type Mutation { extend type Mutation {

View File

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

View File

@ -1,7 +1,7 @@
import { Form, Input, SubmitButton } from '../components/form' import { Form, Input, SubmitButton } from '../components/form'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import * as Yup from 'yup' import * as Yup from 'yup'
import { gql, useMutation } from '@apollo/client' import { gql, useLazyQuery, useMutation } from '@apollo/client'
import { ensureProtocol } from '../lib/url' import { ensureProtocol } from '../lib/url'
import ActionTooltip from '../components/action-tooltip' import ActionTooltip from '../components/action-tooltip'
import Countdown from './countdown' import Countdown from './countdown'
@ -24,6 +24,10 @@ export const LinkSchema = Yup.object({
}) })
export function LinkForm ({ item, editThreshold }) { export function LinkForm ({ item, editThreshold }) {
const [getPageTitle, { data }] = useLazyQuery(gql`
query PageTitle($url: String!) {
pageTitle(url: $url)
}`)
const router = useRouter() const router = useRouter()
const [createLink] = useMutation( const [createLink] = useMutation(
gql` gql`
@ -81,6 +85,7 @@ export function LinkForm ({ item, editThreshold }) {
<Input <Input
label='title' label='title'
name='title' name='title'
overrideValue={data?.pageTitle}
required required
autoFocus autoFocus
/> />
@ -91,6 +96,13 @@ export function LinkForm ({ item, editThreshold }) {
hint={editThreshold hint={editThreshold
? <Countdown date={editThreshold} /> ? <Countdown date={editThreshold} />
: null} : null}
onChange={async (formik, e) => {
if ((/^ *$/).test(formik?.values.title)) {
getPageTitle({
variables: { url: e.target.value }
})
}
}}
/> />
<ActionTooltip> <ActionTooltip>
<SubmitButton variant='secondary' className='mt-2'>{item ? 'save' : 'post'}</SubmitButton> <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", "bech32": "^2.0.0",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
"domino": "^2.1.6",
"formik": "^2.2.6", "formik": "^2.2.6",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"ln-service": "^51.7.0", "ln-service": "^51.7.0",
@ -23,6 +24,7 @@
"next-auth": "^3.13.3", "next-auth": "^3.13.3",
"next-plausible": "^2.0.0", "next-plausible": "^2.0.0",
"next-seo": "^4.24.0", "next-seo": "^4.24.0",
"page-metadata-parser": "^1.1.4",
"pageres": "^6.2.3", "pageres": "^6.2.3",
"prisma": "^2.25.0", "prisma": "^2.25.0",
"qrcode.react": "^1.0.1", "qrcode.react": "^1.0.1",
@ -3287,6 +3289,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "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": { "node_modules/domutils": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
@ -7660,6 +7667,11 @@
"node": ">=4" "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": { "node_modules/pageres": {
"version": "6.2.3", "version": "6.2.3",
"resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz", "resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz",
@ -14030,6 +14042,11 @@
"domelementtype": "^2.2.0" "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": { "domutils": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
@ -17312,6 +17329,11 @@
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true "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": { "pageres": {
"version": "6.2.3", "version": "6.2.3",
"resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz", "resolved": "https://registry.npmjs.org/pageres/-/pageres-6.2.3.tgz",

View File

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