Skip to content

Commit

Permalink
feat: improve community provisioning
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Oct 20, 2024
1 parent 27c9740 commit 626268b
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 71 deletions.
179 changes: 157 additions & 22 deletions cli/src/commands/provision-communities.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,102 @@
import { CommunityCreateOptions, getExplorerUrl } from '@pubkey-protocol/sdk'
import { IdentityProvider } from '@pubkey-protocol/anchor'
import { CommunityCreateOptions, CommunityUpdateOptions, getExplorerUrl } from '@pubkey-protocol/sdk'
import { getConfig } from '../utils/get-config'
import { createOrGetConfig } from './create-or-get-config'

export const provisionCommunities: Omit<CommunityCreateOptions, 'authority' | 'communityAuthority'>[] = [
{
name: 'PubKey',
avatarUrl: 'https://github.com/pubkeyapp.png',
type ProvisionCommunityCreate = Omit<CommunityCreateOptions, 'authority' | 'communityAuthority'>
type ProvisionCommunityUpdate = Omit<CommunityUpdateOptions, 'authority' | 'feePayer' | 'slug'>

interface MapItem {
create: ProvisionCommunityCreate
providers?: IdentityProvider[]
signers?: string[]
update?: ProvisionCommunityUpdate
}
type ProvisionMap = Record<string, MapItem>

const provisionMap: ProvisionMap = {
pubkey: {
create: {
name: 'PubKey',
slug: 'pubkey',
avatarUrl: 'https://github.com/pubkeyapp.png',
},
update: {
discord: 'https://discord.gg/XxuZQeDPNf',
github: 'https://github.com/pubkeyapp',
farcaster: 'https://warpcast.com/pubkey',
website: 'https://pubkey.app',
x: 'https://x.com/pubkeyapp',
},
providers: [IdentityProvider.Discord, IdentityProvider.Github, IdentityProvider.X],
signers: [
'PPLAPR9qXjkQ8QhY7nBXgsEDXVnZ4VBghzNXfnmT4Uw',
'Dd1JSwojUsptwFa97A3WRZU1SijCWYo9Qa3xLxT8yzb7',
'BumrJWH5kf4MXZ5bEg7VyZY6oXAMr78jXC1mFiDAE3u3',
],
},
{
name: 'Legends of SOL',
slug: 'los',
avatarUrl: 'https://github.com/legends-of-sol.png',
los: {
create: {
name: 'Legends of SOL',
slug: 'los',
avatarUrl: 'https://github.com/legends-of-sol.png',
},
update: {
discord: 'https://discord.gg/kQb7YQYePS',
github: 'https://github.com/legends-of-sol',
website: 'https://legendsofsol.com',
x: 'https://x.com/Legends_of_SOL',
},
providers: [IdentityProvider.Discord, IdentityProvider.Github, IdentityProvider.X],
signers: ['PPLAPR9qXjkQ8QhY7nBXgsEDXVnZ4VBghzNXfnmT4Uw'],
},
{
name: `Dean's List`,
slug: 'deanslist',
avatarUrl: 'https://github.com/dean-s-list.png',
deanslist: {
create: {
name: `Dean's List`,
slug: 'deanslist',
avatarUrl: 'https://github.com/dean-s-list.png',
},
update: {
discord: 'https://discord.gg/deanslist',
github: 'https://github.com/dean-s-list',
website: 'https://deanslist.services',
x: 'https://x.com/deanslistDAO',
},
providers: [IdentityProvider.Discord],
signers: ['PPLAPR9qXjkQ8QhY7nBXgsEDXVnZ4VBghzNXfnmT4Uw'],
},
{
name: `Gib Work`,
slug: 'gibwork',
avatarUrl: 'https://github.com/gibwork.png',
gibwork: {
create: {
name: `Gib Work`,
slug: 'gibwork',
avatarUrl: 'https://github.com/gibwork.png',
},
update: {
discord: 'https://discord.gg/G54VHkcuHS',
github: 'https://github.com/gibwork',
website: 'https://gib.work',
telegram: 'https://t.me/gib_work',
x: 'https://x.com/gib_work',
},
providers: [IdentityProvider.Discord, IdentityProvider.Github, IdentityProvider.X],
signers: ['PPLAPR9qXjkQ8QhY7nBXgsEDXVnZ4VBghzNXfnmT4Uw'],
},
{
name: 'Marinade',
avatarUrl: 'https://github.com/marinade-finance.png',
marinade: {
create: {
name: 'Marinade',
slug: 'marinade',
avatarUrl: 'https://github.com/marinade-finance.png',
},
update: {
discord: 'https://discord.gg/yTdH8YkYKg',
github: 'https://github.com/marinade-finance',
website: 'https://marinade.finance',
x: 'https://x.com/MarinadeFinance',
},
providers: [IdentityProvider.Discord],
signers: ['PPLAPR9qXjkQ8QhY7nBXgsEDXVnZ4VBghzNXfnmT4Uw'],
},
]
}

export async function provisionCommunitiesIfNeeded() {
const { authority, connection, endpoint, cluster, sdk } = await getConfig()
Expand All @@ -38,7 +108,15 @@ export async function provisionCommunitiesIfNeeded() {
const existingNames = existing.map((c) => c.name)
console.log(` -> Found ${existing.length} communities`, existingNames.join(', '))

for (const { avatarUrl, name, slug } of provisionCommunities.filter((c) => !existingNames.includes(c.name))) {
const communitiesMap = Object.keys(provisionMap)
.filter((slug) => !existingNames.includes(slug))
.reduce((map, slug) => ({ ...map, [slug]: provisionMap[slug] }), {} as ProvisionMap)

console.log('communitiesMap', communitiesMap)

const communitiesCreate = Object.keys(provisionMap).map((slug) => provisionMap[slug].create)

for (const { avatarUrl, name, slug } of communitiesCreate.filter((c) => !existingNames.includes(c.name))) {
console.log(` -> Creating community: ${name}`)
const { input, tx: transaction } = await sdk.communityCreate({
authority: authority.publicKey,
Expand All @@ -52,4 +130,61 @@ export async function provisionCommunitiesIfNeeded() {
console.log(` -> Created community: ${name} ${input.slug}`, s)
console.log(getExplorerUrl(`tx/${s}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, cluster, endpoint))
}

const communitiesUpdate = Object.keys(provisionMap)
.filter((slug) => Object.keys(provisionMap[slug].update ?? {}).length > 0)
.map((slug) => ({ ...provisionMap[slug].update, slug }))

for (const input of communitiesUpdate) {
console.log(` -> Updating community: ${input.slug}`)
const { tx: transaction } = await sdk.communityUpdate({
...input,
authority: authority.publicKey,
feePayer: authority.publicKey,
})
transaction.sign([authority])
const s = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true })
console.log(` -> Updated community: ${input.slug}`, s)
console.log(getExplorerUrl(`tx/${s}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, cluster, endpoint))
}

const communitiesSignerAdd = Object.keys(provisionMap)
.filter((slug) => Object.keys(provisionMap[slug].signers ?? []).length > 0)
.map((slug) => ({ signers: provisionMap[slug].signers, slug }))

for (const signers of communitiesSignerAdd) {
for (const signer of signers.signers) {
console.log(` -> Adding signer ${signer} to community: ${signers.slug}`)
const { tx: transaction } = await sdk.communitySignerAdd({
slug: signers.slug,
signer,
authority: authority.publicKey,
feePayer: authority.publicKey,
})
transaction.sign([authority])
const s = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true })
console.log(` -> Added signers to community: ${signers.slug}`, s)
console.log(getExplorerUrl(`tx/${s}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, cluster, endpoint))
}
}

const communitiesProviderEnable = Object.keys(provisionMap)
.filter((slug) => Object.keys(provisionMap[slug].providers ?? []).length > 0)
.map((slug) => ({ providers: provisionMap[slug].providers, slug }))

for (const providers of communitiesProviderEnable) {
for (const provider of providers.providers) {
console.log(` -> Enabling provider ${provider} for community: ${providers.slug}`)
const { tx: transaction } = await sdk.communityProviderEnable({
slug: providers.slug,
provider,
authority: authority.publicKey,
feePayer: authority.publicKey,
})
transaction.sign([authority])
const s = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true })
console.log(` -> Enabled provider ${provider} for community: ${providers.slug}`, s)
console.log(getExplorerUrl(`tx/${s}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`, cluster, endpoint))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { UiDebug, UiLoader, UiStack } from '@pubkey-ui/core'
import { Button, Group } from '@mantine/core'
import { UiDebugModal, UiLoader, UiPage, UiStack } from '@pubkey-ui/core'
import { IconUsers } from '@tabler/icons-react'
import { Link } from 'react-router-dom'
import { useQueryCommunityGetAll } from '../data-access'
import { PubkeyProtocolUiCommunityGrid } from '../ui'

export function PubkeyCommunityFeatureList({ basePath }: { basePath: string }) {
const query = useQueryCommunityGetAll()

return query.isLoading ? (
<UiLoader />
) : (
<UiStack>
<PubkeyProtocolUiCommunityGrid communities={query.data ?? []} basePath={basePath} />
<UiDebug data={query.data ?? []} />
</UiStack>
return (
<UiPage
leftAction={<IconUsers />}
title="Communities"
rightAction={
<Group>
<UiDebugModal data={query.data ?? []} />
<Button size="xs" component={Link} to="create">
Create
</Button>
</Group>
}
>
{query.isLoading ? (
<UiLoader />
) : (
<UiStack>
<PubkeyProtocolUiCommunityGrid communities={query.data ?? []} basePath={basePath} />
</UiStack>
)}
</UiPage>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { Button, Group } from '@mantine/core'
import { UiPage } from '@pubkey-ui/core'
import { IconUsers } from '@tabler/icons-react'
import { Link, useRoutes } from 'react-router-dom'
import { useRoutes } from 'react-router-dom'
import { PubKeyProtocolLoader } from '../../pubkey-protocol'
import { PubkeyCommunityFeatureCreate } from './pubkey-community-feature-create'
import { PubkeyCommunityFeatureDetail } from './pubkey-community-feature-detail'
Expand All @@ -11,21 +8,7 @@ export default function PubkeyCommunityRoutes({ basePath }: { basePath: string }
const routes = useRoutes([
{
index: true,
element: (
<UiPage
leftAction={<IconUsers />}
title="Communities"
rightAction={
<Group>
<Button size="xs" component={Link} to="create">
Create
</Button>
</Group>
}
>
<PubkeyCommunityFeatureList basePath={basePath} />
</UiPage>
),
element: <PubkeyCommunityFeatureList basePath={basePath} />,
},
{ path: 'create', element: <PubkeyCommunityFeatureCreate /> },
{ path: ':slug/*', element: <PubkeyCommunityFeatureDetail /> },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { UiDebugModal } from '@pubkey-ui/core'
import { ReactNode } from 'react'
import { PubkeyProtocolUiCommunityAnchor } from './pubkey-protocol-ui-community-anchor'
import { PubkeyProtocolUiCommunityAvatar } from './pubkey-protocol-ui-community-avatar'
import { PubkeyProtocolUiCommunitySocials } from './pubkey-protocol-ui-community-socials'

export function PubkeyProtocolUiCommunityGridItem({
children,
Expand All @@ -20,7 +21,7 @@ export function PubkeyProtocolUiCommunityGridItem({
<PubkeyProtocolUiCommunityAvatar community={community} />
<Stack gap={0}>
<PubkeyProtocolUiCommunityAnchor community={community} to={`${basePath}/${community.slug}`} />
{children ?? community.slug}
{children ?? <PubkeyProtocolUiCommunitySocials community={community} />}
</Stack>
</Group>
<UiDebugModal data={community} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Button, Group, TextInput } from '@mantine/core'
import { ActionIcon, TextInput } from '@mantine/core'
import { useForm } from '@mantine/form'
import { PubKeyCommunity } from '@pubkey-protocol/anchor'
import { UiStack } from '@pubkey-ui/core'
import { IconPlus } from '@tabler/icons-react'

export function PubkeyProtocolUiCommunitySignerForm({
community,
Expand All @@ -23,25 +24,20 @@ export function PubkeyProtocolUiCommunitySignerForm({
})

return (
<form
onSubmit={form.onSubmit((values) =>
submit({ ...values }).then(() => {
form.reset()
}),
)}
>
<form onSubmit={form.onSubmit((values) => submit({ ...values }).then(() => form.reset()))}>
<UiStack>
<TextInput
description="Public key of the signer to add to the community."
label="Signer"
name="signer"
required
{...form.getInputProps('signer')}
rightSection={
<ActionIcon disabled={!form.isValid()} variant="light" color="green" type="submit">
<IconPlus size="16" />
</ActionIcon>
}
/>
<Group justify="right">
<Button disabled={!form.isValid()} type="submit">
Add
</Button>
</Group>
</UiStack>
</form>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export function PubkeyProtocolUiCommunitySocials({ community, ...props }: GroupP

function MaybeSocial({ link, social }: { link?: string; social: Social }) {
return link ? (
<Tooltip label={link.replace('https://', '')}>
<ActionIcon component={'a'} href={link} target="_blank" size="sm" variant="light">
<Tooltip label={link.replace('https://', '')} withArrow>
<ActionIcon component={'a'} href={link} target="_blank" size="xs" variant="light">
<SocialIcon social={social} />
</ActionIcon>
</Tooltip>
Expand All @@ -50,17 +50,17 @@ function MaybeSocial({ link, social }: { link?: string; social: Social }) {
function SocialIcon({ social }: { social: Social }) {
switch (social) {
case Social.Discord:
return <IconBrandDiscordFilled size={16} />
return <IconBrandDiscordFilled size={12} />
case Social.Farcaster:
return <IconLetterW size={16} />
return <IconLetterW size={12} />
case Social.Github:
return <IconBrandGithubFilled size={16} />
return <IconBrandGithubFilled size={12} />
case Social.Telegram:
return <IconBrandTelegram size={16} />
return <IconBrandTelegram size={12} />
case Social.Website:
return <IconWorldWww size={16} />
return <IconWorldWww size={12} />
case Social.X:
return <IconBrandXFilled size={16} />
return <IconBrandXFilled size={12} />
default:
return null
}
Expand Down

0 comments on commit 626268b

Please sign in to comment.