Add masonry layout and small fixes

This commit is contained in:
2024-05-14 20:56:02 +04:00
parent ab6eca4661
commit 0563abd669
12 changed files with 334 additions and 293 deletions

16
package-lock.json generated
View File

@@ -13,12 +13,14 @@
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
"react-responsive-masonry": "^2.2.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-responsive-masonry": "^2.1.3",
"eslint": "^8",
"eslint-config-next": "14.2.3",
"postcss": "^8",
@@ -485,6 +487,15 @@
"@types/react": "*"
}
},
"node_modules/@types/react-responsive-masonry": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@types/react-responsive-masonry/-/react-responsive-masonry-2.1.3.tgz",
"integrity": "sha512-aOFUtv3QwNMmy0BgpQpvivQ/+vivMTB6ARrzf9eTSXsLzXpVnfEtjpHpSknYDnr8KaQmlgeauAj8E7wo/qMOTg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
@@ -3744,6 +3755,11 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"node_modules/react-responsive-masonry": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/react-responsive-masonry/-/react-responsive-masonry-2.2.0.tgz",
"integrity": "sha512-IYbnfe2tWCZ3pvyTLyBWPj7uv5ZmNOULYMcAZi5a47ZLhSotOck1vkkISq6gP2qiyWdMvPfeMhjvYzUYGw9BOQ=="
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

View File

@@ -14,12 +14,14 @@
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
"react-responsive-masonry": "^2.2.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-responsive-masonry": "^2.1.3",
"eslint": "^8",
"eslint-config-next": "14.2.3",
"postcss": "^8",

View File

@@ -1,5 +1,6 @@
import { GameService } from "@/entities/game";
import { GameCard } from "@/features/gameCard";
import { getYouTubeID } from "@/shared/utils";
import { Section } from "@/widgets/section";
import clsx from "clsx";
import Image from "next/image";
@@ -17,17 +18,17 @@ export default async function Games({
{game && (
<div className="p-4 flex flex-col lp:block">
{game.cover && (
<div className="lp:w-[60%] lp:px-4 lp:pl-0 pt-2 float-left">
<div className="lp:w-[60%] lp:px-4 lp:pl-0 py-2 float-left">
<Image
src={game.cover}
className="rounded-lg aspect-video object-cover"
className="rounded-lg w-full object-contain"
alt=""
width={1280}
height={720}
/>
</div>
)}
<span className="pt-2 lp:max-w-[40%]">
<span className="lp:max-w-[40%]">
<h1 className="text-4xl">{game.title}</h1>
{game.description && (
<p className="text-md text-justify text-fg4 pt-2">
@@ -35,7 +36,7 @@ export default async function Games({
</p>
)}
</span>
<div className="flex justify-between pt-6">
<div className="w-full flex justify-between pt-4">
{[
[
{ name: "Система", value: game.system },
@@ -84,25 +85,28 @@ export default async function Games({
</ul>
))}
</div>
{game.trailer && (
{game.trailer && getYouTubeID(game.trailer) && (
<iframe
src={game.trailer.replace("/watch?v=", "/embed/")}
className="w-full aspect-video rounded-lg my-4"
src={"https://youtube.com/embed/" + getYouTubeID(game.trailer)}
className="w-full aspect-video rounded-lg mt-4"
allowFullScreen
/>
)}
<div className="relative w-full flex items-center justify-around">
<div className="relative w-full flex items-center justify-around pt-4">
<Link
href={
process.env.NEXT_PUBLIC_CONTENT_URL + "/" + game.torrent_file
}
className="p-4 bg-ac0 text-fg1 text-xl rounded-lg"
className="p-4 bg-ac0 text-fg1 text-2xl rounded-lg"
>
Скачать {game.title}.torrent
Скачать {game.title}
</Link>
</div>
<div className="w-full flex justify-end">
<Link className="text-right text-sm" href="/how_to_download">
<Link
className="text-right text-sm relative top-4 lp:-top-4"
href="/how_to_download"
>
Как скачать игру
<br /> с помощью .torrent файла?
</Link>
@@ -112,7 +116,7 @@ export default async function Games({
{gameCards && (
<Section
name="Другие популярные игры"
name="Популярные игры"
link="/games"
invite_text={'Перейти в раздел "Игры"'}
>

View File

@@ -16,7 +16,7 @@
--color-ac2: #8ec07c;
--app-width: 70%;
font-size: calc((100vw / 1920) * 24);
font-size: calc((100vw / 1920) * 20);
}
[data-theme="dark"] {
@@ -50,17 +50,14 @@ body {
background-color: var(--color-bg0);
}
@media (max-width: 1280px) {
:root {
--app-width: 100%;
}
}
@media (max-width: 1024px) {
:root {
font-size: calc((100vw / 1920) * 56);
--app-width: 100%;
}
}
@media (max-width: 640px) {
:root {
font-size: calc((100vw / 1920) * 64);

View File

@@ -12,12 +12,12 @@ export default async function HowToDownload() {
<div className="w-full p-4 lp:w-[50%] lp:pr-10">
<h1 className="text-4xl lp:text-3xl">Как скачать?</h1>
<div className="text-fg4 text-justify pt-2">
Чтобы скачать что-либо с помощью торрент-файла, выполните следующие
Чтобы скачать данные с помощью торрент-файла, выполните следующие
шаги:
<ul className="*:text-fg4">
<li>
1. Найдите и загрузите торрент-файл или magnet-ссылку, содержащую
информацию о файле, который вы хотите скачать.
1. Загрузите торрент-файл, содержащий информацию о файлах, которые
вы хотите скачать с нашего сайта.
</li>
<li>
2. Откройте программу-клиент для загрузки торрентов, например,

View File

@@ -1,4 +1,3 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "next-themes";
@@ -6,12 +5,6 @@ import { Header } from "@/widgets/header";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: ".Torrent",
description:
".Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг",
};
export default function RootLayout({
children,
}: Readonly<{

View File

@@ -1,6 +1,13 @@
import { GameService } from "@/entities/game";
import { GameCard } from "@/features/gameCard";
import { Section } from "@/widgets/section";
import { Metadata } from "next";
export const metadata: Metadata = {
title: ".Torrent",
description:
".Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг",
};
export default async function Home() {
const gameCards = await GameService.getGameCards();

View File

@@ -8,18 +8,18 @@ export const GameCard = ({ card }: { card: GameCardType }) => {
{!!card.cover_preview && (
<Image
src={card.cover_preview}
className="rounded-lg"
className="rounded-lg object-contain"
alt=""
width={700}
height={400}
width={1280}
height={720}
/>
)}
<div className="flex items-center justify-between pr-2">
<h2 className="text-2xl py-1 group-hover/gamecard:underline underline-offset-4">
<h2 className="text-3xl tb:text-xl py-1 group-hover/gamecard:underline underline-offset-4">
{card.title}
</h2>
{card.version && (
<span className="text-xs max-w-[30%] line-clamp-2 text-fg4">
<span className="text-xs max-w-[30%] text-right line-clamp-2 text-fg4">
{card.version}
</span>
)}

View File

@@ -0,0 +1,6 @@
export const getYouTubeID = (url: string) => {
const regExp =
/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
const match = url.match(regExp);
return match && match[7].length == 11 ? match[7] : false;
};

View File

@@ -0,0 +1,3 @@
import { getYouTubeID } from "./get_youtube_id";
export { getYouTubeID };

View File

@@ -1,17 +1,23 @@
"use client";
import { SchemeSwitch } from "@/features/colorSchemeSwitch";
import { PersonIcon, SearchIcon } from "@/shared/assets/icons";
import { MobileMenu } from "./mobileMenu/mobileMenu";
import Link from "next/link";
import { useSelectedLayoutSegment } from "next/navigation";
import clsx from "clsx";
const sections = [
{ title: "Игры", href: "/games" },
{ title: "Фильмы", href: "/films" },
{ title: "Аудиокниги", href: "/audiobooks" },
{ title: "Игры", href: "games" },
{ title: "Фильмы", href: "films" },
{ title: "Аудиокниги", href: "audiobooks" },
];
export const Header = () => {
const currentPageName = useSelectedLayoutSegment();
return (
<header className="w-full h-20 bg-bg1 sticky top-0">
<header className="w-full h-20 bg-bg1 sticky top-0 shadow-xl">
<div
className="w-full h-full max-w-[var(--app-width)] m-auto px-5
flex items-center justify-between"
@@ -26,7 +32,10 @@ export const Header = () => {
{sections.map((section) => (
<Link
key={section.title}
className="px-5 cursor-pointer hover:underline"
className={clsx(
"px-5 cursor-pointer hover:underline underline-offset-2",
currentPageName === section.href && "underline"
)}
href={section.href}
>
{section.title}

View File

@@ -2,6 +2,7 @@
import Link from "next/link";
import { useRouter } from "next/navigation";
import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
export const Section = ({
name,
@@ -17,7 +18,7 @@ export const Section = ({
const router = useRouter();
return (
<section className="w-full h-fit p-5 mb-20 pt-8">
<section className="w-full h-fit p-2 mb-20 pt-8">
{name && (
<h2
className="text-4xl pb-2 cursor-pointer w-fit"
@@ -26,12 +27,15 @@ export const Section = ({
{name}
</h2>
)}
<div className="grid grid-cols-1 tb:grid-cols-2 lp:grid-cols-3 gap-y-10 gap-x-3">
{children}
</div>
<ResponsiveMasonry columnsCountBreakPoints={{ 0: 1, 640: 2, 1024: 3 }}>
<Masonry gutter="1rem">{children}</Masonry>
</ResponsiveMasonry>
{link && invite_text && (
<div className="w-full flex justify-end pt-5">
<Link href={link} className="text-lg">
<Link
href={link}
className="text-lg hover:underline underline-offset-4"
>
{invite_text}
</Link>
</div>