Verified contributors (#474)

* `isContributor`, `hideIsContributor` user fields and basic UI decoration on profile page

* Update verified contributor decoration on profile page

* Add contributors instructions

* update setting label

* Remove `isContributor` from DB, load contributors from file into memory

* fix merge error

---------

Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
This commit is contained in:
SatsAllDay 2023-09-18 14:57:02 -04:00 committed by GitHub
parent 8ab58fff87
commit bc2363dfab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 5 deletions

View File

@ -1,3 +1,5 @@
import { readFile } from 'fs/promises'
import { join, resolve } from 'path'
import { GraphQLError } from 'graphql'
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
import { msatsToSats } from '../../lib/format'
@ -5,6 +7,20 @@ import { bioSchema, emailSchema, settingsSchema, ssValidate, userSchema } from '
import { getItem, updateItem, filterClause, createItem } from './item'
import { datePivot } from '../../lib/time'
const contributors = new Set()
const loadContributors = async (set) => {
try {
const fileContent = await readFile(resolve(join(process.cwd(), 'contributors.txt')), 'utf-8')
fileContent.split('\n')
.map(line => line.trim())
.filter(line => !!line)
.forEach(name => set.add(name))
} catch (err) {
console.error('Error loading contributors', err)
}
}
export function within (table, within) {
let interval = ' AND "' + table + '".created_at >= $1 - INTERVAL '
switch (within) {
@ -817,6 +833,16 @@ export default {
})
return !!subscription?.commentsSubscribedAt
},
isContributor: async (user, args, { me }) => {
// lazy init contributors only once
if (contributors.size === 0) {
await loadContributors(contributors)
}
if (me?.id === user.id) {
return contributors.has(user.name)
}
return !user.hideIsContributor && contributors.has(user.name)
}
}
}

View File

@ -25,7 +25,7 @@ export default gql`
noteInvites: Boolean!, noteJobIndicator: Boolean!, noteCowboyHat: Boolean!, hideInvoiceDesc: Boolean!,
hideFromTopUsers: Boolean!, hideCowboyHat: Boolean!, clickToLoadImg: Boolean!,
wildWestMode: Boolean!, greeterMode: Boolean!, nostrPubkey: String, nostrRelays: [String!], hideBookmarks: Boolean!,
noteForwardedSats: Boolean!, hideWalletBalance: Boolean!): User
noteForwardedSats: Boolean!, hideWalletBalance: Boolean!, hideIsContributor: Boolean!): User
setPhoto(photoId: ID!): Int!
upsertBio(bio: String!): User!
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
@ -92,6 +92,8 @@ export default gql`
greeterMode: Boolean!
lastCheckedJobs: String
authMethods: AuthMethods!
isContributor: Boolean!
hideIsContributor: Boolean!
meSubscriptionPosts: Boolean!
meSubscriptionComments: Boolean!
}

View File

@ -212,6 +212,7 @@ function HeaderHeader ({ user }) {
: <span>never</span>}
</small>
<small className='text-muted d-flex-inline'>longest cowboy streak: {user.maxStreak !== null ? user.maxStreak : 'none'}</small>
{user.isContributor && <small className='text-muted'>🧑💻 verified stacker.news contributor</small>}
</div>
</div>
</div>

4
contributors.txt Normal file
View File

@ -0,0 +1,4 @@
k00b
kr
ekzyis
WeAreAllSatoshi

View File

@ -36,6 +36,8 @@ export const ME = gql`
lastCheckedJobs
hideWelcomeBanner
hideWalletBalance
isContributor
hideIsContributor
}
}`
@ -57,6 +59,7 @@ export const SETTINGS_FIELDS = gql`
hideFromTopUsers
hideCowboyHat
hideBookmarks
hideIsContributor
clickToLoadImg
hideWalletBalance
nostrPubkey
@ -88,14 +91,14 @@ mutation setSettings($tipDefault: Int!, $turboTipping: Boolean!, $fiatCurrency:
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $noteCowboyHat: Boolean!, $hideInvoiceDesc: Boolean!,
$hideFromTopUsers: Boolean!, $hideCowboyHat: Boolean!, $clickToLoadImg: Boolean!,
$wildWestMode: Boolean!, $greeterMode: Boolean!, $nostrPubkey: String, $nostrRelays: [String!], $hideBookmarks: Boolean!,
$noteForwardedSats: Boolean!, $hideWalletBalance: Boolean!) {
$noteForwardedSats: Boolean!, $hideWalletBalance: Boolean!, $hideIsContributor: Boolean!) {
setSettings(tipDefault: $tipDefault, turboTipping: $turboTipping, fiatCurrency: $fiatCurrency,
noteItemSats: $noteItemSats, noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
noteJobIndicator: $noteJobIndicator, noteCowboyHat: $noteCowboyHat, hideInvoiceDesc: $hideInvoiceDesc,
hideFromTopUsers: $hideFromTopUsers, hideCowboyHat: $hideCowboyHat, clickToLoadImg: $clickToLoadImg,
wildWestMode: $wildWestMode, greeterMode: $greeterMode, nostrPubkey: $nostrPubkey, nostrRelays: $nostrRelays, hideBookmarks: $hideBookmarks,
noteForwardedSats: $noteForwardedSats, hideWalletBalance: $hideWalletBalance) {
noteForwardedSats: $noteForwardedSats, hideWalletBalance: $hideWalletBalance, hideIsContributor: $hideIsContributor) {
...SettingsFields
}
}
@ -150,6 +153,7 @@ export const USER_FIELDS = gql`
stacked
since
photoId
isContributor
meSubscriptionPosts
meSubscriptionComments
}`

View File

@ -224,7 +224,8 @@ export const settingsSchema = object({
).max(NOSTR_MAX_RELAY_NUM,
({ max, value }) => `${Math.abs(max - value.length)} too many`),
hideBookmarks: boolean(),
hideWalletBalance: boolean()
hideWalletBalance: boolean(),
hideIsContributor: boolean()
})
const warningMessage = 'If I logout, even accidentally, I will never be able to access my account again'

View File

@ -23,6 +23,7 @@ import { useShowModal } from '../components/modal'
import { authErrorMessage } from '../components/login'
import { NostrAuth } from '../components/nostr-auth'
import { useToast } from '../components/toast'
import { useMe } from '../components/me'
export const getServerSideProps = getGetServerSideProps({ query: SETTINGS, authRequired: true })
@ -32,6 +33,7 @@ function bech32encode (hexString) {
export default function Settings ({ ssrData }) {
const toaster = useToast()
const me = useMe()
const [setSettings] = useMutation(SET_SETTINGS, {
update (cache, { data: { setSettings } }) {
cache.modify({
@ -77,7 +79,8 @@ export default function Settings ({ ssrData }) {
nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '',
nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : [''],
hideBookmarks: settings?.hideBookmarks,
hideWalletBalance: settings?.hideWalletBalance
hideWalletBalance: settings?.hideWalletBalance,
hideIsContributor: settings?.hideIsContributor
}}
schema={settingsSchema}
onSubmit={async ({ tipDefault, nostrPubkey, nostrRelays, ...values }) => {
@ -241,6 +244,12 @@ export default function Settings ({ ssrData }) {
name='clickToLoadImg'
groupClassName='mb-0'
/>
{me.isContributor &&
<Checkbox
label={<>hide that I'm a stacker.news contributor</>}
name='hideIsContributor'
groupClassName='mb-0'
/>}
<Checkbox
label={<>hide my bookmarks from other stackers</>}
name='hideBookmarks'

View File

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

View File

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