Skip to content

Commit

Permalink
Introduce Basic Page
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans committed Oct 6, 2023
1 parent af4ab4a commit 422fabf
Show file tree
Hide file tree
Showing 16 changed files with 852 additions and 49 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Build App
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/v3xlabs/ens-page
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=edge
type=sha
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
id: docker_build
uses: docker/build-push-action@v3
with:
push: true
context: .
file: Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.next
.dockerignore
59 changes: 59 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN yarn build

# If using npm comment out above and use below instead
# RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node", "server.js"]
141 changes: 141 additions & 0 deletions app/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { ens_normalize } from '@adraffy/ens-normalize';
import { formatRecord } from '@ens-tools/format';
import { FC, PropsWithChildren, ReactNode } from 'react';
import { FaTelegram } from 'react-icons/fa';
import { FiGithub, FiLink, FiTwitter } from 'react-icons/fi';

import { useEnstate } from '../../hooks/useEnstate';

const Button: FC<PropsWithChildren<{ href: string }>> = ({
children,
href,
}) => {
return (
<a
href={href}
target="_blank"
className="bg-[#7116EB] rounded-lg px-4 py-3 flex items-center justify-center gap-2"
>
{children}
</a>
);
};

const buttonControls = (key: string, value: string): ReactNode | undefined => {
const formatted = formatRecord(key as any, value);

if (key == 'com.twitter') {
return (
<Button href={'https://twitter.com/' + formatted}>
<FiTwitter />
{formatted || value}
</Button>
);
}

if (key == 'url') {
const { hostname } = new URL(formatted || value);

return (
<Button href={value}>
<FiLink />
{hostname}
</Button>
);
}

if (key == 'com.github') {
return (
<Button href={'https://github.com/' + (formatted || value)}>
<FiGithub />
{formatted || value}
</Button>
);
}

if (key == 'org.telegram') {
return (
<Button
href={'https://t.me/' + (formatted || value).replace(/^@/, '')}
>
<FaTelegram />
{formatted || value}
</Button>
);
}

if (key == 'com.discord') {
// TODO: Discord url: https://discord.com/users/1234567890 <-- userid
// Can be done once discord implements a way to resolve user ids, or we change the record
}

// return <div>Unknown {key}</div>;
};

export default async function ({
params: { slug },
}: {
params: { slug: string };
}) {
const raw_name = slug;
const name = ens_normalize(raw_name.toLowerCase());

if (raw_name.toLowerCase() !== name) {
throw new Error('Invalid ENS name');
}

// if (!name.match(/^[a-z0-9-.]+$/)) {

const data = await useEnstate(name);

return (
<div className="mx-auto w-full max-w-md flex flex-col gap-8 mt-4 lg:mt-10 px-6 py-8">
<div className="text-center px-4">
<img
src="/frensday_2.png"
alt="frensday"
className="w-full h-auto mx-auto"
/>
<div>November 13 2023, Istanbul Türkiye</div>
</div>
<div className="w-full flex flex-col gap-2 items-center justify-center">
<div className="flex items-center relative w-full pt-8">
<div className="mx-auto w-40 h-40 aspect-square border bg-white rounded-full overflow-hidden">
<img
src={data.avatar}
alt="profile"
className="w-full h-full"
/>
</div>
<div className="absolute inset-0">
<img
src="/frensday_1.svg"
alt="frensday"
className="w-full h-full object-contain"
/>
</div>
</div>
<div className="text-center px-2 py-2 space-y-2">
<div className="text-3xl font-extrabold text-center">
{data.name}
</div>
{data.records.description && (
<div>{data.records.description}</div>
)}
</div>
</div>
<div className="flex flex-col gap-4">
{Object.keys(data.records)
.map((key) => buttonControls(key, data.records[key]))
.filter(Boolean)}
<Button href={'https://ens.app/' + name}>
<div
className="bg-white"
style={{ height: '1em', width: '1em' }}
></div>
View on ENS App
</Button>
</div>
</div>
);
}
3 changes: 3 additions & 0 deletions app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
24 changes: 14 additions & 10 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import './global.css';

import React from 'react';

export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
title: 'Next.js',
description: 'Generated by Next.js',
};

export default function RootLayout({
children,
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
return (
<html lang="en">
<body className="bg-[#2A2244] text-white">{children}</body>
</html>
);
}
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function () {
return <div>h</div>;
return <div>ens.page v0.1-alpha.1</div>;
}
32 changes: 32 additions & 0 deletions hooks/useEnstate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const FALLBACK_DATA = {
name: 'luc.eth',
address: '0x225f137127d9067788314bc7fcc1f36746a3c3B5',
avatar: 'https://ens.xyz/luc.eth',
display: 'luc.eth',
records: {
'com.discord': 'lucemans',
'com.github': 'lucemans',
'com.twitter': 'lucemansnl',
description: 'Create Epic Shit',
email: '[email protected]',
location: 'Breda, NL',
name: 'luc',
'org.telegram': 'lucemans',
timezone: 'Etc/UTC',
url: 'https://luc.computer',
},
chains: {
eth: '0x225f137127d9067788314bc7fcc1f36746a3c3B5',
},
fresh: 1_696_518_163_749,
resolver: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41',
errors: {},
};

export const useEnstate = async (name: string) => {
const http = await fetch(`https://enstate.rs/n/${name}`);

const data: typeof FALLBACK_DATA = await http.json();

return data;
};
11 changes: 11 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** @type {import('next').NextConfig} */
// eslint-disable-next-line unicorn/no-empty-file
module.exports = {
reactStrictMode: true,
output: 'standalone',
webpack: (config) => {
config.resolve.fallback = { fs: false, net: false, tls: false };

return config;
},
};
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint -c .eslintrc.json --ext .ts ./src"
"lint": "eslint -c .eslintrc.json --ext .ts ./src",
"dev": "next dev",
"build": "next build"
},
"keywords": [],
"author": "",
"license": "GPL-3",
"dependencies": {
"next": "^13.4.10"
"@adraffy/ens-normalize": "^1.10.0",
"@ens-tools/format": "^0.0.2",
"autoprefixer": "^10.4.14",
"next": "^13.4.10",
"postcss": "^8.4.26",
"react": "^18.2.0",
"react-icons": "^4.11.0",
"tailwindcss": "^3.3.3"
},
"devDependencies": {
"@types/node": "20.4.2",
Expand Down
Loading

0 comments on commit 422fabf

Please sign in to comment.