Show founded territories on profile (#868)
* add nterritories field to User * add userSubs query * show territories tab on user profiles hide the tab if user has 0 territories, except when the viewer navigated directly to the user's territories page * add USER_WITH_SUBS query for user territories page * add user territories page
This commit is contained in:
parent
6f8b6c36d8
commit
b0bf7add34
|
@ -151,6 +151,49 @@ export default {
|
|||
OFFSET $3
|
||||
LIMIT $4`, ...range, decodedCursor.offset, limit)
|
||||
|
||||
return {
|
||||
cursor: subs.length === limit ? nextCursorEncoded(decodedCursor, limit) : null,
|
||||
subs
|
||||
}
|
||||
},
|
||||
userSubs: async (_parent, { name, cursor, when, by, from, to, limit = LIMIT }, { models }) => {
|
||||
if (!name) {
|
||||
throw new GraphQLError('must supply user name', { extensions: { code: 'BAD_INPUT' } })
|
||||
}
|
||||
|
||||
const user = await models.user.findUnique({ where: { name } })
|
||||
if (!user) {
|
||||
throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } })
|
||||
}
|
||||
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
const range = whenRange(when, from, to || decodeCursor.time)
|
||||
|
||||
let column
|
||||
switch (by) {
|
||||
case 'revenue': column = 'revenue'; break
|
||||
case 'spent': column = 'spent'; break
|
||||
case 'posts': column = 'nposts'; break
|
||||
case 'comments': column = 'ncomments'; break
|
||||
default: column = 'stacked'; break
|
||||
}
|
||||
|
||||
const subs = await models.$queryRawUnsafe(`
|
||||
SELECT "Sub".*,
|
||||
COALESCE(floor(sum(msats_revenue)/1000), 0) as revenue,
|
||||
COALESCE(floor(sum(msats_stacked)/1000), 0) as stacked,
|
||||
COALESCE(floor(sum(msats_spent)/1000), 0) as spent,
|
||||
COALESCE(sum(posts), 0) as nposts,
|
||||
COALESCE(sum(comments), 0) as ncomments
|
||||
FROM ${subViewGroup(range)} ss
|
||||
JOIN "Sub" on "Sub".name = ss.sub_name
|
||||
WHERE "Sub"."userId" = $3
|
||||
AND "Sub".status = 'ACTIVE'
|
||||
GROUP BY "Sub".name
|
||||
ORDER BY ${column} DESC NULLS LAST, "Sub".created_at ASC
|
||||
OFFSET $4
|
||||
LIMIT $5`, ...range, user.id, decodedCursor.offset, limit)
|
||||
|
||||
return {
|
||||
cursor: subs.length === limit ? nextCursorEncoded(decodedCursor, limit) : null,
|
||||
subs
|
||||
|
|
|
@ -698,6 +698,23 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
nterritories: async (user, { when, from, to }, { models }) => {
|
||||
if (typeof user.nterritories !== 'undefined') {
|
||||
return user.nterritories
|
||||
}
|
||||
|
||||
const [gte, lte] = whenRange(when, from, to)
|
||||
return await models.sub.count({
|
||||
where: {
|
||||
userId: user.id,
|
||||
status: 'ACTIVE',
|
||||
createdAt: {
|
||||
gte,
|
||||
lte
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
bio: async (user, args, { models, me }) => {
|
||||
return getItem(user, { id: user.bioId }, { models, me })
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export default gql`
|
|||
subLatestPost(name: String!): String
|
||||
subs: [Sub!]!
|
||||
topSubs(cursor: String, when: String, from: String, to: String, by: String, limit: Limit): Subs
|
||||
userSubs(name: String!, cursor: String, when: String, from: String, to: String, by: String, limit: Limit): Subs
|
||||
}
|
||||
|
||||
type Subs {
|
||||
|
|
|
@ -39,6 +39,7 @@ export default gql`
|
|||
name: String
|
||||
nitems(when: String, from: String, to: String): Int!
|
||||
nposts(when: String, from: String, to: String): Int!
|
||||
nterritories(when: String, from: String, to: String): Int!
|
||||
ncomments(when: String, from: String, to: String): Int!
|
||||
bio: Item
|
||||
bioId: Int
|
||||
|
|
|
@ -32,21 +32,25 @@ import TwitterIcon from '../svgs/twitter-fill.svg'
|
|||
export default function UserHeader ({ user }) {
|
||||
const router = useRouter()
|
||||
|
||||
const pathParts = router.asPath.split('/')
|
||||
const activeKey = pathParts[2] === 'territories' ? 'territories' : pathParts.length === 2 ? 'bio' : 'items'
|
||||
const showTerritoriesTab = activeKey === 'territories' || user.nterritories > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderHeader user={user} />
|
||||
<Nav
|
||||
className={styles.nav}
|
||||
activeKey={!!router.asPath.split('/')[2]}
|
||||
activeKey={activeKey}
|
||||
>
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name} passHref legacyBehavior>
|
||||
<Nav.Link eventKey={false}>bio</Nav.Link>
|
||||
<Nav.Link eventKey='bio'>bio</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name + '/all'} passHref legacyBehavior>
|
||||
<Nav.Link eventKey>
|
||||
<Nav.Link eventKey='items'>
|
||||
{numWithUnits(user.nitems, {
|
||||
abbreviate: false,
|
||||
unitSingular: 'item',
|
||||
|
@ -55,6 +59,19 @@ export default function UserHeader ({ user }) {
|
|||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
{showTerritoriesTab && (
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name + '/territories'} passHref legacyBehavior>
|
||||
<Nav.Link eventKey='territories'>
|
||||
{numWithUnits(user.nterritories, {
|
||||
abbreviate: false,
|
||||
unitSingular: 'territory',
|
||||
unitPlural: 'territories'
|
||||
})}
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
)}
|
||||
</Nav>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { gql } from '@apollo/client'
|
||||
import { COMMENTS, COMMENTS_ITEM_EXT_FIELDS } from './comments'
|
||||
import { ITEM_FIELDS, ITEM_FULL_FIELDS } from './items'
|
||||
import { SUB_FULL_FIELDS } from './subs'
|
||||
|
||||
export const ME = gql`
|
||||
{
|
||||
|
@ -184,6 +185,7 @@ export const USER_FIELDS = gql`
|
|||
since
|
||||
photoId
|
||||
nitems
|
||||
nterritories
|
||||
meSubscriptionPosts
|
||||
meSubscriptionComments
|
||||
meMute
|
||||
|
@ -283,3 +285,26 @@ export const USER_WITH_ITEMS = gql`
|
|||
}
|
||||
}
|
||||
}`
|
||||
|
||||
export const USER_WITH_SUBS = gql`
|
||||
${USER_FIELDS}
|
||||
${SUB_FULL_FIELDS}
|
||||
query UserWithSubs($name: String!, $cursor: String, $type: String, $when: String, $from: String, $to: String, $by: String) {
|
||||
user(name: $name) {
|
||||
...UserFields
|
||||
}
|
||||
userSubs(name: $name, cursor: $cursor) {
|
||||
cursor
|
||||
subs {
|
||||
...SubFullFields
|
||||
ncomments(when: "forever")
|
||||
nposts(when: "forever")
|
||||
|
||||
optional {
|
||||
stacked(when: "forever")
|
||||
spent(when: "forever")
|
||||
revenue(when: "forever")
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { getGetServerSideProps } from '../../api/ssrApollo'
|
||||
import { useRouter } from 'next/router'
|
||||
import { USER, USER_WITH_SUBS } from '../../fragments/users'
|
||||
import { useQuery } from '@apollo/client'
|
||||
import PageLoading from '../../components/page-loading'
|
||||
import { UserLayout } from '.'
|
||||
import TerritoryList from '../../components/territory-list'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps({ query: USER_WITH_SUBS })
|
||||
|
||||
export default function UserTerritories ({ ssrData }) {
|
||||
const router = useRouter()
|
||||
const variables = { ...router.query }
|
||||
|
||||
const { data } = useQuery(USER, { variables })
|
||||
if (!data && !ssrData) return <PageLoading />
|
||||
|
||||
const { user } = data || ssrData
|
||||
|
||||
return (
|
||||
<UserLayout user={user}>
|
||||
<div className='mt-2'>
|
||||
<TerritoryList
|
||||
ssrData={ssrData}
|
||||
query={USER_WITH_SUBS}
|
||||
variables={variables}
|
||||
destructureData={data => data.userSubs}
|
||||
rank
|
||||
/>
|
||||
</div>
|
||||
</UserLayout>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue