Add new visitor welcome banner (#418)
* add new visitor welcome banner * show dismissible banner on first-time login * add mutation to hide welcome banner * Update components/banners.js Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com> * fix error handling * simplifications and other review suggestions * cleanup * restore selective display logic * remove unnecessary query arguments * cleanup a bit more * don't show welcome banner to existing stackers --------- Co-authored-by: rleed <rleed1@pm.me> Co-authored-by: ekzyis <27162016+ekzyis@users.noreply.github.com> Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
This commit is contained in:
parent
e2548e718d
commit
2ba969ebab
|
@ -578,6 +578,14 @@ export default {
|
||||||
await models.userSubscription.create({ data })
|
await models.userSubscription.create({ data })
|
||||||
}
|
}
|
||||||
return { id }
|
return { id }
|
||||||
|
},
|
||||||
|
hideWelcomeBanner: async (parent, data, { me, models }) => {
|
||||||
|
if (!me) {
|
||||||
|
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
||||||
|
}
|
||||||
|
|
||||||
|
await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default gql`
|
||||||
unlinkAuth(authType: String!): AuthMethods!
|
unlinkAuth(authType: String!): AuthMethods!
|
||||||
linkUnverifiedEmail(email: String!): Boolean
|
linkUnverifiedEmail(email: String!): Boolean
|
||||||
subscribeUser(id: ID): User
|
subscribeUser(id: ID): User
|
||||||
|
hideWelcomeBanner: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthMethods {
|
type AuthMethods {
|
||||||
|
@ -81,6 +82,7 @@ export default gql`
|
||||||
hideFromTopUsers: Boolean!
|
hideFromTopUsers: Boolean!
|
||||||
hideCowboyHat: Boolean!
|
hideCowboyHat: Boolean!
|
||||||
hideBookmarks: Boolean!
|
hideBookmarks: Boolean!
|
||||||
|
hideWelcomeBanner: Boolean!
|
||||||
clickToLoadImg: Boolean!
|
clickToLoadImg: Boolean!
|
||||||
wildWestMode: Boolean!
|
wildWestMode: Boolean!
|
||||||
greeterMode: Boolean!
|
greeterMode: Boolean!
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import Alert from 'react-bootstrap/Alert'
|
||||||
|
import styles from './banners.module.css'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useMe } from '../components/me'
|
||||||
|
import { useMutation } from '@apollo/client'
|
||||||
|
import { WELCOME_BANNER_MUTATION } from '../fragments/users'
|
||||||
|
import { useToast } from '../components/toast'
|
||||||
|
|
||||||
|
export default function WelcomeBanner () {
|
||||||
|
const me = useMe()
|
||||||
|
const toaster = useToast()
|
||||||
|
const [hidden, setHidden] = useState(true)
|
||||||
|
const handleClose = async () => {
|
||||||
|
window.localStorage.setItem('hideWelcomeBanner', true)
|
||||||
|
setHidden(true)
|
||||||
|
if (me) {
|
||||||
|
try {
|
||||||
|
await hideWelcomeBanner()
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
toaster.danger('mutation failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const [hideWelcomeBanner] = useMutation(WELCOME_BANNER_MUTATION, {
|
||||||
|
update (cache) {
|
||||||
|
cache.modify({
|
||||||
|
id: `User:${me.id}`,
|
||||||
|
fields: {
|
||||||
|
hideWelcomeBanner () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
useEffect(() => {
|
||||||
|
setHidden(me?.hideWelcomeBanner || (!me && window.localStorage.getItem('hideWelcomeBanner')))
|
||||||
|
}, [me?.hideWelcomeBanner])
|
||||||
|
|
||||||
|
if (hidden) return
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert className={styles.banner} key='info' variant='info' onClose={handleClose} dismissible>
|
||||||
|
<Alert.Heading>
|
||||||
|
👋 Welcome to Stacker News!
|
||||||
|
</Alert.Heading>
|
||||||
|
<p>
|
||||||
|
To get started, check out our{' '}
|
||||||
|
<Alert.Link href='/faq'>FAQs</Alert.Link> or{' '}
|
||||||
|
<Alert.Link href='/guide'>content guidelines</Alert.Link>, or go ahead and{' '}
|
||||||
|
{
|
||||||
|
me
|
||||||
|
? (
|
||||||
|
<Alert.Link href='/post'>make a post</Alert.Link>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<>
|
||||||
|
<Alert.Link href='/signup'>signup</Alert.Link> or create an{' '}
|
||||||
|
<Alert.Link href='/post'>anonymous post</Alert.Link>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}.
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.banner {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ export const ME = gql`
|
||||||
wildWestMode
|
wildWestMode
|
||||||
greeterMode
|
greeterMode
|
||||||
lastCheckedJobs
|
lastCheckedJobs
|
||||||
|
hideWelcomeBanner
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
@ -108,6 +109,13 @@ gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const WELCOME_BANNER_MUTATION =
|
||||||
|
gql`
|
||||||
|
mutation hideWelcomeBanner {
|
||||||
|
hideWelcomeBanner
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const USER_SEARCH =
|
export const USER_SEARCH =
|
||||||
gql`
|
gql`
|
||||||
query searchUsers($q: String!, $limit: Int, $similarity: Float) {
|
query searchUsers($q: String!, $limit: Int, $similarity: Float) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Items from '../../components/items'
|
||||||
import Layout from '../../components/layout'
|
import Layout from '../../components/layout'
|
||||||
import { SUB_ITEMS } from '../../fragments/subs'
|
import { SUB_ITEMS } from '../../fragments/subs'
|
||||||
import Snl from '../../components/snl'
|
import Snl from '../../components/snl'
|
||||||
|
import WelcomeBanner from '../../components/banners'
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({
|
export const getServerSideProps = getGetServerSideProps({
|
||||||
query: SUB_ITEMS,
|
query: SUB_ITEMS,
|
||||||
|
@ -17,6 +18,7 @@ export default function Sub ({ ssrData }) {
|
||||||
return (
|
return (
|
||||||
<Layout sub={variables.sub}>
|
<Layout sub={variables.sub}>
|
||||||
<Snl />
|
<Snl />
|
||||||
|
<WelcomeBanner />
|
||||||
<Items ssrData={ssrData} variables={variables} />
|
<Items ssrData={ssrData} variables={variables} />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ADD COLUMN "hideWelcomeBanner" BOOLEAN NOT NULL DEFAULT true, ALTER COLUMN "hideWelcomeBanner" SET DEFAULT false;
|
|
@ -87,6 +87,7 @@ model User {
|
||||||
hideBookmarks Boolean @default(false)
|
hideBookmarks Boolean @default(false)
|
||||||
followers UserSubscription[] @relation("follower")
|
followers UserSubscription[] @relation("follower")
|
||||||
followees UserSubscription[] @relation("followee")
|
followees UserSubscription[] @relation("followee")
|
||||||
|
hideWelcomeBanner Boolean @default(false)
|
||||||
|
|
||||||
@@index([createdAt], map: "users.created_at_index")
|
@@index([createdAt], map: "users.created_at_index")
|
||||||
@@index([inviteId], map: "users.inviteId_index")
|
@@index([inviteId], map: "users.inviteId_index")
|
||||||
|
|
Loading…
Reference in New Issue