feat: site is pretty much done now, all functionality is cover just losing some details

This commit is contained in:
Juan Di Toro 2023-11-15 10:16:20 +01:00
parent fa67c88c6c
commit 50ba72f950
8 changed files with 209 additions and 140 deletions

View File

@ -0,0 +1,34 @@
"use client"
import { ArrowLeftIcon } from "@heroicons/react/24/outline"
import Link from "next/link"
// page https://www.businessinsider.com/web3-music-platforms-blockchain-even-sound-iyk-streaming-services-artists-2023-9#:~:text=Web3%20music%20platforms%20are%20combining,and%20'create%20dope%20consumer%20experiences'&text=Web3%20music%20platforms%20are%20changing,and%20ways%20to%20reach%20fans.
import React from "react"
type Props = {
params: {
slug: string
}
}
const Page = ({ params }: Props) => {
// TODO: Explore based on the slug, we can also change the slug to be like the id or something the backend understands
// We can also pre-render the article from the backend
return (
<>
<Link href="/" className="w-full mt-1">
<button className="px-3 py-2 text-gray-400 hover:bg-gray-800 hover:text-white rounded">
<ArrowLeftIcon className="w-4 h-4 inline mr-2 -mt-1" />
Go Back Home
</button>
</Link>
<div className="w-full min-h-[calc(100%-80px)] !h-full !mt-1 !mb-0">
<iframe
className="w-full h-full"
src="https://www.businessinsider.com/web3-music-platforms-blockchain-even-sound-iyk-streaming-services-artists-2023-9#:~:text=Web3%20music%20platforms%20are%20combining,and%20'create%20dope%20consumer%20experiences'&text=Web3%20music%20platforms%20are%20changing,and%20ways%20to%20reach%20fans"
></iframe>
</div>
</>
)
}
export default Page

View File

@ -2,6 +2,8 @@ import type { Metadata } from "next"
import Image from "next/image" import Image from "next/image"
import { Inter, Jaldi } from "next/font/google" import { Inter, Jaldi } from "next/font/google"
import "./globals.css" import "./globals.css"
import Header from "@/components/LayoutHeader"
import Footer from "@/components/LayoutFooter"
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" }) const inter = Inter({ subsets: ["latin"], variable: "--font-inter" })
const jaldi = Jaldi({ const jaldi = Jaldi({
@ -23,55 +25,12 @@ export default function RootLayout({
return ( return (
<html lang="en"> <html lang="en">
<body className={`font-main bg-gray-900 flex`}> <body className={`font-main bg-gray-900 flex`}>
<main className="flex w-full min-h-screen flex-col md:px-40 items-center space-y-10 py-16 mx-auto"> <main className="flex w-full min-h-screen flex-col md:px-40 items-center py-16 mx-auto">
<Header /> <Header />
{children} {children}
<Footer />
</main> </main>
</body> </body>
</html> </html>
) )
} }
export const Header = () => {
return (
<header className="w-full flex flex-row justify-between relative">
<div className="flex-1">
<Web3NewsLogo />
</div>
<div className="w-28 h-8 relative">
<Image
className="-right-4 top-0 absolute"
width={28}
height={24}
src="/lume-logo-sm.png"
alt=""
/>
<span className="left-0 top-[6px] absolute text-white text-opacity-50 text-sm font-normal font-secondary leading-7">
a Lume project
</span>
</div>
</header>
)
}
const Web3NewsLogo = ({ className }: { className?: string }) => {
return (
<svg
className={className}
width="159"
height="23"
viewBox="0 0 159 23"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.55 0.299999H3.309V16.172L8.703 8.081H11.059L16.391 16.172V0.299999H19.15V22H17.104L9.85 11.367L2.627 22H0.55V0.299999ZM22.2251 22L22.2561 0.299999H36.7641V3.059H25.0151V9.755H33.6331V12.545H25.0151V19.241H36.7641V22H22.2251ZM39.4824 22L39.4514 0.299999H47.7284C53.1844 0.299999 55.8194 6.903 52.0374 10.654C57.2144 14.157 55.0444 22 48.9374 22H39.4824ZM42.2414 9.786H47.7284C52.0064 9.786 52.2234 3.059 47.7284 3.059H42.2414V9.786ZM42.2414 19.241H48.9374C53.2154 19.241 53.4324 12.514 48.9374 12.514H42.2414V19.241ZM62.7651 22C59.9751 22 57.8051 19.768 57.8051 16.761H60.5021C60.5021 18.342 61.5251 19.303 62.7651 19.303H66.7021C71.1661 19.303 71.2281 12.297 66.7021 12.297H63.3541V10.003H66.0511C70.5771 10.003 70.5461 2.997 66.0511 2.997H62.8891C61.6181 2.997 60.5951 4.02 60.5951 5.415H57.9291C57.9291 2.532 60.1611 0.299999 62.8891 0.299999H66.0511C71.6931 0.299999 74.4831 7.771 69.7091 10.902C75.3511 13.909 72.6541 22 66.7021 22H62.7651Z"
fill="white"
/>
<path
d="M77.9783 22.248C76.8003 22.248 75.8703 21.504 75.8703 20.326C75.8703 19.148 76.8003 18.435 77.9783 18.435C79.1253 18.435 80.1173 19.148 80.1173 20.326C80.1173 21.504 79.1253 22.248 77.9783 22.248ZM83.2268 22V0.299999H85.3348L97.9828 16.854V0.299999H100.742V22H98.6028L85.9858 5.477V22H83.2268ZM103.812 22L103.843 0.299999H118.351V3.059H106.602V9.755H115.22V12.545H106.602V19.241H118.351V22H103.812ZM121.038 0.299999H123.797V16.172L129.191 8.081H131.547L136.879 16.172V0.299999H139.638V22H137.592L130.338 11.367L123.115 22H121.038V0.299999ZM142.744 15.614H145.503C145.503 18.218 146.991 19.427 148.944 19.427H152.044C153.408 19.427 156.105 18.59 156.105 15.924C156.105 10.065 143.054 14.064 143.054 6.19C143.054 2.563 145.968 0.175998 148.727 0.175998H152.323C155.733 0.175998 158.337 2.594 158.337 6.624H155.578C155.578 4.082 154.09 2.935 152.106 2.935H148.913C147.208 2.935 145.813 4.237 145.813 6.159C145.813 11.522 158.864 7.306 158.864 16.42C158.864 19.83 155.671 22.186 152.044 22.186H148.944C145.534 22.186 142.744 19.799 142.744 15.614Z"
fill="#ACF9C0"
/>
</svg>
)
}

View File

@ -1,7 +1,7 @@
import React, { FormEvent } from "react" import React, { FormEvent } from "react"
import Link from "next/link" import Link from "next/link"
import { ArrowLeftIcon } from "@heroicons/react/24/outline" import { ArrowLeftIcon } from "@heroicons/react/24/outline"
import { formatDate } from "@/utils" import { formatDate, getResults } from "@/utils"
import SimplifiedSearchBar from "@/components/SimplifiedSearchBar" import SimplifiedSearchBar from "@/components/SimplifiedSearchBar"
type Props = { type Props = {
@ -16,17 +16,25 @@ const Page = async ({ searchParams }: Props) => {
return ( return (
<div className="w-full items-center text-lg"> <div className="w-full items-center text-lg">
<SimplifiedSearchBar value={query} placeholder={query ? undefined : "Search for web3 news from the community"}/> <SimplifiedSearchBar
value={query}
placeholder={
query ? undefined : "Search for web3 news from the community"
}
/>
<Link href="/">
<button className="my-4 px-3 py-2 text-gray-400 hover:bg-gray-800 hover:text-white rounded"> <button className="my-4 px-3 py-2 text-gray-400 hover:bg-gray-800 hover:text-white rounded">
<ArrowLeftIcon className="w-4 h-4 inline mr-2 -mt-1" /> <ArrowLeftIcon className="w-4 h-4 inline mr-2 -mt-1" />
Go Back Home Go Back Home
</button> </button>
</Link>
{results.length > 0 && ( {results.length > 0 && (
<> <>
{results.map((item) => ( {results.map((item) => (
<div <Link
href={`/article/${item.slug}`}
key={item.id} key={item.id}
className="flex cursor-pointer flex-row items-center space-x-5 my-2 py-2 px-4 hover:bg-gray-800 rounded-md" className="flex cursor-pointer flex-row items-center space-x-5 my-2 py-2 px-4 hover:bg-gray-800 rounded-md"
> >
@ -34,7 +42,7 @@ const Page = async ({ searchParams }: Props) => {
{formatDate(item.timestamp)} {formatDate(item.timestamp)}
</span> </span>
<h3 className="text-md font-semibold text-white">{item.title}</h3> <h3 className="text-md font-semibold text-white">{item.title}</h3>
</div> </Link>
))} ))}
<Link href={`/search?q=${encodeURIComponent(query)}`}> <Link href={`/search?q=${encodeURIComponent(query)}`}>
<button className="rounded mt-4 flex justify-center items-center bg-gray-800 mx-auto w-44 py-7 text-white hover:bg-gray-800/50 transition-colors"> <button className="rounded mt-4 flex justify-center items-center bg-gray-800 mx-auto w-44 py-7 text-white hover:bg-gray-800/50 transition-colors">
@ -47,27 +55,4 @@ const Page = async ({ searchParams }: Props) => {
) )
} }
async function getResults({
query
}: {
query?: string
}): Promise<SearchResult[]> {
if (!query) return []
return [
{
id: 1,
timestamp: new Date(),
title: "Mock Title 1",
description: "Mock Description 1"
},
{
id: 2,
timestamp: new Date(),
title: "Mock Title 2",
description: "Mock Description 2"
}
]
}
export default Page export default Page

View File

@ -0,0 +1,13 @@
import React from "react"
type Props = {}
const Footer = ({}: Props) => {
return (<div className="w-full mt-5 flex flex-row items-center justify-center text-gray-400">
<a>About Web3.news</a>
<div className="h-7 w-[1px] bg-current mx-4" />
<a className="text-white">Contribute to the cause</a>
</div>)
}
export default Footer

View File

@ -0,0 +1,50 @@
import React from "react"
import Image from "next/image"
type Props = {}
export const Header = ({}: Props) => {
return (
<header className="w-full flex flex-row justify-between relative">
<div className="flex-1">
<Web3NewsLogo />
</div>
<div className="w-28 h-8 relative">
<Image
className="-right-4 top-0 absolute"
width={28}
height={24}
src="/lume-logo-sm.png"
alt=""
/>
<span className="left-0 top-[6px] absolute text-white text-opacity-50 text-sm font-normal font-secondary leading-7">
a Lume project
</span>
</div>
</header>
)
}
const Web3NewsLogo = ({ className }: { className?: string }) => {
return (
<svg
className={className}
width="159"
height="23"
viewBox="0 0 159 23"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.55 0.299999H3.309V16.172L8.703 8.081H11.059L16.391 16.172V0.299999H19.15V22H17.104L9.85 11.367L2.627 22H0.55V0.299999ZM22.2251 22L22.2561 0.299999H36.7641V3.059H25.0151V9.755H33.6331V12.545H25.0151V19.241H36.7641V22H22.2251ZM39.4824 22L39.4514 0.299999H47.7284C53.1844 0.299999 55.8194 6.903 52.0374 10.654C57.2144 14.157 55.0444 22 48.9374 22H39.4824ZM42.2414 9.786H47.7284C52.0064 9.786 52.2234 3.059 47.7284 3.059H42.2414V9.786ZM42.2414 19.241H48.9374C53.2154 19.241 53.4324 12.514 48.9374 12.514H42.2414V19.241ZM62.7651 22C59.9751 22 57.8051 19.768 57.8051 16.761H60.5021C60.5021 18.342 61.5251 19.303 62.7651 19.303H66.7021C71.1661 19.303 71.2281 12.297 66.7021 12.297H63.3541V10.003H66.0511C70.5771 10.003 70.5461 2.997 66.0511 2.997H62.8891C61.6181 2.997 60.5951 4.02 60.5951 5.415H57.9291C57.9291 2.532 60.1611 0.299999 62.8891 0.299999H66.0511C71.6931 0.299999 74.4831 7.771 69.7091 10.902C75.3511 13.909 72.6541 22 66.7021 22H62.7651Z"
fill="white"
/>
<path
d="M77.9783 22.248C76.8003 22.248 75.8703 21.504 75.8703 20.326C75.8703 19.148 76.8003 18.435 77.9783 18.435C79.1253 18.435 80.1173 19.148 80.1173 20.326C80.1173 21.504 79.1253 22.248 77.9783 22.248ZM83.2268 22V0.299999H85.3348L97.9828 16.854V0.299999H100.742V22H98.6028L85.9858 5.477V22H83.2268ZM103.812 22L103.843 0.299999H118.351V3.059H106.602V9.755H115.22V12.545H106.602V19.241H118.351V22H103.812ZM121.038 0.299999H123.797V16.172L129.191 8.081H131.547L136.879 16.172V0.299999H139.638V22H137.592L130.338 11.367L123.115 22H121.038V0.299999ZM142.744 15.614H145.503C145.503 18.218 146.991 19.427 148.944 19.427H152.044C153.408 19.427 156.105 18.59 156.105 15.924C156.105 10.065 143.054 14.064 143.054 6.19C143.054 2.563 145.968 0.175998 148.727 0.175998H152.323C155.733 0.175998 158.337 2.594 158.337 6.624H155.578C155.578 4.082 154.09 2.935 152.106 2.935H148.913C147.208 2.935 145.813 4.237 145.813 6.159C145.813 11.522 158.864 7.306 158.864 16.42C158.864 19.83 155.671 22.186 152.044 22.186H148.944C145.534 22.186 142.744 19.799 142.744 15.614Z"
fill="#ACF9C0"
/>
</svg>
)
}
export default Header

View File

@ -1,40 +1,42 @@
"use client" "use client"
import React, { FormEvent, useCallback, useEffect, useRef, useState } from "react" import React, {
import { FormEvent,
ChevronDownIcon, useCallback,
ChevronRightIcon useEffect,
} from "@heroicons/react/24/outline" // Assuming usage of Heroicons for icons useRef,
useState
} from "react"
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/outline" // Assuming usage of Heroicons for icons
import { flushSync } from "react-dom" import { flushSync } from "react-dom"
import Link from "next/link" import Link from "next/link"
import { usePathname, useSearchParams, useRouter } from "next/navigation" import { usePathname, useSearchParams, useRouter } from "next/navigation"
import { formatDate } from "@/utils" import { formatDate, getResults } from "@/utils"
type Props = { type Props = {
variant: "default" | "simplified" variant: "default" | "simplified"
} }
const SearchBar = ({variant}: Props) => { const SearchBar = ({ variant }: Props) => {
const searchParams = useSearchParams(); const searchParams = useSearchParams()
const router = useRouter(); const router = useRouter()
const pathname = usePathname(); const pathname = usePathname()
const [query, setQuery] = useState(searchParams.get("q") ?? ""); const [query, setQuery] = useState(searchParams.get("q") ?? "")
const inputRef = useRef<HTMLInputElement>() const inputRef = useRef<HTMLInputElement>()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [activeInput, setActiveInput] = useState(true) const [activeInput, setActiveInput] = useState(true)
const [results, setResults] = useState< const [results, setResults] = useState<SearchResult[]>([])
SearchResult[]
>([])
const handleSearch = useCallback(async (event?: FormEvent<HTMLFormElement>) => { const handleSearch = useCallback(
async (event?: FormEvent<HTMLFormElement>) => {
event?.preventDefault() event?.preventDefault()
setIsLoading(true) setIsLoading(true)
const newSearchParams = new URLSearchParams(searchParams) const newSearchParams = new URLSearchParams(searchParams)
if(query) { if (query) {
newSearchParams.set('q', query) newSearchParams.set("q", query)
} else { } else {
newSearchParams.delete('q') newSearchParams.delete("q")
} }
router.push(`${pathname}?${newSearchParams}`) router.push(`${pathname}?${newSearchParams}`)
@ -43,28 +45,26 @@ const SearchBar = ({variant}: Props) => {
// Assume fetchResults is a function that fetches search results // Assume fetchResults is a function that fetches search results
// const searchResults = await fetchResults(query); // const searchResults = await fetchResults(query);
// Mock the search results // Mock the search results
const searchResults = [ const searchResults = await getResults({ query })
{
id: 1,
timestamp: new Date(),
title: "Mock Title 1",
description: "Mock Description 1"
},
{
id: 2,
timestamp: new Date(),
title: "Mock Title 2",
description: "Mock Description 2"
}
]
setResults(searchResults) setResults(searchResults)
setIsLoading(false) setIsLoading(false)
setActiveInput(false) setActiveInput(false)
}, [query, setResults, setIsLoading, setActiveInput, searchParams, router, pathname]) },
[
query,
setResults,
setIsLoading,
setActiveInput,
searchParams,
router,
pathname
]
)
useEffect(() => { useEffect(() => {
if(searchParams.get("q") && searchParams.get("q") !== "") { if (searchParams.get("q") && searchParams.get("q") !== "") {
handleSearch(); handleSearch()
} }
}, [searchParams, handleSearch]) }, [searchParams, handleSearch])
@ -162,7 +162,8 @@ const SearchBar = ({variant}: Props) => {
<> <>
<hr className="my-4 border-1" /> <hr className="my-4 border-1" />
{results.map((item) => ( {results.map((item) => (
<div <Link
href={`/article/${item.slug}`}
key={item.id} key={item.id}
className="flex cursor-pointer flex-row items-center space-x-5 my-2 py-2 px-4 hover:bg-gray-800 rounded-md" className="flex cursor-pointer flex-row items-center space-x-5 my-2 py-2 px-4 hover:bg-gray-800 rounded-md"
> >
@ -170,11 +171,12 @@ const SearchBar = ({variant}: Props) => {
{formatDate(item.timestamp)} {formatDate(item.timestamp)}
</span> </span>
<h3 className="text-md font-semibold text-white">{item.title}</h3> <h3 className="text-md font-semibold text-white">{item.title}</h3>
</div> </Link>
))} ))}
<Link href={`/search?q=${encodeURIComponent(query)}`}> <Link href={`/search?q=${encodeURIComponent(query)}`}>
<button className="mt-4 flex justify-center items-center bg-secondary w-full py-7 text-white hover:bg-teal-800 transition-colors"> <button className="mt-4 flex justify-center items-center bg-secondary w-full py-7 text-white hover:bg-teal-800 transition-colors">
{results.length}+ search results for <span className="text-blue-300 ml-1">{query}</span> {results.length}+ search results for{" "}
<span className="text-blue-300 ml-1">{query}</span>
<ChevronRightIcon className="w-5 h-5 inline ml-2 mt-[1px]" /> <ChevronRightIcon className="w-5 h-5 inline ml-2 mt-[1px]" />
</button> </button>
</Link> </Link>

1
src/types.d.ts vendored
View File

@ -3,4 +3,5 @@ type SearchResult = {
timestamp: Date timestamp: Date
title: string title: string
description: string description: string
slug: string
} }

View File

@ -9,4 +9,29 @@ export const formatDate = (date: string | Date) => {
.replace(/ hours?/, "h") .replace(/ hours?/, "h")
.replace(/ days?/, "d") .replace(/ days?/, "d")
.replace(/ weeks?/, "w") .replace(/ weeks?/, "w")
}
export async function getResults({
query
}: {
query?: string
}): Promise<SearchResult[]> {
if (!query) return []
return [
{
id: 1,
timestamp: new Date(),
title: "Mock Title 1",
description: "Mock Description 1",
slug: "hello-world"
},
{
id: 2,
timestamp: new Date(),
title: "Mock Title 2",
description: "Mock Description 2",
slug: "hello-world-2"
} }
]
}