Mute Management Settings Page (#1034)
* first pass of a mute mgmt page, ported from subscription mgmt page pr * adjust error message for mutes * muted users -> muted stackers * fix typo in component name
This commit is contained in:
parent
91a0d1ccd7
commit
e6b825dafe
|
@ -161,6 +161,26 @@ export default {
|
||||||
users
|
users
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
myMutedUsers: async (parent, { cursor }, { models, me }) => {
|
||||||
|
if (!me) {
|
||||||
|
throw new GraphQLError('You must be logged in to view muted users', { extensions: { code: 'UNAUTHENTICATED' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodedCursor = decodeCursor(cursor)
|
||||||
|
const users = await models.$queryRaw`
|
||||||
|
SELECT users.*
|
||||||
|
FROM "Mute"
|
||||||
|
JOIN users ON "Mute"."mutedId" = users.id
|
||||||
|
WHERE "Mute"."muterId" = ${me.id}
|
||||||
|
OFFSET ${decodedCursor.offset}
|
||||||
|
LIMIT ${LIMIT}
|
||||||
|
`
|
||||||
|
|
||||||
|
return {
|
||||||
|
cursor: users.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||||
|
users
|
||||||
|
}
|
||||||
|
},
|
||||||
topCowboys: async (parent, { cursor }, { models, me }) => {
|
topCowboys: async (parent, { cursor }, { models, me }) => {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
const range = whenRange('forever')
|
const range = whenRange('forever')
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default gql`
|
||||||
userSuggestions(q: String, limit: Limit): [User!]!
|
userSuggestions(q: String, limit: Limit): [User!]!
|
||||||
hasNewNotes: Boolean!
|
hasNewNotes: Boolean!
|
||||||
mySubscribedUsers(cursor: String): Users!
|
mySubscribedUsers(cursor: String): Users!
|
||||||
|
myMutedUsers(cursor: String): Users!
|
||||||
}
|
}
|
||||||
|
|
||||||
type UsersNullable {
|
type UsersNullable {
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
|
import { createContext, useContext } from 'react'
|
||||||
import { useMutation } from '@apollo/client'
|
import { useMutation } from '@apollo/client'
|
||||||
import { gql } from 'graphql-tag'
|
import { gql } from 'graphql-tag'
|
||||||
import Dropdown from 'react-bootstrap/Dropdown'
|
import Dropdown from 'react-bootstrap/Dropdown'
|
||||||
import { useToast } from './toast'
|
import { useToast } from './toast'
|
||||||
|
|
||||||
|
const MuteUserContext = createContext(() => ({
|
||||||
|
refetchQueries: []
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const MuteUserContextProvider = ({ children, value }) => {
|
||||||
|
return (
|
||||||
|
<MuteUserContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</MuteUserContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMuteUserContext = () => useContext(MuteUserContext)
|
||||||
|
|
||||||
export default function MuteDropdownItem ({ user: { name, id, meMute } }) {
|
export default function MuteDropdownItem ({ user: { name, id, meMute } }) {
|
||||||
const toaster = useToast()
|
const toaster = useToast()
|
||||||
|
const { refetchQueries } = useMuteUserContext()
|
||||||
const [toggleMute] = useMutation(
|
const [toggleMute] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
mutation toggleMute($id: ID!) {
|
mutation toggleMute($id: ID!) {
|
||||||
|
@ -12,6 +28,7 @@ export default function MuteDropdownItem ({ user: { name, id, meMute } }) {
|
||||||
meMute
|
meMute
|
||||||
}
|
}
|
||||||
}`, {
|
}`, {
|
||||||
|
refetchQueries,
|
||||||
update (cache, { data: { toggleMute } }) {
|
update (cache, { data: { toggleMute } }) {
|
||||||
cache.modify({
|
cache.modify({
|
||||||
id: `User:${id}`,
|
id: `User:${id}`,
|
||||||
|
|
|
@ -226,6 +226,26 @@ export const MY_SUBSCRIBED_USERS = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const MY_MUTED_USERS = gql`
|
||||||
|
query MyMutedUsers($cursor: String) {
|
||||||
|
myMutedUsers(cursor: $cursor) {
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
photoId
|
||||||
|
meSubscriptionPosts
|
||||||
|
meSubscriptionComments
|
||||||
|
meMute
|
||||||
|
|
||||||
|
optional {
|
||||||
|
streak
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const TOP_USERS = gql`
|
export const TOP_USERS = gql`
|
||||||
query TopUsers($cursor: String, $when: String, $from: String, $to: String, $by: String, ) {
|
query TopUsers($cursor: String, $when: String, $from: String, $to: String, $by: String, ) {
|
||||||
topUsers(cursor: $cursor, when: $when, from: $from, to: $to, by: $by) {
|
topUsers(cursor: $cursor, when: $when, from: $from, to: $to, by: $by) {
|
||||||
|
|
|
@ -99,6 +99,19 @@ function getClient (uri) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
myMutedUsers: {
|
||||||
|
keyArgs: false,
|
||||||
|
merge (existing, incoming) {
|
||||||
|
if (isFirstPage(incoming.cursor, existing?.users)) {
|
||||||
|
return incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cursor: incoming.cursor,
|
||||||
|
users: [...(existing?.users || []), ...incoming.users]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
userSuggestions: {
|
userSuggestions: {
|
||||||
keyArgs: ['q', 'limit'],
|
keyArgs: ['q', 'limit'],
|
||||||
merge (existing, incoming) {
|
merge (existing, incoming) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ function bech32encode (hexString) {
|
||||||
export function SettingsHeader () {
|
export function SettingsHeader () {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const pathParts = router.asPath.split('/').filter(segment => !!segment)
|
const pathParts = router.asPath.split('/').filter(segment => !!segment)
|
||||||
const activeKey = pathParts.length === 1 ? 'general' : 'subscriptions'
|
const activeKey = pathParts[1] ?? 'general'
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2 className='mb-2 text-start'>settings</h2>
|
<h2 className='mb-2 text-start'>settings</h2>
|
||||||
|
@ -59,6 +59,11 @@ export function SettingsHeader () {
|
||||||
<Nav.Link eventKey='subscriptions'>subscriptions</Nav.Link>
|
<Nav.Link eventKey='subscriptions'>subscriptions</Nav.Link>
|
||||||
</Link>
|
</Link>
|
||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
|
<Nav.Item>
|
||||||
|
<Link href='/settings/mutes' passHref legacyBehavior>
|
||||||
|
<Nav.Link eventKey='mutes'>muted stackers</Nav.Link>
|
||||||
|
</Link>
|
||||||
|
</Nav.Item>
|
||||||
</Nav>
|
</Nav>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { getGetServerSideProps } from '@/api/ssrApollo'
|
||||||
|
import Layout from '@/components/layout'
|
||||||
|
import UserList from '@/components/user-list'
|
||||||
|
import { MY_MUTED_USERS } from '@/fragments/users'
|
||||||
|
import { SettingsHeader } from '../index'
|
||||||
|
import { MuteUserContextProvider } from '@/components/mute'
|
||||||
|
|
||||||
|
export const getServerSideProps = getGetServerSideProps({ query: MY_MUTED_USERS, authRequired: true })
|
||||||
|
|
||||||
|
export default function MyMutedUsers ({ ssrData }) {
|
||||||
|
const muteUserContextValue = useMemo(() => ({ refetchQueries: ['MyMutedUsers'] }), [])
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className='pb-3 w-100 mt-2'>
|
||||||
|
<SettingsHeader />
|
||||||
|
<div className='mb-4 text-muted'>Well now, reckon these here are the folks you've gone and silenced.</div>
|
||||||
|
<MuteUserContextProvider value={muteUserContextValue}>
|
||||||
|
<UserList
|
||||||
|
ssrData={ssrData} query={MY_MUTED_USERS}
|
||||||
|
destructureData={data => data.myMutedUsers}
|
||||||
|
variables={{}}
|
||||||
|
rank
|
||||||
|
nymActionDropdown
|
||||||
|
statCompsProp={[]}
|
||||||
|
/>
|
||||||
|
</MuteUserContextProvider>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue