Skip to content

Commit 8626d10

Browse files
committed
Merge branch 'master' into remodel
2 parents d517479 + a3e0b6a commit 8626d10

File tree

24 files changed

+398
-223
lines changed

24 files changed

+398
-223
lines changed

api/resolvers/notifications.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -316,13 +316,36 @@ export default {
316316

317317
if (meFull.noteCowboyHat) {
318318
queries.push(
319-
`(SELECT id::text, updated_at AS "sortTime", 0 as "earnedSats", 'Streak' AS type
319+
`(SELECT id::text, updated_at AS "sortTime", 0 as "earnedSats", 'CowboyHat' AS type
320320
FROM "Streak"
321321
WHERE "userId" = $1
322322
AND updated_at < $2
323+
AND type = 'COWBOY_HAT'
323324
ORDER BY "sortTime" DESC
324325
LIMIT ${LIMIT})`
325326
)
327+
for (const type of ['HORSE', 'GUN']) {
328+
const gqlType = type.charAt(0) + type.slice(1).toLowerCase()
329+
queries.push(
330+
`(SELECT id::text, "startedAt" AS "sortTime", 0 as "earnedSats", 'New${gqlType}' AS type
331+
FROM "Streak"
332+
WHERE "userId" = $1
333+
AND updated_at < $2
334+
AND type = '${type}'::"StreakType"
335+
ORDER BY "sortTime" DESC
336+
LIMIT ${LIMIT})`
337+
)
338+
queries.push(
339+
`(SELECT id::text AS id, "endedAt" AS "sortTime", 0 as "earnedSats", 'Lost${gqlType}' AS type
340+
FROM "Streak"
341+
WHERE "userId" = $1
342+
AND updated_at < $2
343+
AND "endedAt" IS NOT NULL
344+
AND type = '${type}'::"StreakType"
345+
ORDER BY "sortTime" DESC
346+
LIMIT ${LIMIT})`
347+
)
348+
}
326349
}
327350

328351
queries.push(
@@ -500,23 +523,14 @@ export default {
500523
}
501524
}
502525
},
503-
Streak: {
526+
CowboyHat: {
504527
days: async (n, args, { models }) => {
505528
const res = await models.$queryRaw`
506-
SELECT "endedAt" - "startedAt" AS days
529+
SELECT "endedAt"::date - "startedAt"::date AS days
507530
FROM "Streak"
508531
WHERE id = ${Number(n.id)} AND "endedAt" IS NOT NULL
509532
`
510-
511533
return res.length ? res[0].days : null
512-
},
513-
type: async (n, args, { models }) => {
514-
const res = await models.$queryRaw`
515-
SELECT "type"
516-
FROM "Streak"
517-
WHERE id = ${Number(n.id)}
518-
`
519-
return res.length ? res[0].type : null
520534
}
521535
},
522536
Earn: {

api/resolvers/user.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,27 +1082,25 @@ export default {
10821082

10831083
return user.streak
10841084
},
1085-
gunStreak: async (user, args, { models }) => {
1085+
hasSendWallet: async (user, args, { models }) => {
10861086
if (user.hideCowboyHat) {
1087-
return null
1087+
return false
10881088
}
1089-
1090-
return user.gunStreak
1089+
return user.hasSendWallet
10911090
},
1092-
horseStreak: async (user, args, { models }) => {
1091+
hasRecvWallet: async (user, args, { models }) => {
10931092
if (user.hideCowboyHat) {
1094-
return null
1093+
return false
10951094
}
1096-
1097-
return user.horseStreak
1095+
return user.hasRecvWallet
10981096
},
10991097
maxStreak: async (user, args, { models }) => {
11001098
if (user.hideCowboyHat) {
11011099
return null
11021100
}
11031101

11041102
const [{ max }] = await models.$queryRaw`
1105-
SELECT MAX(COALESCE("endedAt", (now() AT TIME ZONE 'America/Chicago')::date) - "startedAt")
1103+
SELECT MAX(COALESCE("endedAt"::date, (now() AT TIME ZONE 'America/Chicago')::date) - "startedAt"::date)
11061104
FROM "Streak" WHERE "userId" = ${user.id}`
11071105
return max
11081106
},

api/typeDefs/notifications.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,6 @@ export default gql`
7575
tipComments: Int!
7676
}
7777
78-
type Streak {
79-
id: ID!
80-
sortTime: Date!
81-
days: Int
82-
type: String!
83-
}
84-
8578
type Earn {
8679
id: ID!
8780
earnedSats: Int!
@@ -156,11 +149,37 @@ export default gql`
156149
sortTime: Date!
157150
}
158151
152+
type CowboyHat {
153+
id: ID!
154+
sortTime: Date!
155+
days: Int
156+
}
157+
158+
type NewHorse {
159+
id: ID!
160+
sortTime: Date!
161+
}
162+
163+
type LostHorse {
164+
id: ID!
165+
sortTime: Date!
166+
}
167+
168+
type NewGun {
169+
id: ID!
170+
sortTime: Date!
171+
}
172+
173+
type LostGun {
174+
id: ID!
175+
sortTime: Date!
176+
}
177+
159178
union Notification = Reply | Votification | Mention
160179
| Invitification | Earn | JobChanged | InvoicePaid | WithdrawlPaid | Referral
161-
| Streak | FollowActivity | ForwardedVotification | Revenue | SubStatus
180+
| FollowActivity | ForwardedVotification | Revenue | SubStatus
162181
| TerritoryPost | TerritoryTransfer | Reminder | ItemMention | Invoicification
163-
| ReferralReward
182+
| ReferralReward | CowboyHat | NewHorse | LostHorse | NewGun | LostGun
164183
165184
type Notifications {
166185
lastChecked: Date

api/typeDefs/user.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ export default gql`
211211
streak: Int
212212
gunStreak: Int
213213
horseStreak: Int
214+
hasSendWallet: Boolean
215+
hasRecvWallet: Boolean
214216
maxStreak: Int
215217
isContributor: Boolean
216218
githubId: String

components/badge.js

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
1+
import { Fragment } from 'react'
12
import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
23
import Tooltip from 'react-bootstrap/Tooltip'
34
import CowboyHatIcon from '@/svgs/cowboy.svg'
45
import AnonIcon from '@/svgs/spy-fill.svg'
5-
import { numWithUnits } from '@/lib/format'
6-
import { USER_ID } from '@/lib/constants'
76
import GunIcon from '@/svgs/revolver.svg'
87
import HorseIcon from '@/svgs/horse.svg'
8+
import { numWithUnits } from '@/lib/format'
9+
import { USER_ID } from '@/lib/constants'
910
import classNames from 'classnames'
1011

11-
const BADGES = [
12-
{
13-
icon: CowboyHatIcon,
14-
streakName: 'streak'
15-
},
16-
{
17-
icon: HorseIcon,
18-
streakName: 'horseStreak'
19-
},
20-
{
21-
icon: GunIcon,
22-
streakName: 'gunStreak',
23-
sizeDelta: 2
24-
}
25-
]
26-
2712
export default function Badges ({ user, badge, className = 'ms-1', badgeClassName, spacingClassName = 'ms-1', height = 16, width = 16 }) {
2813
if (!user || Number(user.id) === USER_ID.ad) return null
2914
if (Number(user.id) === USER_ID.anon) {
@@ -34,14 +19,41 @@ export default function Badges ({ user, badge, className = 'ms-1', badgeClassNam
3419
)
3520
}
3621

22+
const badges = []
23+
24+
const streak = user.optional.streak
25+
if (streak !== null) {
26+
badges.push({
27+
icon: CowboyHatIcon,
28+
overlayText: streak
29+
? `${numWithUnits(streak, { abbreviate: false, unitSingular: 'day', unitPlural: 'days' })}`
30+
: 'new'
31+
})
32+
}
33+
34+
if (user.optional.hasSendWallet) {
35+
badges.push({
36+
icon: GunIcon,
37+
sizeDelta: 2,
38+
overlayText: 'can send sats'
39+
})
40+
}
41+
42+
if (user.optional.hasRecvWallet) {
43+
badges.push({
44+
icon: HorseIcon,
45+
overlayText: 'can receive sats'
46+
})
47+
}
48+
3749
return (
3850
<span className={className}>
39-
{BADGES.map(({ icon, streakName, sizeDelta }, i) => (
51+
{badges.map(({ icon, overlayText, sizeDelta }, i) => (
4052
<SNBadge
41-
key={streakName}
53+
key={i}
4254
user={user}
4355
badge={badge}
44-
streakName={streakName}
56+
overlayText={overlayText}
4557
badgeClassName={classNames(badgeClassName, i > 0 && spacingClassName)}
4658
IconForBadge={icon}
4759
height={height}
@@ -53,20 +65,19 @@ export default function Badges ({ user, badge, className = 'ms-1', badgeClassNam
5365
)
5466
}
5567

56-
function SNBadge ({ user, badge, streakName, badgeClassName, IconForBadge, height = 16, width = 16, sizeDelta = 0 }) {
57-
const streak = user.optional[streakName]
58-
if (streak === null) {
59-
return null
68+
function SNBadge ({ user, badge, overlayText, badgeClassName, IconForBadge, height = 16, width = 16, sizeDelta = 0 }) {
69+
let Wrapper = Fragment
70+
71+
if (overlayText) {
72+
Wrapper = ({ children }) => (
73+
<BadgeTooltip overlayText={overlayText}>{children}</BadgeTooltip>
74+
)
6075
}
6176

6277
return (
63-
<BadgeTooltip
64-
overlayText={streak
65-
? `${numWithUnits(streak, { abbreviate: false, unitSingular: 'day', unitPlural: 'days' })}`
66-
: 'new'}
67-
>
78+
<Wrapper>
6879
<span><IconForBadge className={badgeClassName} height={height + sizeDelta} width={width + sizeDelta} /></span>
69-
</BadgeTooltip>
80+
</Wrapper>
7081
)
7182
}
7283

components/item-act.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export function useZap () {
327327
// but right now this toast is noisy for optimistic zaps
328328
console.error(error)
329329
}
330-
}, [act, toaster, strike, wallets.length])
330+
}, [act, toaster, strike, wallets])
331331
}
332332

333333
export class ActCanceledError extends Error {

components/notifications.js

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ function Notification ({ n, fresh }) {
5858
(type === 'InvoicePaid' && (n.invoice.nostr ? <NostrZap n={n} /> : <InvoicePaid n={n} />)) ||
5959
(type === 'WithdrawlPaid' && <WithdrawlPaid n={n} />) ||
6060
(type === 'Referral' && <Referral n={n} />) ||
61-
(type === 'Streak' && <Streak n={n} />) ||
61+
(type === 'CowboyHat' && <CowboyHat n={n} />) ||
62+
(['NewHorse', 'LostHorse'].includes(type) && <Horse n={n} />) ||
63+
(['NewGun', 'LostGun'].includes(type) && <Gun n={n} />) ||
6264
(type === 'Votification' && <Votification n={n} />) ||
6365
(type === 'ForwardedVotification' && <ForwardedVotification n={n} />) ||
6466
(type === 'Mention' && <Mention n={n} />) ||
@@ -165,7 +167,7 @@ const defaultOnClick = n => {
165167
if (type === 'WithdrawlPaid') return { href: `/withdrawals/${n.id}` }
166168
if (type === 'Referral') return { href: '/referrals/month' }
167169
if (type === 'ReferralReward') return { href: '/referrals/month' }
168-
if (type === 'Streak') return {}
170+
if (['CowboyHat', 'NewHorse', 'LostHorse', 'NewGun', 'LostGun'].includes(type)) return {}
169171
if (type === 'TerritoryTransfer') return { href: `/~${n.sub.name}` }
170172

171173
if (!n.item) return {}
@@ -174,30 +176,64 @@ const defaultOnClick = n => {
174176
return itemLink(n.item)
175177
}
176178

177-
function Streak ({ n }) {
178-
function blurb (n) {
179-
const type = n.type ?? 'COWBOY_HAT'
180-
const index = Number(n.id) % Math.min(FOUND_BLURBS[type].length, LOST_BLURBS[type].length)
181-
if (n.days) {
182-
return `After ${numWithUnits(n.days, {
183-
abbreviate: false,
184-
unitSingular: 'day',
185-
unitPlural: 'days'
186-
})}, ` + LOST_BLURBS[type][index]
187-
}
179+
function blurb (n) {
180+
const type = n.__typename === 'CowboyHat'
181+
? 'COWBOY_HAT'
182+
: (n.__typename.includes('Horse') ? 'HORSE' : 'GUN')
183+
const index = Number(n.id) % Math.min(FOUND_BLURBS[type].length, LOST_BLURBS[type].length)
184+
const lost = n.days || n.__typename.includes('Lost')
185+
return lost ? LOST_BLURBS[type][index] : FOUND_BLURBS[type][index]
186+
}
188187

189-
return FOUND_BLURBS[type][index]
188+
function CowboyHat ({ n }) {
189+
const Icon = n.days ? BaldIcon : CowboyHatIcon
190+
191+
let body = ''
192+
if (n.days) {
193+
body = `After ${numWithUnits(n.days, {
194+
abbreviate: false,
195+
unitSingular: 'day',
196+
unitPlural: 'days'
197+
})}, `
190198
}
191199

192-
const Icon = n.days
193-
? n.type === 'GUN' ? HolsterIcon : n.type === 'HORSE' ? SaddleIcon : BaldIcon
194-
: n.type === 'GUN' ? GunIcon : n.type === 'HORSE' ? HorseIcon : CowboyHatIcon
200+
body += `you ${n.days ? 'lost your' : 'found a'} cowboy hat`
201+
202+
return (
203+
<div className='d-flex'>
204+
<div style={{ fontSize: '2rem' }}><Icon className='fill-grey' height={40} width={40} /></div>
205+
<div className='ms-1 p-1'>
206+
<span className='fw-bold'>{body}</span>
207+
<div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div>
208+
</div>
209+
</div>
210+
)
211+
}
212+
213+
function Horse ({ n }) {
214+
const found = n.__typename.includes('New')
215+
const Icon = found ? HorseIcon : SaddleIcon
216+
217+
return (
218+
<div className='d-flex'>
219+
<div style={{ fontSize: '2rem' }}><Icon className='fill-grey' height={40} width={40} /></div>
220+
<div className='ms-1 p-1'>
221+
<span className='fw-bold'>you {found ? 'found a' : 'lost your'} horse</span>
222+
<div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div>
223+
</div>
224+
</div>
225+
)
226+
}
227+
228+
function Gun ({ n }) {
229+
const found = n.__typename.includes('New')
230+
const Icon = found ? GunIcon : HolsterIcon
195231

196232
return (
197233
<div className='d-flex'>
198234
<div style={{ fontSize: '2rem' }}><Icon className='fill-grey' height={40} width={40} /></div>
199235
<div className='ms-1 p-1'>
200-
<span className='fw-bold'>you {n.days ? 'lost your' : 'found a'} {n.type.toLowerCase().replace('_', ' ')}</span>
236+
<span className='fw-bold'>you {found ? 'found a' : 'lost your'} gun</span>
201237
<div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div>
202238
</div>
203239
</div>

fragments/comments.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { gql } from '@apollo/client'
44
const STREAK_FIELDS = gql`
55
fragment StreakFields on User {
66
optional {
7-
streak
8-
gunStreak
9-
horseStreak
7+
streak
8+
hasSendWallet
9+
hasRecvWallet
1010
}
1111
}
1212
`

0 commit comments

Comments
 (0)