user suggestions on forward
This commit is contained in:
parent
016e357ebd
commit
a5d1d8dc0f
|
@ -134,6 +134,10 @@ export default {
|
|||
cursor: users.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||
users
|
||||
}
|
||||
},
|
||||
searchUsers: async (parent, { name }, { models }) => {
|
||||
return await models.$queryRaw`
|
||||
SELECT * FROM users where id > 615 AND SIMILARITY(name, ${name}) > .1 ORDER BY SIMILARITY(name, ${name}) DESC LIMIT 5`
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ export default gql`
|
|||
users: [User!]
|
||||
nameAvailable(name: String!): Boolean!
|
||||
topUsers(cursor: String, within: String!, userType: String!): TopUsers
|
||||
searchUsers(name: String!): [User!]!
|
||||
}
|
||||
|
||||
type Users {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import AccordianItem from './accordian-item'
|
||||
import * as Yup from 'yup'
|
||||
import { Input } from './form'
|
||||
import { Input, InputUserSuggest } from './form'
|
||||
import { InputGroup } from 'react-bootstrap'
|
||||
import { BOOST_MIN } from '../lib/constants'
|
||||
import { NAME_QUERY } from '../fragments/users'
|
||||
|
@ -69,7 +69,7 @@ export default function AdvPostForm ({ edit }) {
|
|||
hint={<span className='text-muted'>ranks posts higher temporarily based on the amount</span>}
|
||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||
/>
|
||||
<Input
|
||||
<InputUserSuggest
|
||||
label={<>forward sats to</>}
|
||||
name='forward'
|
||||
hint={<span className='text-muted'>100% of sats will be sent to this user</span>}
|
||||
|
|
|
@ -41,6 +41,7 @@ export function DiscussionForm ({
|
|||
initial={{
|
||||
title: item?.title || '',
|
||||
text: item?.text || '',
|
||||
suggest: '',
|
||||
...AdvPostInitial({ forward: item?.fwdUser?.name })
|
||||
}}
|
||||
schema={DiscussionSchema}
|
||||
|
|
|
@ -6,13 +6,15 @@ import { Formik, Form as FormikForm, useFormikContext, useField, FieldArray } fr
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import copy from 'clipboard-copy'
|
||||
import Thumb from '../svgs/thumb-up-fill.svg'
|
||||
import { Col, Nav } from 'react-bootstrap'
|
||||
import { Col, Dropdown, Nav } from 'react-bootstrap'
|
||||
import Markdown from '../svgs/markdown-line.svg'
|
||||
import styles from './form.module.css'
|
||||
import Text from '../components/text'
|
||||
import AddIcon from '../svgs/add-fill.svg'
|
||||
import { mdHas } from '../lib/md'
|
||||
import CloseIcon from '../svgs/close-line.svg'
|
||||
import { useLazyQuery } from '@apollo/client'
|
||||
import { USER_SEARCH } from '../fragments/users'
|
||||
|
||||
export function SubmitButton ({
|
||||
children, variant, value, onClick, ...props
|
||||
|
@ -130,7 +132,7 @@ function FormGroup ({ className, label, children }) {
|
|||
|
||||
function InputInner ({
|
||||
prepend, append, hint, showValid, onChange, overrideValue,
|
||||
innerRef, storageKeyPrefix, noForm, clear, ...props
|
||||
innerRef, storageKeyPrefix, noForm, clear, onKeyDown, ...props
|
||||
}) {
|
||||
const [field, meta, helpers] = noForm ? [{}, {}, {}] : useField(props)
|
||||
const formik = noForm ? null : useFormikContext()
|
||||
|
@ -168,6 +170,7 @@ function InputInner ({
|
|||
if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) {
|
||||
formik?.submitForm()
|
||||
}
|
||||
if (onKeyDown) onKeyDown(e)
|
||||
}}
|
||||
ref={innerRef}
|
||||
{...field} {...props}
|
||||
|
@ -215,6 +218,76 @@ function InputInner ({
|
|||
)
|
||||
}
|
||||
|
||||
export function InputUserSuggest ({ label, groupClassName, ...props }) {
|
||||
const [getSuggestions] = useLazyQuery(USER_SEARCH, {
|
||||
fetchPolicy: 'network-only',
|
||||
onCompleted: data => {
|
||||
setSuggestions({ array: data.searchUsers, index: 0 })
|
||||
}
|
||||
})
|
||||
|
||||
const INITIAL_SUGGESTIONS = { array: [], index: 0 }
|
||||
const [suggestions, setSuggestions] = useState(INITIAL_SUGGESTIONS)
|
||||
const [ovalue, setOValue] = useState()
|
||||
|
||||
return (
|
||||
<FormGroup label={label} className={groupClassName}>
|
||||
<InputInner
|
||||
{...props}
|
||||
autoComplete='off'
|
||||
onChange={(_, e) => getSuggestions({ variables: { name: e.target.value } })}
|
||||
overrideValue={ovalue}
|
||||
onKeyDown={(e) => {
|
||||
switch (e.code) {
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
setSuggestions(
|
||||
{
|
||||
...suggestions,
|
||||
index: Math.max(suggestions.index - 1, 0)
|
||||
})
|
||||
break
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
setSuggestions(
|
||||
{
|
||||
...suggestions,
|
||||
index: Math.min(suggestions.index + 1, suggestions.array.length - 1)
|
||||
})
|
||||
break
|
||||
case 'Enter':
|
||||
e.preventDefault()
|
||||
setOValue(suggestions.array[suggestions.index].name)
|
||||
setSuggestions(INITIAL_SUGGESTIONS)
|
||||
break
|
||||
case 'Escape':
|
||||
e.preventDefault()
|
||||
setSuggestions(INITIAL_SUGGESTIONS)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Dropdown show={suggestions.array.length > 0}>
|
||||
<Dropdown.Menu className={styles.suggestionsMenu}>
|
||||
{suggestions.array.map((v, i) =>
|
||||
<Dropdown.Item
|
||||
key={v.name}
|
||||
active={suggestions.index === i}
|
||||
onClick={() => {
|
||||
setOValue(v.name)
|
||||
setSuggestions(INITIAL_SUGGESTIONS)
|
||||
}}
|
||||
>
|
||||
{v.name}
|
||||
</Dropdown.Item>)}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
|
||||
export function Input ({ label, groupClassName, ...props }) {
|
||||
return (
|
||||
<FormGroup label={label} className={groupClassName}>
|
||||
|
|
|
@ -22,4 +22,10 @@
|
|||
|
||||
.clearButton.isInvalid {
|
||||
border-color: #c03221;
|
||||
}
|
||||
|
||||
/* https://github.com/react-bootstrap/react-bootstrap/issues/5475 */
|
||||
.suggestionsMenu {
|
||||
opacity: 1 !important;
|
||||
pointer-events: unset !important;
|
||||
}
|
|
@ -50,7 +50,8 @@ export default function Search ({ sub }) {
|
|||
required
|
||||
autoFocus={showSearch && !atBottom}
|
||||
groupClassName='mr-3 mb-0 flex-grow-1'
|
||||
className='w-100'
|
||||
className='flex-grow-1'
|
||||
clear
|
||||
onChange={async (formik, e) => {
|
||||
setSearching(true)
|
||||
setQ(e.target.value?.trim())
|
||||
|
|
|
@ -89,6 +89,14 @@ gql`
|
|||
}
|
||||
`
|
||||
|
||||
export const USER_SEARCH =
|
||||
gql`
|
||||
query searchUsers($name: String!) {
|
||||
searchUsers(name: $name) {
|
||||
name
|
||||
}
|
||||
}`
|
||||
|
||||
export const USER_FIELDS = gql`
|
||||
${ITEM_FIELDS}
|
||||
fragment UserFields on User {
|
||||
|
|
|
@ -87,4 +87,4 @@
|
|||
"eslint-plugin-compat": "^3.9.0",
|
||||
"standard": "^16.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
CREATE EXTENSION pg_trgm;
|
Loading…
Reference in New Issue