Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions api/resolvers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,28 @@ export default {
if (id) id = Number(id)
return await models.user.findUnique({ where: { id, name } })
},
userByMention: async (parent, { name, itemId }, { models }) => {
let user = null
if (itemId) {
const mentions = await models.mention.findMany({
where: {
itemId: Number(itemId)
},
include: {
user: true,
item: true
}
})
const matchingMention = mentions.find(mention => mention.user?.name === name)
if (matchingMention) {
user = matchingMention.user
}
}
if (!user) {
user = await models.user.findUnique({ where: { name } })
}
return user
},
users: async (parent, args, { models }) =>
await models.user.findMany(),
nameAvailable: async (parent, { name }, { models, me }) => {
Expand Down
1 change: 1 addition & 0 deletions api/typeDefs/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default gql`
me: User
settings: User
user(id: ID, name: String): User
userByMention(name: String!, itemId: ID): User
users: [User!]
nameAvailable(name: String!): Boolean!
topUsers(cursor: String, when: String, from: String, to: String, by: String, limit: Limit! = ${LIMIT}): UsersNullable!
Expand Down
22 changes: 12 additions & 10 deletions components/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, child
table: Table,
p: P,
code: Code,
mention: Mention,
mention: (props) => <Mention {...props} itemId={itemId} />,
sub: Sub,
item: Item,
footnote: Footnote,
Expand All @@ -147,7 +147,7 @@ export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, child
},
img: TextMediaOrLink,
embed: (props) => <Embed {...props} topLevel={topLevel} />
}), [outlawed, rel, TextMediaOrLink, topLevel])
}), [outlawed, rel, TextMediaOrLink, topLevel, itemId])

const carousel = useCarousel()

Expand Down Expand Up @@ -192,15 +192,17 @@ export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, child
)
}, isEqual)

function Mention ({ children, node, href, name, id }) {
function Mention ({ children, node, href, name, id, itemId }) {
return (
<UserPopover name={name}>
<Link
id={id}
href={href}
>
{children}
</Link>
<UserPopover name={name} itemId={itemId}>
{({ user }) => (
<Link
id={id}
href={user ? `/${user.name}` : href}
>
{children}
</Link>
)}
</UserPopover>
)
}
Expand Down
33 changes: 25 additions & 8 deletions components/user-popover.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { USER } from '@/fragments/users'
import { USER, USER_BY_MENTION } from '@/fragments/users'
import errorStyles from '@/styles/error.module.css'
import { useLazyQuery } from '@apollo/client'
import { useLazyQuery, useQuery } from '@apollo/client'
import classNames from 'classnames'
import Link from 'next/link'
import HoverablePopover from './hoverable-popover'
Expand All @@ -22,7 +22,16 @@ function StackingSince ({ since }) {
)
}

export default function UserPopover ({ name, children }) {
export default function UserPopover ({ name, itemId, children }) {
const { data: mentionData, loading: mentionLoading } = useQuery(
USER_BY_MENTION,
{
variables: { name, itemId },
skip: !itemId,
fetchPolicy: 'cache-first'
}
)

const [getUser, { loading, data }] = useLazyQuery(
USER,
{
Expand All @@ -31,17 +40,25 @@ export default function UserPopover ({ name, children }) {
}
)

const resolvedUser = mentionData?.userByMention
const popoverUser = resolvedUser || data?.user
const isLoading = loading || (itemId && mentionLoading && !resolvedUser)
const trigger =
typeof children === 'function'
? children({ user: popoverUser })
: children

return (
<HoverablePopover
onShow={getUser}
trigger={children}
body={!data || loading
trigger={trigger}
body={isLoading
? <UserSkeleton />
: !data.user
: !popoverUser
? <h1 className={classNames(errorStyles.status, errorStyles.describe)}>USER NOT FOUND</h1>
: (
<UserBase user={data.user} className='mb-0 pb-0'>
<StackingSince since={data.user.since} />
<UserBase user={popoverUser} className='mb-0 pb-0'>
<StackingSince since={popoverUser.since} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: UserPopover Loading and Display Logic Errors

The UserPopover has issues with its loading and display logic. When itemId is present, the loading skeleton unnecessarily flashes over available user data. When itemId is absent, "USER NOT FOUND" may briefly appear instead of a skeleton before the lazy query. Also, the trigger function receives resolvedUser instead of popoverUser, potentially causing incorrect link generation.

Fix in Cursor Fix in Web

</UserBase>
)}
/>
Expand Down
8 changes: 8 additions & 0 deletions fragments/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ export const USER = gql`
}
}`

export const USER_BY_MENTION = gql`
${USER_FIELDS}
query UserByMention($name: String!, $itemId: ID) {
userByMention(name: $name, itemId: $itemId) {
...UserFields
}
}`

export const USER_WITH_ITEMS = gql`
${USER_FIELDS}
${ITEM_FIELDS}
Expand Down