Skip to content

Add Territory Sub management tab in Subscriptions #2191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
51 changes: 50 additions & 1 deletion api/resolvers/sub.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default {
subs
}
},
userSubs: async (_parent, { name, cursor, when, by, from, to, limit = LIMIT }, { models }) => {
userSubs: async (_parent, { name, cursor, when, by, from, to, limit = LIMIT }, { models, me }) => {
if (!name) {
throw new GqlInputError('must supply user name')
}
Expand Down Expand Up @@ -166,10 +166,59 @@ export default {
OFFSET $4
LIMIT $5`, ...range, user.id, decodedCursor.offset, limit)

if (me) {
const userSubNames = await models.subSubscription.findMany({
where: { userId: me.id },
select: { subName: true }
})
const subscribedSet = new Set(userSubNames.map(s => s.subName))
const mutedSubNames = await models.muteSub.findMany({
where: { userId: me.id },
select: { subName: true }
})
const mutedSet = new Set(mutedSubNames.map(s => s.subName))
subs.forEach(sub => {
sub.meSubscription = subscribedSet.has(sub.name)
sub.meMuteSub = mutedSet.has(sub.name)
})
} else {
subs.forEach(sub => {
sub.meSubscription = false
sub.meMuteSub = false
})
}

return {
cursor: subs.length === limit ? nextCursorEncoded(decodedCursor, limit) : null,
subs
}
},
mySubscribedSubs: async (parent, { cursor }, { models, me }) => {
if (!me) {
throw new GqlAuthenticationError()
}

const decodedCursor = decodeCursor(cursor)
const subs = await models.$queryRaw`
SELECT "Sub".*,
EXISTS (
SELECT 1 FROM "MuteSub"
WHERE "MuteSub"."userId" = ${me.id} AND "MuteSub"."subName" = "Sub".name
) AS "meMuteSub",
TRUE as "meSubscription"
FROM "SubSubscription"
JOIN "Sub" ON "SubSubscription"."subName" = "Sub".name
WHERE "SubSubscription"."userId" = ${me.id}
AND "Sub".status <> 'STOPPED'
ORDER BY "Sub".name ASC
OFFSET ${decodedCursor.offset}
LIMIT ${LIMIT}
`

return {
cursor: subs.length === LIMIT ? nextCursorEncoded(decodedCursor, LIMIT) : null,
subs
}
}
},
Mutation: {
Expand Down
1 change: 1 addition & 0 deletions api/typeDefs/sub.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default gql`
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
mySubscribedSubs(cursor: String): Subs
subSuggestions(q: String!, limit: Limit): [Sub!]!
}

Expand Down
17 changes: 17 additions & 0 deletions components/territory-header.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createContext, useContext } from 'react'
import { Badge, Button, CardFooter, Dropdown } from 'react-bootstrap'
import { AccordianCard } from './accordian-item'
import TerritoryPaymentDue, { TerritoryBillingLine } from './territory-payment-due'
Expand All @@ -13,6 +14,16 @@ import { useToast } from './toast'
import ActionDropdown from './action-dropdown'
import { TerritoryTransferDropdownItem } from './territory-transfer'

const SubscribeTerritoryContext = createContext({ refetchQueries: [] })

export const SubscribeTerritoryContextProvider = ({ children, value }) => (
<SubscribeTerritoryContext.Provider value={value}>
{children}
</SubscribeTerritoryContext.Provider>
)

export const useSubscribeTerritoryContext = () => useContext(SubscribeTerritoryContext)

export function TerritoryDetails ({ sub, children }) {
return (
<AccordianCard
Expand Down Expand Up @@ -149,12 +160,15 @@ export default function TerritoryHeader ({ sub }) {

export function MuteSubDropdownItem ({ item, sub }) {
const toaster = useToast()
const { refetchQueries } = useSubscribeTerritoryContext()

const [toggleMuteSub] = useMutation(
gql`
mutation toggleMuteSub($name: String!) {
toggleMuteSub(name: $name)
}`, {
refetchQueries,
awaitRefetchQueries: true,
update (cache, { data: { toggleMuteSub } }) {
cache.modify({
id: `Sub:{"name":"${sub.name}"}`,
Expand Down Expand Up @@ -213,11 +227,14 @@ export function PinSubDropdownItem ({ item: { id, position } }) {

export function ToggleSubSubscriptionDropdownItem ({ sub: { name, meSubscription } }) {
const toaster = useToast()
const { refetchQueries } = useSubscribeTerritoryContext()
const [toggleSubSubscription] = useMutation(
gql`
mutation toggleSubSubscription($name: String!) {
toggleSubSubscription(name: $name)
}`, {
refetchQueries,
awaitRefetchQueries: true,
update (cache, { data: { toggleSubSubscription } }) {
cache.modify({
id: `Sub:{"name":"${name}"}`,
Expand Down
17 changes: 12 additions & 5 deletions components/territory-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { useQuery } from '@apollo/client'
import MoreFooter from './more-footer'
import { useData } from './use-data'
import Info from './info'
import { TerritoryInfo } from './territory-header'
import ActionDropdown from './action-dropdown'
import { TerritoryInfo, ToggleSubSubscriptionDropdownItem, MuteSubDropdownItem } from './territory-header'

// all of this nonsense is to show the stat we are sorting by first
const Revenue = ({ sub }) => (sub.optional.revenue !== null && <span>{abbrNum(sub.optional.revenue)} revenue</span>)
Expand Down Expand Up @@ -35,16 +36,16 @@ function separate (arr, separator) {
return arr.flatMap((x, i) => i < arr.length - 1 ? [x, separator] : [x])
}

export default function TerritoryList ({ ssrData, query, variables, destructureData, rank }) {
export default function TerritoryList ({ ssrData, query, variables, destructureData, rank, subActionDropdown, statCompsProp = STAT_COMPONENTS }) {
const { data, fetchMore } = useQuery(query, { variables })
const dat = useData(data, ssrData)
const [statComps, setStatComps] = useState(separate(STAT_COMPONENTS, Separator))
const [statComps, setStatComps] = useState(separate(statCompsProp, Separator))

useEffect(() => {
// shift the stat we are sorting by to the front
const comps = [...STAT_COMPONENTS]
const comps = [...statCompsProp]
setStatComps(separate([...comps.splice(STAT_POS[variables?.by || 0], 1), ...comps], Separator))
}, [variables?.by])
}, [variables?.by], statCompsProp)

const { subs, cursor } = useMemo(() => {
if (!dat) return {}
Expand Down Expand Up @@ -77,6 +78,12 @@ export default function TerritoryList ({ ssrData, query, variables, destructureD
{sub.name}
</Link>
<Info className='d-flex'><TerritoryInfo sub={sub} /></Info>
{subActionDropdown && (
<ActionDropdown>
<ToggleSubSubscriptionDropdownItem sub={sub} />
<MuteSubDropdownItem sub={sub} />
</ActionDropdown>
)}
</div>
<div className={styles.other}>
{statComps.map((Comp, i) => <Comp key={i} sub={sub} />)}
Expand Down
49 changes: 30 additions & 19 deletions fragments/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,25 +216,6 @@ export const USER_FIELDS = gql`
...StreakFields
}`

export const MY_SUBSCRIBED_USERS = gql`
${STREAK_FIELDS}
query MySubscribedUsers($cursor: String) {
mySubscribedUsers(cursor: $cursor) {
users {
id
name
photoId
meSubscriptionPosts
meSubscriptionComments
meMute

...StreakFields
}
cursor
}
}
`

export const MY_MUTED_USERS = gql`
${STREAK_FIELDS}
query MyMutedUsers($cursor: String) {
Expand Down Expand Up @@ -390,3 +371,33 @@ export const USER_STATS = gql`
}
}
}`

export const MY_SUBSCRIBED_USERS = gql`
${STREAK_FIELDS}
query MySubscribedUsers($cursor: String) {
mySubscribedUsers(cursor: $cursor) {
users {
id
name
photoId
meSubscriptionPosts
meSubscriptionComments
meMute
...StreakFields
}
cursor
}
}
`

export const MY_SUBSCRIBED_SUBS = gql`
${SUB_FULL_FIELDS}
query MySubscribedSubs($cursor: String) {
mySubscribedSubs(cursor: $cursor) {
subs {
...SubFullFields
}
cursor
}
}
`
3 changes: 3 additions & 0 deletions pages/[name]/territories.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useQuery } from '@apollo/client'
import PageLoading from '@/components/page-loading'
import { UserLayout } from '.'
import TerritoryList from '@/components/territory-list'
import { useMe } from '@/components/me'

export const getServerSideProps = getGetServerSideProps({ query: USER_WITH_SUBS })

Expand All @@ -16,6 +17,7 @@ export default function UserTerritories ({ ssrData }) {
if (!data && !ssrData) return <PageLoading />

const { user } = data || ssrData
const { me } = useMe()

return (
<UserLayout user={user}>
Expand All @@ -25,6 +27,7 @@ export default function UserTerritories ({ ssrData }) {
query={USER_WITH_SUBS}
variables={variables}
destructureData={data => data.userSubs}
subActionDropdown={!!me}
rank
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion pages/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function SettingsHeader () {
</Link>
</Nav.Item>
<Nav.Item>
<Link href='/settings/subscriptions' passHref legacyBehavior>
<Link href='/settings/subscriptions/stackers' passHref legacyBehavior>
<Nav.Link eventKey='subscriptions'>subscriptions</Nav.Link>
</Link>
</Nav.Item>
Expand Down
31 changes: 0 additions & 31 deletions pages/settings/subscriptions/index.js

This file was deleted.

55 changes: 55 additions & 0 deletions pages/settings/subscriptions/stackers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useMemo } from 'react'
import { getGetServerSideProps } from '@/api/ssrApollo'
import Layout from '@/components/layout'
import { Select } from '@/components/form'
import UserList from '@/components/user-list'
import { MY_SUBSCRIBED_USERS } from '@/fragments/users'
import { SettingsHeader } from '../index'
import { SubscribeUserContextProvider } from '@/components/subscribeUser'
import { useRouter } from 'next/router'

export const getServerSideProps = getGetServerSideProps({
query: MY_SUBSCRIBED_USERS,
authRequired: true
})

export function SubscriptionLayout ({ subType, children }) {
const router = useRouter()

return (
<Layout>
<div className='pb-3 w-100 mt-2'>
<SettingsHeader />
<Select
name='subscriptionType'
size='sm'
className='w-auto'
noForm
items={['stackers', 'territories']}
value={subType}
onChange={(_, e) => router.push(`/settings/subscriptions/${e.target.value}`)}
/>
{children}
</div>
</Layout>
)
}

export default function MySubscribedUsers ({ ssrData }) {
const subscribeContextValue = useMemo(() => ({ refetchQueries: ['MySubscribedUsers'] }), [])
return (
<SubscriptionLayout subType='stackers'>
<SubscribeUserContextProvider value={subscribeContextValue}>
<UserList
ssrData={ssrData}
query={MY_SUBSCRIBED_USERS}
destructureData={data => data.mySubscribedUsers}
variables={{}}
rank
nymActionDropdown
statCompsProp={[]}
/>
</SubscribeUserContextProvider>
</SubscriptionLayout>
)
}
30 changes: 30 additions & 0 deletions pages/settings/subscriptions/territories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useMemo } from 'react'
import { getGetServerSideProps } from '@/api/ssrApollo'
import { MY_SUBSCRIBED_SUBS } from '@/fragments/users'
import TerritoryList from '@/components/territory-list'
import { SubscribeTerritoryContextProvider } from '@/components/territory-header'
import { SubscriptionLayout } from './stackers'

export const getServerSideProps = getGetServerSideProps({
query: MY_SUBSCRIBED_SUBS,
authRequired: true
})

export default function MySubscribedSubs ({ ssrData }) {
const subscribeContextValue = useMemo(() => ({ refetchQueries: ['MySubscribedSubs'] }), [])
return (
<SubscriptionLayout subType='territories'>
<SubscribeTerritoryContextProvider value={subscribeContextValue}>
<TerritoryList
ssrData={ssrData}
query={MY_SUBSCRIBED_SUBS}
variables={{}}
destructureData={data => data.mySubscribedSubs}
rank
subActionDropdown
statCompsProp={[]}
/>
</SubscribeTerritoryContextProvider>
</SubscriptionLayout>
)
}