diff --git a/api/resolvers/user.js b/api/resolvers/user.js
index 91c13968..90e9c22c 100644
--- a/api/resolvers/user.js
+++ b/api/resolvers/user.js
@@ -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)
}
}
}
diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js
index 95b102e6..3cb97a9f 100644
--- a/api/typeDefs/user.js
+++ b/api/typeDefs/user.js
@@ -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!
}
diff --git a/components/user-header.js b/components/user-header.js
index aaa0f1bf..6bd88da6 100644
--- a/components/user-header.js
+++ b/components/user-header.js
@@ -212,6 +212,7 @@ function HeaderHeader ({ user }) {
: never}
longest cowboy streak: {user.maxStreak !== null ? user.maxStreak : 'none'}
+ {user.isContributor && 🧑💻✅ verified stacker.news contributor}
diff --git a/contributors.txt b/contributors.txt
new file mode 100644
index 00000000..5f59b521
--- /dev/null
+++ b/contributors.txt
@@ -0,0 +1,4 @@
+k00b
+kr
+ekzyis
+WeAreAllSatoshi
diff --git a/fragments/users.js b/fragments/users.js
index d0d17e54..6871ceac 100644
--- a/fragments/users.js
+++ b/fragments/users.js
@@ -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
}`
diff --git a/lib/validate.js b/lib/validate.js
index 0062f2f2..db5e8c28 100644
--- a/lib/validate.js
+++ b/lib/validate.js
@@ -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'
diff --git a/pages/settings.js b/pages/settings.js
index eb8d8de2..87206198 100644
--- a/pages/settings.js
+++ b/pages/settings.js
@@ -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 &&
+ hide that I'm a stacker.news contributor>}
+ name='hideIsContributor'
+ groupClassName='mb-0'
+ />}
hide my bookmarks from other stackers>}
name='hideBookmarks'
diff --git a/prisma/migrations/20230906010648_verified_contributors/migration.sql b/prisma/migrations/20230906010648_verified_contributors/migration.sql
new file mode 100644
index 00000000..3ce4fb00
--- /dev/null
+++ b/prisma/migrations/20230906010648_verified_contributors/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "users" ADD COLUMN "hideIsContributor" BOOLEAN NOT NULL DEFAULT false;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index c3fceb8d..5f6f3280 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -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")