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

Integrate ENS Record Setting in UI #256

Merged
merged 6 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
2 changes: 2 additions & 0 deletions packages/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ REACT_APP_SUBGRAPH_SEPOLIA=auryn-macmillan/tabula-sepolia
REACT_APP_SUBGRAPH_POLYGON=auryn-macmillan/tabula-polygon
REACT_APP_SUBGRAPH_ARBITRUM=auryn-macmillan/tabula-arbitrum
REACT_APP_SUBGRAPH_OPTIMISM=auryn-macmillan/tabula-optimism
REACT_APP_ENS_SUBGRAPH_MAINNET=ensdomains/ens
REACT_APP_ENS_SUBGRAPH_GOERLI=ensdomains/ensgoerli
REACT_APP_IPFS_GATEWAY=https://ipfs.io/ipfs
REACT_APP_SUBGRAPH_OPTIMISM_ON_GNOSIS_CHAIN=auryn-macmillan/tabula-optimism-on-gnosis-chain
REACT_APP_INFURA_API_KEY=
Expand Down
61 changes: 32 additions & 29 deletions packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { PosterProvider } from "./services/poster/context"
import { WalletProvider } from "./connectors/WalletProvider"
import { RedirectOldRoute } from "./components/commons/RedicrectOldRoute"
import PreviewArticleView from "./components/views/publication/PreviewArticleView"
import { EnsProvider } from "./services/ens/context"

const App: React.FC = () => {
// the chainId should be from the publication if its present
Expand All @@ -44,39 +45,41 @@ const App: React.FC = () => {
<SnackbarProvider maxSnack={1}>
<UrqlProvider value={currentSubgraphClient}>
<WalletProvider>
<PublicationProvider>
<ArticleProvider>
<PosterProvider>
<ScrollToTop />
<Routes>
{" "}
<Route path="/" element={<LandingView />} />
<Route path="/wallet" element={<WalletView />} />
<Route path="/publications" element={<PublicationsView updateChainId={updateChainId} />} />
<Route path=":publicationSlug" element={<PublicationView updateChainId={updateChainId} />} />
{/* Redirect old routes to new routes */}
<Route path="/goerli/*" element={<RedirectOldRoute />} />
<Route path="/mainnet/*" element={<RedirectOldRoute />} />
<Route path="/gnosis_chain/*" element={<RedirectOldRoute />} />
<Route path="/polygon/*" element={<RedirectOldRoute />} />
<Route path="/arbitrum/*" element={<RedirectOldRoute />} />
<Route path="/optimism/*" element={<RedirectOldRoute />} />
{/* New routes */}
<Route path=":publicationSlug">
<Route path="permissions/:type" element={<PermissionView />} />
<EnsProvider>
<PublicationProvider>
<ArticleProvider>
<PosterProvider>
<ScrollToTop />
<Routes>
{" "}
<Route path="/" element={<LandingView />} />
<Route path="/wallet" element={<WalletView />} />
<Route path="/publications" element={<PublicationsView updateChainId={updateChainId} />} />
<Route path=":publicationSlug" element={<PublicationView updateChainId={updateChainId} />} />
{/* Redirect old routes to new routes */}
<Route path="/goerli/*" element={<RedirectOldRoute />} />
<Route path="/mainnet/*" element={<RedirectOldRoute />} />
<Route path="/gnosis_chain/*" element={<RedirectOldRoute />} />
<Route path="/polygon/*" element={<RedirectOldRoute />} />
<Route path="/arbitrum/*" element={<RedirectOldRoute />} />
<Route path="/optimism/*" element={<RedirectOldRoute />} />
{/* New routes */}
<Route path=":publicationSlug">
<Route path="permissions/:type" element={<PermissionView />} />

<Route path="new" element={<CreateArticleView type="new" />} />
<Route path="new" element={<CreateArticleView type="new" />} />

<Route path=":type/preview" element={<PreviewArticleView />} />
<Route path=":type/preview" element={<PreviewArticleView />} />

<Route path=":articleId" element={<ArticleView updateChainId={updateChainId} />} />
<Route path=":articleId" element={<ArticleView updateChainId={updateChainId} />} />

<Route path=":articleId/edit" element={<CreateArticleView type="edit" />} />
</Route>
</Routes>
</PosterProvider>
</ArticleProvider>
</PublicationProvider>
<Route path=":articleId/edit" element={<CreateArticleView type="edit" />} />
</Route>
</Routes>
</PosterProvider>
</ArticleProvider>
</PublicationProvider>
</EnsProvider>
</WalletProvider>
</UrqlProvider>
</SnackbarProvider>
Expand Down
14 changes: 8 additions & 6 deletions packages/app/src/components/commons/WalletBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from "react"
import React, { useEffect } from "react"

import { Avatar } from "@mui/material"
import * as blockies from "blockies-ts"

import { useNotification } from "../../hooks/useNotification"
import { useWeb3React } from "@web3-react/core"
import { lookupAddress } from "../../services/ens"
import { useEnsContext } from "../../services/ens/context"
import useENS from "../../services/ens/hooks/useENS"

type WalletBadgeProps = {
copyable?: boolean
Expand All @@ -14,25 +15,26 @@ type WalletBadgeProps = {
}

export const WalletBadge: React.FC<WalletBadgeProps> = ({ address, hover, copyable }) => {
const { lookupAddress } = useENS()
const avatarSrc = blockies.create({ seed: address.toLowerCase() }).toDataURL()
const { connector, active, chainId } = useWeb3React()
const { setEnsName } = useEnsContext()

const [ensName, setEnsName] = useState<string>()
const openNotification = useNotification()

useEffect(() => {
const fetchData = async () => {
if (address && active) {
const provider = await connector?.getProvider()
if (provider != null) {
const ensName = await lookupAddress(provider, address)
setEnsName(ensName ?? undefined)
const ens = await lookupAddress(provider, address)
setEnsName(ens)
}
}
}

fetchData().catch(console.error)
}, [active, address, connector, ensName, chainId])
}, [active, address, connector, chainId, setEnsName, lookupAddress])

const handleAddressClick = async () => {
if (copyable) {
Expand Down
123 changes: 123 additions & 0 deletions packages/app/src/components/views/publication/components/EnsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Button, CircularProgress, Grid, Modal, ModalProps, Typography, styled } from "@mui/material"
import React, { useEffect, useRef, useState } from "react"
import { palette, typography } from "../../../../theme"
import { ViewContainer } from "../../../commons/ViewContainer"
import CloseIcon from "@mui/icons-material/Close"
import { useEnsContext } from "../../../../services/ens/context"
import { useWeb3React } from "@web3-react/core"
import useENS from "../../../../services/ens/hooks/useENS"
import { Dropdown } from "../../../commons/Dropdown"

interface EnsModalProps extends Omit<ModalProps, "children"> {
publicationId: string
}

const ModalContainer = styled(ViewContainer)({
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
borderRadius: 8,
width: 648,
background: palette.whites[1000],
padding: 24,
})

const EnsModal: React.FC<EnsModalProps> = ({ publicationId, ...props }) => {
const { setTextRecord, loading, transactionCompleted } = useENS()
const { connector, chainId } = useWeb3React()
const ref = useRef(null)
const { ensNameList } = useEnsContext()
const [ensNameSelected, setEnsNameSelected] = useState<string>("")

useEffect(() => {
if (transactionCompleted) {
props.onClose && props.onClose({}, "backdropClick")
}
}, [props, transactionCompleted])

const handleEnsRecord = async () => {
const provider = await connector?.getProvider()
if (provider !== null && ensNameSelected && chainId) {
await setTextRecord(provider, publicationId, ensNameSelected, chainId)
}
}

return (
<Modal
open={props.open}
onClose={() => {
if (!loading) {
props.onClose && props.onClose({}, "backdropClick")
}
}}
>
<ModalContainer maxWidth="md" ref={ref}>
<Grid container spacing={3} py={3} px={4} flexDirection="column">
<Grid item>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Typography
fontFamily={typography.fontFamilies.sans}
variant="h5"
sx={{ margin: 0 }}
color={palette.grays[1000]}
>
Linking Your Publication to Your ENS Name
</Typography>
</Grid>
<Grid item>
<CloseIcon
style={{ cursor: "pointer" }}
onClick={() => {
if (!loading) {
props.onClose && props.onClose({}, "escapeKeyDown")
}
}}
/>
</Grid>
</Grid>
</Grid>

<Grid item>
<Dropdown
title="Select an ENS name"
options={ensNameList}
onSelected={(e) => {
setEnsNameSelected(e.value)
}}
/>
</Grid>

<Grid item>
<Grid container gap={1}>
<Typography fontFamily={typography.fontFamilies.sans}>
We're registering a record to your ENS name with the publication ID. This allows direct access to your
publication via{" "}
<span
style={{
color: palette.secondary[1000],
textDecoration: "underline",
cursor: "pointer",
}}
>
tabula.gg/#/{ensNameSelected ?? "yourENS"}
</span>
.
</Typography>
</Grid>
</Grid>

<Grid item>
<Button variant="contained" type="submit" onClick={handleEnsRecord} disabled={loading || !ensNameSelected}>
{loading && <CircularProgress size={20} sx={{ marginRight: 1 }} />}
Continue
</Button>
</Grid>
</Grid>
</ModalContainer>
</Modal>
)
}

export default EnsModal
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import { CreatableSelect } from "../../../commons/CreatableSelect"
import { CreateSelectOption } from "../../../../models/dropdown"
import useLocalStorage from "../../../../hooks/useLocalStorage"
import { Pinning, PinningService } from "../../../../models/pinning"
import { useEnsContext } from "../../../../services/ens/context"
import EnsModal from "./EnsModal"
import useENS from "../../../../services/ens/hooks/useENS"

type Post = {
title: string
Expand All @@ -48,6 +51,7 @@ export const SettingSection: React.FC<SettingsSectionProps> = ({ couldDelete, co
const [pinning] = useLocalStorage<Pinning | undefined>("pinning", undefined)
const [tags, setTags] = useState<string[]>([])
const [loading, setLoading] = useState<boolean>(false)
const [openENSModal, setOpenENSModal] = useState<boolean>(false)
const [deleteLoading, setDeleteLoading] = useState<boolean>(false)
const {
publication,
Expand All @@ -57,6 +61,8 @@ export const SettingSection: React.FC<SettingsSectionProps> = ({ couldDelete, co
removePublicationImage,
setRemovePublicationImage,
} = usePublicationContext()
const { ensNameList } = useEnsContext()
const { fetchNames } = useENS()
const { executePublication, deletePublication } = usePoster()
const {
indexing: updateIndexing,
Expand All @@ -75,6 +81,11 @@ export const SettingSection: React.FC<SettingsSectionProps> = ({ couldDelete, co
resolver: yupResolver(publicationSchema),
})

useEffect(() => {
fetchNames()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

useEffect(() => {
saveIsEditingPublication(true)
// returned function will be called on component unmount
Expand All @@ -93,6 +104,15 @@ export const SettingSection: React.FC<SettingsSectionProps> = ({ couldDelete, co
}
}, [loading, publication, setCurrentTimestamp, setValue])

useEffect(() => {
if (publication && !loading && publication.lastUpdated) {
setValue("title", publication.title)
setValue("description", publication.description || "")
setTags(publication.tags || [])
setCurrentTimestamp(parseInt(publication.lastUpdated))
}
}, [loading, publication, setCurrentTimestamp, setValue])

useEffect(() => {
if (redirect) {
navigate("../publications")
Expand Down Expand Up @@ -236,6 +256,27 @@ export const SettingSection: React.FC<SettingsSectionProps> = ({ couldDelete, co
errorMsg={tags.length && tags.length >= 6 ? "Add up to 5 tags for your publication" : undefined}
/>
</Grid>
{ensNameList && (
<Grid item>
<Button
onClick={() => setOpenENSModal(true)}
style={{
color: palette.primary[1000],
textDecoration: "underline",
cursor: "pointer",
}}
>
Link Your Publication to Your ENS Name
</Button>
</Grid>
)}
{ensNameList && (
<EnsModal
open={openENSModal}
onClose={() => setOpenENSModal(false)}
publicationId={publication?.id ?? ""}
/>
)}
<Grid item>
<Grid container justifyContent="space-between" sx={{ mt: 2 }}>
{couldDelete && (
Expand Down
Loading
Loading