feat: new homepage
This commit is contained in:
parent
cd049348bc
commit
2783561cfb
43
backend/pb_migrations/1705455990_created_wallpapers.js
Normal file
43
backend/pb_migrations/1705455990_created_wallpapers.js
Normal file
@ -0,0 +1,43 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((db) => {
|
||||
const collection = new Collection({
|
||||
"id": "ogs3cfy8l3jo32k",
|
||||
"created": "2024-01-17 01:46:30.155Z",
|
||||
"updated": "2024-01-17 01:46:30.155Z",
|
||||
"name": "wallpapers",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "kxurmv6q",
|
||||
"name": "artwork",
|
||||
"type": "relation",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"collectionId": "eo6iaxf4pkeqynf",
|
||||
"cascadeDelete": false,
|
||||
"minSelect": null,
|
||||
"maxSelect": 1,
|
||||
"displayFields": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": null,
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
});
|
||||
|
||||
return Dao(db).saveCollection(collection);
|
||||
}, (db) => {
|
||||
const dao = new Dao(db);
|
||||
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k");
|
||||
|
||||
return dao.deleteCollection(collection);
|
||||
})
|
18
backend/pb_migrations/1705456227_updated_wallpapers.js
Normal file
18
backend/pb_migrations/1705456227_updated_wallpapers.js
Normal file
@ -0,0 +1,18 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((db) => {
|
||||
const dao = new Dao(db)
|
||||
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k")
|
||||
|
||||
collection.listRule = ""
|
||||
collection.viewRule = ""
|
||||
|
||||
return dao.saveCollection(collection)
|
||||
}, (db) => {
|
||||
const dao = new Dao(db)
|
||||
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k")
|
||||
|
||||
collection.listRule = null
|
||||
collection.viewRule = null
|
||||
|
||||
return dao.saveCollection(collection)
|
||||
})
|
@ -14,6 +14,7 @@
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"howler": "^2.2.4",
|
||||
"lucide-react": "^0.306.0",
|
||||
"pixi.js": "^7.3.3",
|
||||
|
@ -4,14 +4,18 @@ import MainLayout from "./components/layouts/MainLayout";
|
||||
import ErrorBoundaryPage from "./pages/errors/error-boundary/page";
|
||||
|
||||
const HomePage = lazy(() => import("./pages/home/page"));
|
||||
const PatPatPage = lazy(() => import("./pages/pat-pat/page"));
|
||||
const MyFurinaPage = lazy(() => import("./pages/my-furina/page"));
|
||||
const ArtworksPage = lazy(() => import("./pages/artworks/page"));
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
Component: MainLayout,
|
||||
children: [
|
||||
{ index: true, Component: HomePage },
|
||||
{
|
||||
Component: MainLayout,
|
||||
children: [
|
||||
{ path: "/pat-pat", Component: PatPatPage },
|
||||
{ path: "/toodle", Component: MyFurinaPage },
|
||||
{
|
||||
path: "/treasures",
|
||||
@ -28,6 +32,8 @@ const router = createBrowserRouter([
|
||||
</MainLayout>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const Router = () => {
|
||||
|
@ -23,7 +23,8 @@ const AppBar = () => {
|
||||
</Link>
|
||||
|
||||
<Navbar>
|
||||
<NavbarItem path="/" title="Pet the Furina" />
|
||||
<NavbarItem path="/" title="Home" />
|
||||
<NavbarItem path="/pat-pat" title="Pat Furina" />
|
||||
<NavbarItem path="/treasures" title="Treasures‧₊˚" />
|
||||
<NavbarItem path="/toodle" title="Toodle-oo~" />
|
||||
</Navbar>
|
||||
@ -53,7 +54,7 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
return (
|
||||
<Link
|
||||
to={path}
|
||||
className="group flex flex-shrink-0 items-center px-2 md:px-0 md:py-4 first:ml-auto last:mr-auto"
|
||||
className="group flex flex-shrink-0 items-center px-2 md:py-4 first:ml-auto last:mr-auto md:first:ml-0 md:last:mr-0"
|
||||
>
|
||||
<img
|
||||
src={ahogeImg}
|
||||
@ -66,7 +67,7 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
|
||||
<p
|
||||
className={cn(
|
||||
"text-white ml-2 md:ml-4 md:text-xl py-2 md:py-0 border-b-2 md:border-dotted group-hover:border-primary-500 border-transparent transition-all",
|
||||
"text-white ml-2 md:ml-4 py-2 md:py-0 border-b-2 md:border-dotted group-hover:border-primary-500 border-transparent transition-all",
|
||||
isActive ? "border-primary-500 md:border-white" : ""
|
||||
)}
|
||||
>
|
||||
|
@ -10,7 +10,11 @@ type PageMetadataProps = {
|
||||
const PageMetadata = (props: PageMetadataProps) => {
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{[props.title, "Furina.id"].filter((i) => !!i).join(" - ")}</title>
|
||||
<title>
|
||||
{props.title
|
||||
? [props.title, "Furina.id"].join(" - ")
|
||||
: "Welcome to Furina.id"}
|
||||
</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={props.description || "Welcome to Furina.id"}
|
||||
|
1
src/pages/home/icons/facebook.svg
Normal file
1
src/pages/home/icons/facebook.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' style='fill:none;' viewBox='0 0 500 501'><path style='fill:#0866FF;' d='M500 250.199C500 112.099 388.1 .199219 250 .199219C111.9 .199219 0 112.099 0 250.199C0 367.399 80.7 465.799 189.6 492.799V326.599H138V250.199H189.6V217.299C189.6 132.199 228.1 92.7992 311.6 92.7992C327.4 92.7992 354.8 95.8992 365.9 98.9992V168.299C360 167.699 349.8 167.399 337 167.399C296 167.399 280.2 182.899 280.2 223.299V250.299H361.9L347.9 326.699H280.3V498.499C404.1 483.499 500 378.099 500 250.199Z'/><path d='M347.9 326.599L361.9 250.199H280.2V223.199C280.2 182.799 296 167.299 337 167.299C349.7 167.299 360 167.599 365.9 168.199V98.9988C354.7 95.8988 327.4 92.7988 311.6 92.7988C228.1 92.7988 189.6 132.199 189.6 217.299V250.199H138V326.599H189.6V492.799C208.9 497.599 229.2 500.199 250 500.199C260.3 500.199 270.4 499.599 280.3 498.399V326.599H347.9Z' style='fill:white;'/></svg>
|
After Width: | Height: | Size: 902 B |
BIN
src/pages/home/icons/furinamains.webp
Normal file
BIN
src/pages/home/icons/furinamains.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
13
src/pages/home/icons/index.ts
Normal file
13
src/pages/home/icons/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import patFurina from "./pat-furina.webp";
|
||||
import treasures from "./treasures.webp";
|
||||
import facebook from "./facebook.svg";
|
||||
import twitter from "./twitter.jpg";
|
||||
import furinamains from "./furinamains.webp";
|
||||
|
||||
export const icons = {
|
||||
patFurina,
|
||||
treasures,
|
||||
facebook,
|
||||
twitter,
|
||||
furinamains,
|
||||
};
|
BIN
src/pages/home/icons/pat-furina.webp
Normal file
BIN
src/pages/home/icons/pat-furina.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
src/pages/home/icons/treasures.webp
Normal file
BIN
src/pages/home/icons/treasures.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
src/pages/home/icons/twitter.jpg
Normal file
BIN
src/pages/home/icons/twitter.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@ -1,92 +1,182 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import styles from "./style.module.css";
|
||||
import { cn } from "@/utility/utils";
|
||||
import LoadingPage from "../misc/loading-page";
|
||||
import titleImg from "@/assets/images/title-img.svg";
|
||||
import PageMetadata from "@/components/containers/PageMetadata";
|
||||
import Modal from "@/components/ui/Modal";
|
||||
import useModal from "@/hooks/useModal";
|
||||
import Button from "@/components/ui/Button";
|
||||
import { cn } from "@/utility/utils";
|
||||
import dayjs from "dayjs";
|
||||
import { ComponentProps, useEffect, useMemo, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { icons } from "./icons";
|
||||
import { useQuery } from "react-query";
|
||||
import pb from "@/utility/api";
|
||||
|
||||
const HomePage = () => {
|
||||
const appRef = useRef<any>();
|
||||
const cleanRef = useRef<any>();
|
||||
const targetRef = useRef<HTMLDivElement>(null);
|
||||
const [isReady, setReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!appRef.current) {
|
||||
appRef.current = true;
|
||||
|
||||
const init = async () => {
|
||||
const { default: game } = await import("./game");
|
||||
const { app, clean } = await game();
|
||||
targetRef.current?.appendChild(app.view as never);
|
||||
|
||||
appRef.current = app;
|
||||
cleanRef.current = clean;
|
||||
setReady(true);
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cleanRef.current) {
|
||||
cleanRef.current();
|
||||
}
|
||||
};
|
||||
}, [setReady]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageMetadata
|
||||
title="Pet the Furina"
|
||||
description="Play pet the furina meme game"
|
||||
keywords="pet furina, pet the furina, pet the meme, furina pat pat, pat furina"
|
||||
/>
|
||||
<div className="h-screen w-full bg-slate-900 overflow-hidden relative">
|
||||
<PageMetadata title="" />
|
||||
<BackgroundSlideshow />
|
||||
|
||||
{!isReady ? <LoadingPage /> : null}
|
||||
|
||||
<div
|
||||
ref={targetRef}
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center",
|
||||
styles.canvasContainer
|
||||
)}
|
||||
/>
|
||||
|
||||
<Credits />
|
||||
<DateTime />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Credits = () => {
|
||||
const modal = useModal();
|
||||
const BackgroundSlideshow = () => {
|
||||
const { data: wallpapers } = useQuery({
|
||||
queryKey: ["wallpapers"],
|
||||
queryFn: async () => {
|
||||
const items = await pb.collection("wallpapers").getFullList({
|
||||
sort: "@random",
|
||||
expand: "artwork",
|
||||
});
|
||||
|
||||
return items.map((item) => {
|
||||
const artwork = item.expand?.artwork;
|
||||
return pb.files.getUrl(artwork, artwork?.image);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container pt-4 pb-16">
|
||||
<Button onClick={modal.onOpen}>Assets Credits</Button>
|
||||
<>
|
||||
<img
|
||||
src={titleImg}
|
||||
alt="title"
|
||||
className="h-4 md:h-8 absolute top-6 left-6 md:top-8 md:left-8 lg:left-[5%] lg:top-[5%] z-[5]"
|
||||
/>
|
||||
|
||||
<Modal {...modal} title="Big Thanks to:" size="xl">
|
||||
<pre className="font-sans overflow-x-auto">
|
||||
{`
|
||||
Furina Stickers:
|
||||
Guido_ (https://risibank.fr/media/297778-genshin-archon-hydro-c6-r5-soutine)
|
||||
Coll5 (https://risibank.fr/media/317061-furina-focalor-genshin)
|
||||
{wallpapers && wallpapers?.length > 0 ? (
|
||||
<BackgroundImage src={wallpapers[0]} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Music:
|
||||
Kururin Furina Cover by Ariyutta (https://facebook.com/arbi.yudatama)
|
||||
pet the peepo by NitroiF (https://www.youtube.com/shorts/ll2Au3CdV2k)
|
||||
type BackgroundImageProps = {
|
||||
src: string;
|
||||
};
|
||||
|
||||
Hand Sprite:
|
||||
@soapmangraylace2752 (https://www.youtube.com/shorts/HEguW7Gmu2w)
|
||||
Fijiwaterhelp (https://jailbreak.fandom.com/wiki/User_blog:Fijiwaterhelp/hand_petting)
|
||||
`.trim()}
|
||||
</pre>
|
||||
</Modal>
|
||||
const BackgroundImage = ({ src }: BackgroundImageProps) => {
|
||||
const [isLoaded, setLoaded] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
src={src}
|
||||
alt="img"
|
||||
className="hidden"
|
||||
onLoad={() => setTimeout(() => setLoaded(true), 100)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"absolute w-[100vw] bg-center bg-cover inset-0 opacity-0 transition-opacity duration-500",
|
||||
isLoaded ? "opacity-100" : ""
|
||||
)}
|
||||
style={{ backgroundImage: `url('${src}')` }}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const DateTime = () => {
|
||||
const [time, setTime] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const intv = setInterval(() => {
|
||||
setTime(new Date());
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(intv);
|
||||
};
|
||||
}, [setTime]);
|
||||
|
||||
const message = useMemo(() => {
|
||||
const hours = time.getHours();
|
||||
let msg = "Day";
|
||||
|
||||
if (hours >= 18 && hours <= 2) {
|
||||
msg = "Night";
|
||||
} else if (hours > 2 && hours <= 9) {
|
||||
msg = "Morning";
|
||||
} else if (hours > 9 && hours <= 15) {
|
||||
msg = "Day";
|
||||
} else if (hours > 15 && hours < 18) {
|
||||
msg = "Evening";
|
||||
}
|
||||
|
||||
return `Good ${msg}~ 💧✨`;
|
||||
}, [time]);
|
||||
|
||||
return (
|
||||
<div className="absolute left-1/2 -translate-x-1/2 lg:left-[5%] lg:translate-x-0 bottom-1/2 translate-y-1/2 w-full md:w-auto px-4 md:px-0 text-white [text-shadow:_0_1px_5px_rgb(0_0_0_/_60%)] text-center lg:text-left z-10">
|
||||
<p className="text-md md:text-2xl">{message}</p>
|
||||
<p className="text-7xl md:text-8xl font-light mt-0.5">
|
||||
{dayjs(time).format("HH:mm")}
|
||||
</p>
|
||||
<p className="text-md md:text-lg font-light mt-2">
|
||||
{dayjs(time).format("dddd, DD MMM YYYY")}
|
||||
</p>
|
||||
|
||||
<AppNav className="mt-8" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AppNav = ({ className }: ComponentProps<"div">) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-start justify-center lg:justify-start flex-wrap gap-3 md:gap-x-4 lg:gap-x-6",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<AppNavItem title="Pat Furina" icon={icons.patFurina} path="/pat-pat" />
|
||||
<AppNavItem title="Album" icon={icons.treasures} path="/treasures" />
|
||||
<AppNavItem
|
||||
title="Facebook"
|
||||
icon={icons.facebook}
|
||||
path="https://www.facebook.com/"
|
||||
iconClassName="p-1"
|
||||
/>
|
||||
<AppNavItem
|
||||
title="X"
|
||||
icon={icons.twitter}
|
||||
path="https://twitter.com/"
|
||||
iconClassName="bg-black"
|
||||
/>
|
||||
<AppNavItem
|
||||
title="Furinamains Discord"
|
||||
icon={icons.furinamains}
|
||||
path="https://discord.com/invite/ew8yz3h5at"
|
||||
iconClassName="bg-[#6b473a]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type AppNavItemProps = {
|
||||
title: string;
|
||||
icon: string;
|
||||
path?: string;
|
||||
iconClassName?: string;
|
||||
};
|
||||
|
||||
const AppNavItem = ({ title, icon, path, iconClassName }: AppNavItemProps) => {
|
||||
return (
|
||||
<Link to={path || "#"} className="flex flex-col items-center w-12 group">
|
||||
<div
|
||||
className={cn(
|
||||
"bg bg-white rounded-lg w-12 h-12 overflow-hidden group-hover:scale-110 transition-all",
|
||||
iconClassName
|
||||
)}
|
||||
>
|
||||
<img
|
||||
src={icon}
|
||||
alt={title}
|
||||
className="w-full h-full object-contain rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<p className={cn("text-white text-center mt-2 text-sm")}>{title}</p>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomePage;
|
||||
|
91
src/pages/pat-pat/page.tsx
Normal file
91
src/pages/pat-pat/page.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import styles from "./style.module.css";
|
||||
import { cn } from "@/utility/utils";
|
||||
import LoadingPage from "../misc/loading-page";
|
||||
import PageMetadata from "@/components/containers/PageMetadata";
|
||||
import Modal from "@/components/ui/Modal";
|
||||
import useModal from "@/hooks/useModal";
|
||||
import Button from "@/components/ui/Button";
|
||||
|
||||
const PatPatPage = () => {
|
||||
const appRef = useRef<any>();
|
||||
const cleanRef = useRef<any>();
|
||||
const targetRef = useRef<HTMLDivElement>(null);
|
||||
const [isReady, setReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!appRef.current) {
|
||||
appRef.current = true;
|
||||
|
||||
const init = async () => {
|
||||
const { default: game } = await import("./game");
|
||||
const { app, clean } = await game();
|
||||
targetRef.current?.appendChild(app.view as never);
|
||||
|
||||
appRef.current = app;
|
||||
cleanRef.current = clean;
|
||||
setReady(true);
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (cleanRef.current) {
|
||||
cleanRef.current();
|
||||
}
|
||||
};
|
||||
}, [setReady]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageMetadata
|
||||
title="Pet the Furina"
|
||||
description="Play pet the furina meme game"
|
||||
keywords="pet furina, pet the furina, pet the meme, furina pat pat, pat furina"
|
||||
/>
|
||||
|
||||
{!isReady ? <LoadingPage /> : null}
|
||||
|
||||
<div
|
||||
ref={targetRef}
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center",
|
||||
styles.canvasContainer
|
||||
)}
|
||||
/>
|
||||
|
||||
<Credits />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Credits = () => {
|
||||
const modal = useModal();
|
||||
|
||||
return (
|
||||
<div className="container pt-4 pb-16">
|
||||
<Button onClick={modal.onOpen}>Assets Credits</Button>
|
||||
|
||||
<Modal {...modal} title="Big Thanks to:" size="xl">
|
||||
<pre className="font-sans overflow-x-auto">
|
||||
{`
|
||||
Furina Stickers:
|
||||
Guido_ (https://risibank.fr/media/297778-genshin-archon-hydro-c6-r5-soutine)
|
||||
Coll5 (https://risibank.fr/media/317061-furina-focalor-genshin)
|
||||
|
||||
Music:
|
||||
Kururin Furina Cover by Ariyutta (https://facebook.com/arbi.yudatama)
|
||||
|
||||
Hand Sprite:
|
||||
@soapmangraylace2752 (https://www.youtube.com/shorts/HEguW7Gmu2w)
|
||||
Fijiwaterhelp (https://jailbreak.fandom.com/wiki/User_blog:Fijiwaterhelp/hand_petting)
|
||||
`.trim()}
|
||||
</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PatPatPage;
|
@ -1408,6 +1408,11 @@ csstype@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
dayjs@^1.11.10:
|
||||
version "1.11.10"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
|
Loading…
x
Reference in New Issue
Block a user