Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrations #122

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c88bc8a
feat: create lock initial form
trbutler4 Jul 13, 2023
469ef6e
feat: only show create lock form if wallet connected
trbutler4 Jul 13, 2023
4711325
feat: added unlock provider
trbutler4 Jul 13, 2023
f736a43
feat: able to deploy locks on goerli
trbutler4 Jul 13, 2023
ac3f121
feat: moved unlock contract interaction to hook
trbutler4 Jul 14, 2023
f2fa35e
fix: cleanup up linting warnings
trbutler4 Jul 14, 2023
25c41a4
feat: added graph api key
trbutler4 Jul 17, 2023
6acca27
feat: graphclient integrated, pulling rough data to initial component
trbutler4 Jul 19, 2023
306608b
feat: lock name displayed on user locks component
trbutler4 Jul 19, 2023
6f91360
feat: query based on connected wallet
trbutler4 Jul 19, 2023
9a9af0f
feat: lock previews redirect to lock id
trbutler4 Jul 19, 2023
8a08331
feat: extract id from url
trbutler4 Jul 19, 2023
ff962ec
feat: barebones lock stats
trbutler4 Jul 19, 2023
ff4101d
feat: rough user keys component
trbutler4 Jul 19, 2023
2bf58da
feat: moved unlock subgraph query functions into hook
trbutler4 Jul 20, 2023
dfa553c
feat: change query endpoint based on connected chain
trbutler4 Jul 20, 2023
f45926e
feat: added lock stats to unlock subgraph hook
trbutler4 Jul 20, 2023
8281d8f
refactor: changed lock deploy logic
trbutler4 Jul 20, 2023
51b45db
feat: lock checkout on lock page
trbutler4 Jul 20, 2023
024160a
feat: added example paywall page
trbutler4 Jul 22, 2023
8261029
feat: formatting, general finalization stuff
trbutler4 Jul 25, 2023
d4fd96e
feat: created readme description
trbutler4 Jul 26, 2023
52f7323
feat: added paywall example button, last minute design tweaks
trbutler4 Jul 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,608 changes: 1,608 additions & 0 deletions .graphclient/index.ts

Large diffs are not rendered by default.

1,115 changes: 1,115 additions & 0 deletions .graphclient/schema.graphql

Large diffs are not rendered by default.

13,594 changes: 13,594 additions & 0 deletions .graphclient/sources/unlock/introspectionSchema.ts

Large diffs are not rendered by default.

1,115 changes: 1,115 additions & 0 deletions .graphclient/sources/unlock/schema.graphql

Large diffs are not rendered by default.

1,104 changes: 1,104 additions & 0 deletions .graphclient/sources/unlock/types.ts

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .graphclientrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sources:
- name: unlock
handler:
graphql:
endpoint: 'https://api.thegraph.com/subgraphs/name/unlock-protocol/{context.network:goerli-v2}'

documents:
- ./integrations/unlock/queries/user-locks-query.graphql
- ./integrations/unlock/queries/lock-stats-query.graphql
- ./integrations/unlock/queries/user-keys-query.graphql
12 changes: 12 additions & 0 deletions app/(general)/integration/unlock/[lockId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import LockStats from '@/integrations/unlock/components/lock-stats'
import ButtonKeyCheckout from '@/integrations/unlock/components/button-key-checkout'

export default function UnlockLockPage({ params }: { params: { lockId: string } }) {
return (
<div>
<h1 className="text-center font-bold">Lock Stats</h1>
<LockStats lockId={params.lockId} />
<ButtonKeyCheckout lockId={params.lockId} />
</div>
)
}
58 changes: 58 additions & 0 deletions app/(general)/integration/unlock/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use client'
import { ReactNode } from 'react'

import { motion } from 'framer-motion'
import Image from 'next/image'
import Balancer from 'react-wrap-balancer'

import { IsDarkTheme } from '@/components/shared/is-dark-theme'
import { IsLightTheme } from '@/components/shared/is-light-theme'
import { LinkComponent } from '@/components/shared/link-component'
import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design'
import { turboIntegrations } from '@/data/turbo-integrations'

const integrationData = turboIntegrations.unlock

export default function UnlockLayoutIntegration({ children }: { children: ReactNode }) {
return (
<>
<div className="flex-center flex-col items-center justify-center text-center">
<motion.div
animate="show"
className="max-w-3xl px-5 text-center xl:px-0"
initial="hidden"
viewport={{ once: true }}
whileInView="show"
variants={{
hidden: {},
show: {
transition: {
staggerChildren: 0.15,
},
},
}}>
<IsLightTheme>
<Image alt="Starter logo" className="mx-auto" height={100} src={integrationData.imgDark} width={100} />
</IsLightTheme>
<IsDarkTheme>
<Image alt="Starter logo" className="mx-auto" height={100} src={integrationData.imgLight} width={100} />
</IsDarkTheme>
<motion.h1
className="text-gradient-sand my-8 text-center text-4xl font-bold tracking-[-0.02em] drop-shadow-sm md:text-8xl md:leading-[6rem]"
variants={FADE_DOWN_ANIMATION_VARIANTS}>
{integrationData.name}
</motion.h1>
<motion.p className="my-4 text-xl" variants={FADE_DOWN_ANIMATION_VARIANTS}>
<Balancer>{integrationData.description}</Balancer>
</motion.p>
<motion.div className="my-4 text-xl" variants={FADE_DOWN_ANIMATION_VARIANTS}>
<LinkComponent href={integrationData.url}>
<button className="btn btn-primary">Documentation</button>
</LinkComponent>
</motion.div>
</motion.div>
</div>
<section>{children}</section>
</>
)
}
31 changes: 31 additions & 0 deletions app/(general)/integration/unlock/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { WalletConnect } from '@/components/blockchain/wallet-connect'
import { IsWalletConnected } from '@/components/shared/is-wallet-connected'
import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected'
import FormDeployLock from '@/integrations/unlock/components/form-deploy-lock'
import UserLocks from '@/integrations/unlock/components/user-locks'
import UserKeys from '@/integrations/unlock/components/user-keys'
import { Button } from '@/components/ui/button'

export default function UnlockIntegration() {
return (
<div>
<div className="flex justify-center items-center m-10">
<a href="/integration/unlock/paywall">
<Button>Paywall Example</Button>
</a>
</div>
<IsWalletConnected>
<h1 className="text-center font-bold">Create a Lock</h1>
<FormDeployLock />
<h1 className="text-center font-bold">Created Locks</h1>
<UserLocks />
<h1 className="text-center font-bold">Owned Keys</h1>
<UserKeys />
</IsWalletConnected>

<IsWalletDisconnected>
<WalletConnect />
</IsWalletDisconnected>
</div>
)
}
10 changes: 10 additions & 0 deletions app/(general)/integration/unlock/paywall/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import PaywallScript from '@/integrations/unlock/components/paywall-script'

export default function PaywallPage() {

return (
<div>
<PaywallScript />
</div>
)
}
8 changes: 8 additions & 0 deletions data/turbo-integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ export const turboIntegrations = {
imgLight: '/integrations/connext.png',
imgDark: '/integrations/connext.png',
},
unlock: {
name: 'Unlock Protocol',
href: '/integration/unlock',
url: 'https://docs.unlock-protocol.com/?_ga=2.100163804.370043445.1690238160-1370418924.1685652724',
description: 'Smart contracts built specifically for memberships and subscriptions',
imgLight: '/logo-gradient.png',
imgDark: '/logo-dark.png',
},
starter: {
name: 'Starter Template',
href: '/integration/starter',
Expand Down
85 changes: 85 additions & 0 deletions integrations/unlock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Unlock Protocol - TurboETH Integration

This integration allows you to create locks, view owned keys and locks, as well as view stats for specific locks.
The also includes an example of using a lock as a paywall, and will require the lock be purchased before access is granted.

## Features
- Implements The Graph protocol to query information from unlock protocol.
- Lists all owned locks on current chain (supports mumbai and goerli)
- Lists all owned keys on current chain (supports mumbai and goerli)
- Interact with Unlock Protocol to create a new lock via the create lock form component
- Paywall example with an already deployed example lock
- View lock statistics, and purchase keys from created locks.

## Components
`ButtonKeyCheckout()`
Opens the checkout modal for a specified key.

'FromDeployLock()'
For to create and deploy new locks.

'KeyPreview()'
Simply a preview card for the user keys list.

'LockPreview()'
Simply a preview card for the user locks list.

'LockStats()'
Simple list of lock information for a specified lock.

'PaywalScript()'
Configures the example paywall.

'UserKeys()'
Displays all keys owned by the user for the connected chain.

`UserLocks()`
Displays all locks owned by the user for the connected chain.

## Hooks
`UseDeployLock()`
React hook to interact with unlock protocol smart contracts.

`UseUnlockSubgraph()`
React hook to interact with The Graph protocol, and query information
from unlock protocol subgraphs.

## Queries
`LockStatsQuery`
Retrieves information for a specified lock.

`UserKeysQuery`
Retreives all keys owned by user.

`UserLocksQuery`
Retrieves all locks owned by user.

## File Structure
```
integrations/unlock
├── components
│   ├── button-key-checkout.tsx
│   ├── form-deploy-lock.tsx
│   ├── key-preview.tsx
│   ├── lock-preview.tsx
│   ├── lock-stats.tsx
│   ├── paywall-script.tsx
│   ├── user-keys.tsx
│   └── user-locks.tsx
├── hooks
│   ├── use-deploy-lock.tsx
│   └── use-unlock-subgraph.tsx
├── queries
│   ├── lock-stats-query.graphql
│   ├── user-keys-query.graphql
│   └── user-locks-query.graphql
└── README.md

app/(general)/integration/unlock
├── layout.tsx
├── [lockId]
│   └── page.tsx
├── page.tsx
└── paywall
└── page.tsx
```
41 changes: 41 additions & 0 deletions integrations/unlock/components/button-key-checkout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client'

import { Button } from '@/components/ui/button'
import useUnlockSubgraph from '@/integrations/unlock/hooks/use-unlock-subgraph'
import { useNetwork } from 'wagmi'
import { useState, useEffect } from 'react'

export default function ButtonKeyCheckout({ lockId }: { lockId: string }) {
const { chain } = useNetwork()
const { getLockStats } = useUnlockSubgraph()
const [lockAddress, setLockAddress] = useState<string>('')

const baseUrl = 'https://app.unlock-protocol.com/checkout?'
const paywallConfig = {
locks: {
[lockAddress]: {
network: chain?.id,
},
},
}
const redirectUri: string = window.location.href
const checkoutUrl = `${baseUrl}&paywallConfig=${encodeURIComponent(JSON.stringify(paywallConfig))}&redirectUri${redirectUri}`

useEffect(() => {
async function fetchLockAddress() {
const stats = await getLockStats({ lockId })
setLockAddress(stats?.locks[0].address)
}
fetchLockAddress()
}, [lockId])

return (
<div>
{lockAddress && (
<a href={checkoutUrl} rel="noreferrer" target="_blank">
<Button>Checkout</Button>
</a>
)}
</div>
)
}
93 changes: 93 additions & 0 deletions integrations/unlock/components/form-deploy-lock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use client'

import { useState } from 'react'

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'

import { useDeployLock } from '../hooks/use-deploy-lock'

export default function FormDeployLock() {
const [lockName, setLockName] = useState<string>('test lock')
const [maxKeys, setMaxKeys] = useState<number>(10)
const [lastMaxKeys, setLastMaxKeys] = useState<number>(0) // NOTE: used to store the last max keys before unlimited keys
const [duration, setDuration] = useState<number>(30)
const [lastDuration, setLastDuration] = useState<number>(0) // NOTE: used to store the last duration before unlimited duration
const [keyPrice, setKeyPrice] = useState<string>('0.01')
const [unlimitedKeys, setUnlimitedKeys] = useState<boolean>(false)
const [unlimitedDuration, setUnlimitedDuration] = useState<boolean>(false)

const { data, isLoading, isSuccess, deployLock } = useDeployLock()

function handleDeploy() {
deployLock(duration, keyPrice, maxKeys, lockName).then(() => {
console.log('deploying lock...')
})
.catch((e) => {
console.log('error deploying lock', e)
})
}

function handleUnlimitedKeys(e: boolean) {
setUnlimitedKeys(e)
if (e == true) {
setLastMaxKeys(maxKeys)
setMaxKeys(0) // 0 for unlimited
} else {
setMaxKeys(lastMaxKeys)
}
}

function handleUnlimitedDuration(e: boolean) {
setUnlimitedDuration(e)
if (e == true) {
setLastDuration(duration)
setDuration(0) // 0 for unlimited
} else {
setDuration(lastDuration)
}
}

return (
<div>
<div>
<p>Lock Name</p>
<Input placeholder={lockName} onChange={(e) => setLockName(e.target.value)} />
</div>
<div>
<p>Max Number of Keys</p>
<div className="flex flex-row items-center justify-center">
{unlimitedKeys ? (
<Input disabled={true} />
) : (
<Input placeholder={maxKeys.toString()} onChange={(e) => setMaxKeys(Number(e.target.value))} />
)}
unlimited <Switch onCheckedChange={(e) => handleUnlimitedKeys(e)} />
</div>
</div>
<div>
<p>Membership Duration (Days)</p>
<div className="flex flex-row items-center justify-center">
{unlimitedDuration ? (
<Input disabled={true} />
) : (
<Input placeholder={duration.toString()} onChange={(e) => setDuration(Number(e.target.value))} />
)}
unlimited <Switch onCheckedChange={(e) => handleUnlimitedDuration(e)} />
</div>
</div>
<div>
<p>Key Price</p>
<Input placeholder={keyPrice} onChange={(e) => setKeyPrice(e.target.value)} />
</div>

<div className="flex justify-center items-center m-10">
<Button onClick={() => handleDeploy()}>Deploy Lock</Button>
{isLoading && <p>Deploying Lock...</p>}
{isSuccess && <p>Lock Deployed!</p>}
</div>

</div>
)
}
12 changes: 12 additions & 0 deletions integrations/unlock/components/key-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
interface KeyPreviewProps {
lockName: string
}
export default function KeyPreview({ lockName }: KeyPreviewProps) {
return (
<div className="bg-slate-500 m-4">
<div className="p-2">
<p>Lock Name: {lockName}</p>
</div>
</div>
)
}
15 changes: 15 additions & 0 deletions integrations/unlock/components/lock-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface LockPreviewProps {
lockId: string
lockName: string
}
export default function LockPreview({ lockId, lockName }: LockPreviewProps) {
return (
<a href={`/integration/unlock/${lockId}`}>
<div className="bg-slate-500 m-4">
<div className="p-2">
<p>Lock Name: {lockName}</p>
</div>
</div>
</a>
)
}
Loading