Skip to content

Commit

Permalink
feat: finish home page and rewrite site in nextjs 13
Browse files Browse the repository at this point in the history
  • Loading branch information
corp-0 committed Jul 19, 2023
1 parent 7534f93 commit dd83f0b
Show file tree
Hide file tree
Showing 62 changed files with 2,875 additions and 10,632 deletions.
24 changes: 24 additions & 0 deletions app/(home)/DownloadButtonClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client';
import Button from "../common/uiLibrary/button";
import {useEffect, useState} from "react";
import getDownloadLink from "../../utils/platform";
import {GITHUB_RELEASES_URL} from "../../utils/urlContants";
import {BiSolidDownload} from "react-icons/bi";



const DownloadButtonClient = () => {
const [downloadLink, setDownloadLink] = useState(GITHUB_RELEASES_URL);

useEffect(() => {
getDownloadLink().then((link) => setDownloadLink(link));
}, []);

return (
<div className={'flex flex-wrap justify-center mt-8 gap-4'}>
<Button filled={true} text={'Download'} linkTo={downloadLink} iconRight={BiSolidDownload} />
</div>
)
}

export default DownloadButtonClient;
16 changes: 16 additions & 0 deletions app/(home)/LandingButtonsServer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Button from "../common/uiLibrary/button";
import {DISCORD_INVITE_URL, GITHUB_URL, PATREON_URL} from "../../utils/urlContants";



const LandingButtonsServer = () => {
return (
<div className={'flex flex-wrap justify-center mt-8 gap-4'}>
<Button filled={false} text={'Github'} linkTo={GITHUB_URL}/>
<Button filled={false} text={'Discord'} linkTo={DISCORD_INVITE_URL}/>
<Button filled={false} text={'Patreon'} linkTo={PATREON_URL}/>
</div>
)
}

export default LandingButtonsServer;
38 changes: 38 additions & 0 deletions app/(home)/contactInformation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {DISCORD_INVITE_URL} from "../../utils/urlContants";
import {BsDiscord} from "react-icons/bs";
import {MdEmail} from "react-icons/md";
import React from "react";

const ContactInformation = () => {
return (
<div
style={{boxShadow: 'inset 0 10px 10px -10px rgba(0, 0, 0, 0.5)'}}
className="w-full p-4 text-center sm:p-8 bg-gray-800 border-gray-700">
<h5 className="mb-2 text-3xl font-bold text-white">Let&apos;s Chat!</h5>
<p className="mb-5 text-base sm:text-lg text-gray-400">
We&apos;d love to hear from you! The quickest way to reach us is by joining our Discord server and dropping
us a message. But, if you&apos;d rather, feel free to shoot us an email.
</p>
<div className="items-center justify-center space-y-4 sm:flex sm:space-y-0 sm:space-x-4">
<a href={DISCORD_INVITE_URL}
className="w-full sm:w-auto focus:ring-4 focus:outline-none text-white rounded-lg inline-flex items-center justify-center px-4 py-2.5 bg-gray-700 hover:bg-gray-600 focus:ring-gray-700">
<BsDiscord className="mr-3 w-7 h-7"/>
<div className="text-left">
<div className="mb-1 text-xs">Join our</div>
<div className="-mt-1 font-sans text-sm font-semibold">Discord Server</div>
</div>
</a>
<a href="mailto:info@unitystation.org"
className="w-full sm:w-auto focus:ring-4 focus:outline-none text-white rounded-lg inline-flex items-center justify-center px-4 py-2.5 bg-gray-700 hover:bg-gray-600 focus:ring-gray-700">
<MdEmail className="mr-3 w-7 h-7"/>
<div className="text-left">
<div className="mb-1 text-xs">Email us at</div>
<div className="-mt-1 font-sans text-sm font-semibold">info@unitystation.org</div>
</div>
</a>
</div>
</div>
);
}

export default ContactInformation;
48 changes: 48 additions & 0 deletions app/(home)/featuresList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import {IconType} from "react-icons";

export type FeatureData = {
title: string;
description: string;
icon: IconType,
imageUrl: string;
}

interface FeaturesListProps{
features: FeatureData[];
}

type FeatureCardProps = {
feature: FeatureData;
}

const FeatureCard = ({ feature }: FeatureCardProps) => {
const { title, description, icon: Icon } = feature;

return (
<div className="flex flex-col space-y-2 p-4 bg-gray-900 rounded-xl shadow-md max-w-sm my-2 h-full">
<div className="flex space-x-2 items-start mb-4">
<div className="mt-1">
<Icon size={24} color="#10b981" />
</div>
<div>
<h2 className="font-semibold text-lg text-gray-200">{title}</h2>
<p className="text-sm text-gray-300 mt-2">{description}</p>
</div>
</div>
</div>
);
}


const FeaturesList = ({ features }: FeaturesListProps) => {
return (
<div className="grid xl:grid-cols-4 lg:grid-cols-2 sm:grid-cols-1 gap-2 items-stretch justify-center">
{features.map((feature, index) => (
<FeatureCard key={index} feature={feature} />
))}
</div>
);
}

export default FeaturesList;
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import RandomThirdTextClient from "./randomThirdTextClient";

interface LandingTextProps {
mainText: string
secondaryText: string
lastText: string
}

const LandingText = (props: LandingTextProps) => {
const {mainText, secondaryText, lastText} = props;
const {mainText, secondaryText} = props;

return (
<div className={'max-w-3xl mx-auto text-center'}>
Expand All @@ -15,10 +16,8 @@ const LandingText = (props: LandingTextProps) => {
<p className="mt-4 sm:leading-relaxed sm:text-xl">
{secondaryText}
</p>
<p className={'mx-auto mt-4 font-extralight'}>
{lastText}
</p>
<RandomThirdTextClient />
</div>)
}

export default LandingText;
export default LandingText;
79 changes: 79 additions & 0 deletions app/(home)/latestNews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {BlogPost} from "../../types/blogPost";
import classNames from "classnames";
import Container from "../common/uiLibrary/container";
import {toAgoTime} from "../../utils/timeUtils";
import PageSectionTitle from "../common/uiLibrary/pageSectionTitle";

interface NewsCardProps {
post: BlogPost,
isMain?: boolean,
className?: string
}

interface LatestNewsProps {
posts: BlogPost[]
}

const NewsCard = (props: NewsCardProps) => {
const {title, summary, socials_image, date_created, slug} = props.post;
const {className: classes} = props;
const isMain = props.isMain ?? false;

const style = {
backgroundImage: `url(${socials_image})`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundBlendMode: 'multiply',
backgroundPosition: 'center',
}

const truncateSummary = (summary: string, isMain: boolean) => {
if (isMain) {
return summary;
}

return summary.slice(0, 175) + '...';
}

return (
<a href={`/blog/${slug}`} className={classNames('relative overflow-hidden', classes)}>
<div className="absolute inset-0 rounded-lg shadow-lg bg-gray-400" style={style}></div>
<div className="relative p-3 h-full flex flex-col justify-between">
<div>
<div
className={
classNames('text-2xl text-white leading-tight border-b hover:border-dashed hover:border-gray-500',
{'lg:text-5xl': isMain})}>{title}</div>
<div className="text-normal text-gray-300">
<span className=" pb-1">{toAgoTime(date_created)}</span>
</div>
</div>
<p className="">{truncateSummary(summary, isMain)}</p>
</div>
</a>
)
}

const LatestNews = (props: LatestNewsProps) => {
const {posts} = props;

return (
<>
<PageSectionTitle>Latest News</PageSectionTitle>
<Container>
<div className="flex flex-col sm:flex-col md:flex-col lg:flex-row gap-5">
<div className="w-full lg:w-1/2 flex flex-col">
<NewsCard post={posts[0]} isMain className="h-full" />
</div>
<div className="w-full lg:w-1/2 flex flex-col gap-4">
{posts.slice(1).map((post, index) => (
<NewsCard key={index} post={post} className="h-auto flex-grow" />
))}
</div>
</div>
</Container>
</>
);
}

export default LatestNews;
74 changes: 74 additions & 0 deletions app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import LandingText from "./landingText";
import {BlogPost} from "../../types/blogPost";
import {BlogPostsResponse} from "../../types/blogPostsResponse";
import FetchOfType from "../../utils/fetchOfType";
import PageSection from "../common/uiLibrary/pageSection";
import LandingButtonsServer from "./LandingButtonsServer";
import DownloadButtonClient from "./DownloadButtonClient";
import LatestNews from "./latestNews";
import ContactInformation from "./contactInformation";
import FeaturesList, {FeatureData} from "./featuresList";
import React from "react";
import {RiGamepadLine, RiRefreshLine, RiRocket2Line, RiTeamLine} from "react-icons/ri";

const mainText = "Welcome to Unitystation!";
const secondaryText = "Free and open-source remake of the cult classic Space Station 13, made in Unity Engine.";

const features: FeatureData[] = [
{
title: "Player-driven gameplay",
description: "Every round plays out differently. Almost anything on the station can be picked up, examined or vandalized",
icon: RiGamepadLine,
imageUrl: "https://unitystationfile.b-cdn.net/CommunityStuff/1/chicken.png"
},
{
title: "Slapstick Simulation",
description: "Fly a shuttle into the station, breaching the hull and venting everyone into space.",
icon: RiRocket2Line,
imageUrl: "https://unitystationfile.b-cdn.net/CommunityStuff/1/production.gif"
},
{
title: "Dozens of jobs",
description: "Want to play a cook? A janitor? Law enforcement? Or maybe the captain? Even lawyers have a place on the outpost.",
icon: RiTeamLine,
imageUrl: "https://unitystationfile.b-cdn.net/WeeklyBlogUpdates/8/60.png"
},
{
title: "Finally Remade",
description: "No more dealing with BYOND to play your favorite spessman game.",
icon: RiRefreshLine,
imageUrl: "https://unitystationfile.b-cdn.net/WeeklyBlogUpdates/1/25.png"
}
]

const fetchLatestBlogPost = async (): Promise<BlogPost[]> => {
const revalidateConfig = {next: {revalidate: 60}};
const resPage1 = await FetchOfType<BlogPostsResponse>('https://changelog.unitystation.org/posts/', revalidateConfig);
const resPage2 = await FetchOfType<BlogPostsResponse>('https://changelog.unitystation.org/posts/?page=2', revalidateConfig);
return resPage1.results.concat(resPage2.results);
}


const HomePage: () => Promise<JSX.Element> = async () => {

const latestBlogPosts: BlogPost[] = await fetchLatestBlogPost();

return (
<>
<PageSection className="gap-16">
<div>
<LandingText mainText={mainText} secondaryText={secondaryText}/>
<DownloadButtonClient/>
<LandingButtonsServer/>
</div>
<FeaturesList features={features}/>
</PageSection>
<PageSection verticalCenter={false}>
<LatestNews posts={latestBlogPosts}/>
</PageSection>
<ContactInformation/>
</>
)
}

export default HomePage;
37 changes: 37 additions & 0 deletions app/(home)/randomThirdTextClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';


import {useEffect, useState} from "react";

const texts = [
'Join our Discord server to get involved!',
'Yeah, we have some bugs but at least they are funny!',
'We are not dead yet!',
'Surviving the curse one PR at a time!',
'I\'m not a web developer, please send help!',
'Thank you patreons for keeping the lights on!',
'Check our Github if you want to contribute!',
'Devlog might release any time now!',
'[object Object]',
'Join us every Saturday on our community playtest! (More info on Discord)',
]

const RandomThirdTextClient = () => {

const [randomText, setRandomText] = useState(texts[0]);

useEffect(() => {
const interval = setInterval(() => {
setRandomText(texts[Math.floor(Math.random() * texts.length)]);
}, 5000);
return () => clearInterval(interval);
})

return (
<p className={'mx-auto mt-4 font-extralight'}>
{randomText}
</p>
)
}

export default RandomThirdTextClient;
Loading

0 comments on commit dd83f0b

Please sign in to comment.