tip walkthrough

This commit is contained in:
keyan 2021-12-09 14:40:40 -06:00
parent ed0d4f2235
commit 4d3ab603da
7 changed files with 149 additions and 41 deletions

View File

@ -44,6 +44,15 @@ export default {
return true return true
}, },
setWalkthrough: async (parent, { upvotePopover, tipPopover }, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
await models.user.update({ where: { id: me.id }, data: { upvotePopover, tipPopover } })
return true
},
setTheme: async (parent, { theme }, { me, models }) => { setTheme: async (parent, { theme }, { me, models }) => {
if (!me) { if (!me) {
throw new AuthenticationError('you must be logged in') throw new AuthenticationError('you must be logged in')

View File

@ -13,6 +13,7 @@ export default gql`
setSettings(tipDefault: Int!): Boolean setSettings(tipDefault: Int!): Boolean
setTheme(theme: String!): Boolean setTheme(theme: String!): Boolean
upsertBio(bio: String!): User! upsertBio(bio: String!): User!
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
} }
type User { type User {
@ -31,5 +32,7 @@ export default gql`
sats: Int! sats: Int!
msats: Int! msats: Int!
theme: String! theme: String!
upvotePopover: Boolean!
tipPopover: Boolean!
} }
` `

View File

@ -8,16 +8,104 @@ import ActionTooltip from './action-tooltip'
import { useItemAct } from './item-act' import { useItemAct } from './item-act'
import Window from '../svgs/window-2-fill.svg' import Window from '../svgs/window-2-fill.svg'
import { useMe } from './me' import { useMe } from './me'
import { useState } from 'react' import { useRef, useState } from 'react'
import LongPressable from 'react-longpressable' import LongPressable from 'react-longpressable'
import Rainbow from '../lib/rainbow' import Rainbow from '../lib/rainbow'
import { OverlayTrigger, Popover } from 'react-bootstrap' import { Overlay, Popover } from 'react-bootstrap'
const getColor = (meSats) => {
if (!meSats || meSats <= 10) {
return 'var(--secondary)'
}
const idx = Math.min(
Math.floor((Math.log(meSats) / Math.log(100000)) * (Rainbow.length - 1)),
Rainbow.length - 1)
return Rainbow[idx]
}
const UpvotePopover = ({ target, show, handleClose }) => (
<Overlay
show={show}
target={target}
placement='right'
>
<Popover id='popover-basic'>
<Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Tipping
<button type='button' className='close' onClick={handleClose}><span aria-hidden='true'>×</span><span className='sr-only'>Close alert</span></button>
</Popover.Title>
<Popover.Content>
Press bolt again to tip 1 sat
</Popover.Content>
</Popover>
</Overlay>
)
const TipPopover = ({ target, show, handleClose }) => (
<Overlay
show={show}
target={target}
placement='right'
>
<Popover id='popover-basic'>
<Popover.Title className='d-flex justify-content-between alert-dismissible' as='h3'>Press and hold
<button type='button' class='close' onClick={handleClose}><span aria-hidden='true'>×</span><span class='sr-only'>Close alert</span></button>
</Popover.Title>
<Popover.Content>
Press and hold bolt to tip a custom amount
</Popover.Content>
</Popover>
</Overlay>
)
export default function UpVote ({ item, className }) { export default function UpVote ({ item, className }) {
const { setError } = useFundError() const { setError } = useFundError()
const { setItem } = useItemAct() const { setItem } = useItemAct()
const [voteLock, setVoteLock] = useState() const [voteLock, setVoteLock] = useState()
const [voteShow, _setVoteShow] = useState(false)
const [tipShow, _setTipShow] = useState(false)
const ref = useRef()
const me = useMe() const me = useMe()
const [setWalkthrough] = useMutation(
gql`
mutation setWalkthrough($upvotePopover: Boolean, $tipPopover: Boolean) {
setWalkthrough(upvotePopover: $upvotePopover, tipPopover: $tipPopover)
}`
)
// TODO: 1. create functions that updates user to say that they've performed the
// actions so they don't get reprompted, 2. check that the user hasn't been prompted
// before prompting ... we can probably just create one toggle function for each
// that does these checks
const setVoteShow = (yes) => {
if (!me) return
if (yes && !me.upvotePopover) {
_setVoteShow(yes)
}
if (voteShow && !yes) {
_setVoteShow(yes)
setWalkthrough({ variables: { upvotePopover: true } })
}
}
const setTipShow = (yes) => {
if (!me) return
// if we want to show it, yet we still haven't shown
if (yes && !me.tipPopover) {
_setTipShow(yes)
}
// if it's currently showing and we want to hide it
if (tipShow && !yes) {
_setTipShow(yes)
setWalkthrough({ variables: { tipPopover: true } })
}
}
const [act] = useMutation( const [act] = useMutation(
gql` gql`
mutation act($id: ID!, $act: ItemAct! $sats: Int!, $tipDefault: Boolean) { mutation act($id: ID!, $act: ItemAct! $sats: Int!, $tipDefault: Boolean) {
@ -29,6 +117,13 @@ export default function UpVote ({ item, className }) {
update (cache, { data: { act: { act, sats } } }) { update (cache, { data: { act: { act, sats } } }) {
// read in the cached object so we don't use meSats prop // read in the cached object so we don't use meSats prop
// which can be stale // which can be stale
if (act === 'VOTE') {
setVoteShow(true)
}
if (act === 'TIP') {
setTipShow(true)
}
cache.modify({ cache.modify({
id: `Item:${item.id}`, id: `Item:${item.id}`,
fields: { fields: {
@ -85,26 +180,6 @@ export default function UpVote ({ item, className }) {
return '1 sat' return '1 sat'
} }
const getColor = (meSats) => {
if (!meSats || meSats <= 10) {
return 'var(--secondary)'
}
const idx = Math.min(
Math.floor((Math.log(meSats) / Math.log(100000)) * (Rainbow.length - 1)),
Rainbow.length - 1)
return Rainbow[idx]
}
const popover = (
<Popover id='popover-basic'>
<Popover.Title as='h3'>Tipping</Popover.Title>
<Popover.Content>
Press bolt again to tip
</Popover.Content>
</Popover>
)
const noSelfTips = item?.meVote && item?.mine const noSelfTips = item?.meVote && item?.mine
// 12 px default height // 12 px default height
const cover = (item?.meSats < 10 ? ((10 - item.meSats) / 10.0) : 0) * 12 const cover = (item?.meSats < 10 ? ((10 - item.meSats) / 10.0) : 0) * 12
@ -112,8 +187,9 @@ export default function UpVote ({ item, className }) {
return ( return (
<LightningConsumer> <LightningConsumer>
{({ strike }) => {({ strike }) =>
<LongPressable <div ref={ref}>
onLongPress={ <LongPressable
onLongPress={
async (e) => { async (e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@ -124,10 +200,11 @@ export default function UpVote ({ item, className }) {
return return
} }
setTipShow(false)
setItem({ itemId: item.id, act, strike }) setItem({ itemId: item.id, act, strike })
} }
} }
onShortPress={ onShortPress={
me me
? async (e) => { ? async (e) => {
e.preventDefault() e.preventDefault()
@ -140,6 +217,7 @@ export default function UpVote ({ item, className }) {
} }
if (item?.meVote) { if (item?.meVote) {
setVoteShow(false)
if (me?.tipDefault) { if (me?.tipDefault) {
try { try {
strike() strike()
@ -170,21 +248,21 @@ export default function UpVote ({ item, className }) {
} }
: signIn : signIn
} }
> >
<ActionTooltip notForm disable={noSelfTips} overlayText={overlayText()}> <ActionTooltip notForm disable={noSelfTips} overlayText={overlayText()}>
<div className={`${noSelfTips ? styles.noSelfTips : ''} <div
${styles.upvoteWrapper}`} className={`${noSelfTips ? styles.noSelfTips : ''}
> ${styles.upvoteWrapper}`}
<OverlayTrigger placement='right' overlay={popover} show> >
<UpBolt <UpBolt
width={24} width={24}
height={24} height={24}
className={ className={
`${styles.upvote} `${styles.upvote}
${className || ''} ${className || ''}
${noSelfTips ? styles.noSelfTips : ''} ${noSelfTips ? styles.noSelfTips : ''}
${item?.meVote ? styles.voted : ''}` ${item?.meVote ? styles.voted : ''}`
} }
style={item?.meVote style={item?.meVote
? { ? {
fill: color, fill: color,
@ -195,11 +273,17 @@ export default function UpVote ({ item, className }) {
e.stopPropagation() e.stopPropagation()
}} }}
/> />
</OverlayTrigger> <div
<div className={styles.cover} style={{ top: item?.parentId ? '9px' : '4px', height: `${cover}px` }} /> className={styles.cover} onClick={e => {
</div> e.stopPropagation()
</ActionTooltip> }} style={{ top: item?.parentId ? '9px' : '4px', height: `${cover}px` }}
</LongPressable>} />
</div>
</ActionTooltip>
</LongPressable>
<TipPopover target={ref.current} show={tipShow} handleClose={() => setTipShow(false)} />
<UpvotePopover target={ref.current} show={voteShow} handleClose={() => setVoteShow(false)} />
</div>}
</LightningConsumer> </LightningConsumer>
) )
} }

View File

@ -18,6 +18,8 @@ export const ME = gql`
} }
hasInvites hasInvites
theme theme
upvotePopover
tipPopover
} }
}` }`

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "tipPopover" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "upvotePopover" BOOLEAN NOT NULL DEFAULT false;

View File

@ -37,6 +37,9 @@ model User {
pubkey String? @unique pubkey String? @unique
theme String @default("light") theme String @default("light")
upvotePopover Boolean @default(false)
tipPopover Boolean @default(false)
@@index([createdAt]) @@index([createdAt])
@@map(name: "users") @@map(name: "users")
} }

View File

@ -230,6 +230,10 @@ textarea.form-control {
font-family: 'lightning'; font-family: 'lightning';
} }
.popover-header.alert-dismissible .close {
padding: .5rem !important;
}
.alert-dismissible .close::after { .alert-dismissible .close::after {
content: 'X'; content: 'X';
} }