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:
rleed 2023-09-11 17:29:45 -04:00 committed by GitHub
parent e2548e718d
commit 2ba969ebab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 97 additions and 0 deletions

View File

@ -578,6 +578,14 @@ export default {
await models.userSubscription.create({ data })
}
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
}
},

View File

@ -31,6 +31,7 @@ export default gql`
unlinkAuth(authType: String!): AuthMethods!
linkUnverifiedEmail(email: String!): Boolean
subscribeUser(id: ID): User
hideWelcomeBanner: Boolean
}
type AuthMethods {
@ -81,6 +82,7 @@ export default gql`
hideFromTopUsers: Boolean!
hideCowboyHat: Boolean!
hideBookmarks: Boolean!
hideWelcomeBanner: Boolean!
clickToLoadImg: Boolean!
wildWestMode: Boolean!
greeterMode: Boolean!

67
components/banners.js Normal file
View File

@ -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>
)
}

View File

@ -0,0 +1,7 @@
.banner {
margin-top: 1em;
}
.banner p:last-of-type {
margin-bottom: 0;
}

View File

@ -33,6 +33,7 @@ export const ME = gql`
wildWestMode
greeterMode
lastCheckedJobs
hideWelcomeBanner
}
}`
@ -108,6 +109,13 @@ gql`
}
`
export const WELCOME_BANNER_MUTATION =
gql`
mutation hideWelcomeBanner {
hideWelcomeBanner
}
`
export const USER_SEARCH =
gql`
query searchUsers($q: String!, $limit: Int, $similarity: Float) {

View File

@ -4,6 +4,7 @@ import Items from '../../components/items'
import Layout from '../../components/layout'
import { SUB_ITEMS } from '../../fragments/subs'
import Snl from '../../components/snl'
import WelcomeBanner from '../../components/banners'
export const getServerSideProps = getGetServerSideProps({
query: SUB_ITEMS,
@ -17,6 +18,7 @@ export default function Sub ({ ssrData }) {
return (
<Layout sub={variables.sub}>
<Snl />
<WelcomeBanner />
<Items ssrData={ssrData} variables={variables} />
</Layout>
)

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "hideWelcomeBanner" BOOLEAN NOT NULL DEFAULT true, ALTER COLUMN "hideWelcomeBanner" SET DEFAULT false;

View File

@ -87,6 +87,7 @@ model User {
hideBookmarks Boolean @default(false)
followers UserSubscription[] @relation("follower")
followees UserSubscription[] @relation("followee")
hideWelcomeBanner Boolean @default(false)
@@index([createdAt], map: "users.created_at_index")
@@index([inviteId], map: "users.inviteId_index")