add treasures (artworks)
This commit is contained in:
parent
205f92001c
commit
2cbaca844e
5
backend/.gitignore
vendored
Normal file
5
backend/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
CHANGELOG.md
|
||||
LICENSE.md
|
||||
*.zip
|
||||
pocketbase
|
||||
pb_data/
|
1
backend/.pbversion
Normal file
1
backend/.pbversion
Normal file
@ -0,0 +1 @@
|
||||
0.20.5
|
13
backend/package.json
Normal file
13
backend/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "./pocketbase serve",
|
||||
"clean-migrations": "rm -rf pb_migrations && ./pocketbase migrate collections"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
11
backend/pb_hooks/main.pb.js
Normal file
11
backend/pb_hooks/main.pb.js
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
|
||||
// artwork view hook
|
||||
onRecordViewRequest((e) => {
|
||||
const { record } = e;
|
||||
if (record) {
|
||||
record.set("views", record.getInt("views") + 1);
|
||||
$app.dao().saveRecord(record);
|
||||
}
|
||||
}, "artworks");
|
161
backend/pb_migrations/1704899482_collections_snapshot.js
Normal file
161
backend/pb_migrations/1704899482_collections_snapshot.js
Normal file
@ -0,0 +1,161 @@
|
||||
/// <reference path="../pb_data/types.d.ts" />
|
||||
migrate((db) => {
|
||||
const snapshot = [
|
||||
{
|
||||
"id": "_pb_users_auth_",
|
||||
"created": "2024-01-10 09:29:21.510Z",
|
||||
"updated": "2024-01-10 09:29:21.511Z",
|
||||
"name": "users",
|
||||
"type": "auth",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "users_name",
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "users_avatar",
|
||||
"name": "avatar",
|
||||
"type": "file",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"mimeTypes": [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
"image/gif",
|
||||
"image/webp"
|
||||
],
|
||||
"thumbs": null,
|
||||
"maxSelect": 1,
|
||||
"maxSize": 5242880,
|
||||
"protected": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": "id = @request.auth.id",
|
||||
"viewRule": "id = @request.auth.id",
|
||||
"createRule": "",
|
||||
"updateRule": "id = @request.auth.id",
|
||||
"deleteRule": "id = @request.auth.id",
|
||||
"options": {
|
||||
"allowEmailAuth": true,
|
||||
"allowOAuth2Auth": true,
|
||||
"allowUsernameAuth": true,
|
||||
"exceptEmailDomains": null,
|
||||
"manageRule": null,
|
||||
"minPasswordLength": 8,
|
||||
"onlyEmailDomains": null,
|
||||
"onlyVerified": false,
|
||||
"requireEmail": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "eo6iaxf4pkeqynf",
|
||||
"created": "2024-01-10 09:34:57.731Z",
|
||||
"updated": "2024-01-10 15:08:00.292Z",
|
||||
"name": "artworks",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "p6dor6eo",
|
||||
"name": "image",
|
||||
"type": "file",
|
||||
"required": true,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"mimeTypes": [
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"image/tiff",
|
||||
"image/bmp",
|
||||
"image/svg+xml"
|
||||
],
|
||||
"thumbs": [
|
||||
"256x192",
|
||||
"256x384",
|
||||
"32x48"
|
||||
],
|
||||
"maxSelect": 1,
|
||||
"maxSize": 5242880,
|
||||
"protected": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "9w1tjysa",
|
||||
"name": "artistName",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "lkiiiwrt",
|
||||
"name": "srcUrl",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "sfh7xwdb",
|
||||
"name": "views",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"noDecimal": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": "",
|
||||
"viewRule": "",
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
}
|
||||
];
|
||||
|
||||
const collections = snapshot.map((item) => new Collection(item));
|
||||
|
||||
return Dao(db).importCollections(collections, true, null);
|
||||
}, (db) => {
|
||||
return null;
|
||||
})
|
@ -17,9 +17,11 @@
|
||||
"howler": "^2.2.4",
|
||||
"lucide-react": "^0.306.0",
|
||||
"pixi.js": "^7.3.3",
|
||||
"pocketbase": "^0.20.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-query": "^3.39.3",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"react-toastify": "^9.1.3",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import Router from "./Router";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { QueryClient, QueryClientProvider } from "react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
const App = () => {
|
||||
const [queryClient] = useState(new QueryClient());
|
||||
|
||||
return (
|
||||
<>
|
||||
<Router />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router />
|
||||
</QueryClientProvider>
|
||||
<ToastContainer />
|
||||
</>
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import ErrorBoundaryPage from "./pages/errors/error-boundary/page";
|
||||
|
||||
const HomePage = lazy(() => import("./pages/home/page"));
|
||||
const MyFurinaPage = lazy(() => import("./pages/my-furina/page"));
|
||||
const ArtworksPage = lazy(() => import("./pages/artworks/page"));
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -12,6 +13,10 @@ const router = createBrowserRouter([
|
||||
children: [
|
||||
{ index: true, Component: HomePage },
|
||||
{ path: "/toodle", Component: MyFurinaPage },
|
||||
{
|
||||
path: "/treasures",
|
||||
children: [{ index: true, Component: ArtworksPage }],
|
||||
},
|
||||
],
|
||||
ErrorBoundary: () => (
|
||||
<MainLayout>
|
||||
|
BIN
src/assets/audio/VO_JA_Furina_Elemental_Burst_03.ogg
Normal file
BIN
src/assets/audio/VO_JA_Furina_Elemental_Burst_03.ogg
Normal file
Binary file not shown.
BIN
src/assets/audio/VO_JA_Furina_Opening_Treasure_Chest_02.ogg
Normal file
BIN
src/assets/audio/VO_JA_Furina_Opening_Treasure_Chest_02.ogg
Normal file
Binary file not shown.
@ -9,7 +9,7 @@ import { cn } from "@/utility/utils";
|
||||
const AppBar = () => {
|
||||
return (
|
||||
<header className="w-full bg-[#111a21] shadow">
|
||||
<div className="container py-8 md:py-4 flex flex-col md:flex-row items-center gap-4">
|
||||
<div className="mx-auto max-w-5xl md:px-4 pt-6 md:py-4 flex flex-col md:flex-row items-center gap-4">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex flex-col md:flex-row items-center gap-2 md:gap-4"
|
||||
@ -24,6 +24,7 @@ const AppBar = () => {
|
||||
|
||||
<Navbar>
|
||||
<NavbarItem path="/" title="Pet the Furina" />
|
||||
<NavbarItem path="/treasures" title="Treasures‧₊˚" />
|
||||
<NavbarItem path="/toodle" title="Toodle-oo~" />
|
||||
</Navbar>
|
||||
</div>
|
||||
@ -33,7 +34,7 @@ const AppBar = () => {
|
||||
|
||||
const Navbar = ({ children }: ComponentProps<"div">) => {
|
||||
return (
|
||||
<nav className="flex-1 flex items-center justify-end gap-3 md:gap-5 overflow-x-auto md:overflow-x-hidden">
|
||||
<nav className="md:flex-1 self-stretch md:self-center flex items-center px-2 md:px-0 md:justify-end md:gap-5 overflow-x-auto md:overflow-x-hidden">
|
||||
{children}
|
||||
</nav>
|
||||
);
|
||||
@ -50,7 +51,10 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
const isActive = isExact ? pathname === path : pathname.startsWith(path);
|
||||
|
||||
return (
|
||||
<Link to={path} className="group flex items-center md:py-4">
|
||||
<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"
|
||||
>
|
||||
<img
|
||||
src={ahogeImg}
|
||||
alt="ahoge"
|
||||
@ -62,8 +66,8 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||
|
||||
<p
|
||||
className={cn(
|
||||
"text-white ml-2 md:ml-4 md:text-xl border-b-2 border-dotted group-hover:border-white/80 border-transparent transition-all",
|
||||
isActive ? "border-white" : ""
|
||||
"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",
|
||||
isActive ? "border-primary-500 md:border-white" : ""
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
|
@ -17,11 +17,8 @@ const MainLayout = ({ children }: ComponentProps<"div">) => {
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
<footer className="bg-primary-700 px-4 py-8 text-center">
|
||||
<footer className="bg-primary-700 shadow relative z-10 px-4 py-8 text-center">
|
||||
<p className="text-sm text-white">
|
||||
<a href="http://rul.sh/">
|
||||
Made with <span className="text-red-300">♡</span> by Khairul.
|
||||
</a>{" "}
|
||||
Artworks displayed belong to respective owners.
|
||||
</p>
|
||||
</footer>
|
||||
|
36
src/components/ui/Badge.tsx
Normal file
36
src/components/ui/Badge.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/utility/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded border border-primary-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-primary-950 focus:ring-offset-2 dark:border-primary-800 dark:focus:ring-primary-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary-600 text-primary-50 hover:bg-primary-600/80 dark:bg-primary-50 dark:text-primary-900 dark:hover:bg-primary-50/80",
|
||||
secondary:
|
||||
"border-transparent bg-primary-100 text-primary-900 hover:bg-primary-100/80 dark:bg-primary-800 dark:text-primary-50 dark:hover:bg-primary-800/80",
|
||||
destructive:
|
||||
"border-transparent bg-red-500 text-primary-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-primary-50 dark:hover:bg-red-900/80",
|
||||
outline: "text-primary-950 dark:text-primary-50",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export default Badge;
|
42
src/components/ui/LazyImage.tsx
Normal file
42
src/components/ui/LazyImage.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { cn } from "@/utility/utils";
|
||||
import React, { useState } from "react";
|
||||
|
||||
type Props = React.ComponentProps<"img"> & {
|
||||
lazySrc: string;
|
||||
containerClassName?: string;
|
||||
};
|
||||
|
||||
const LazyImage = ({
|
||||
lazySrc,
|
||||
src,
|
||||
containerClassName,
|
||||
className,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [isLoaded, setLoaded] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-no-repeat bg-cover",
|
||||
!isLoaded ? "blur-md" : "",
|
||||
containerClassName
|
||||
)}
|
||||
style={{ backgroundImage: `url('${lazySrc}')` }}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
loading="lazy"
|
||||
onLoad={() => setLoaded(true)}
|
||||
className={cn(
|
||||
"transition-all",
|
||||
isLoaded ? "opacity-100" : "opacity-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LazyImage;
|
156
src/components/ui/Sheet.tsx
Normal file
156
src/components/ui/Sheet.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
import * as React from "react";
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { cn } from "@/utility/utils";
|
||||
|
||||
const SheetRoot = SheetPrimitive.Root;
|
||||
|
||||
const SheetPortal = SheetPrimitive.Portal;
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-white shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
interface SheetContentProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
));
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||
|
||||
const SheetHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col p-4 space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetHeader.displayName = "SheetHeader";
|
||||
|
||||
const SheetFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetFooter.displayName = "SheetFooter";
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold text-slate-950 dark:text-slate-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
type SheetProps = React.ComponentProps<typeof SheetRoot> & {
|
||||
isOpen?: boolean;
|
||||
title?: string;
|
||||
description?: string;
|
||||
position?: SheetContentProps["side"];
|
||||
};
|
||||
|
||||
const Sheet = ({
|
||||
isOpen,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
position,
|
||||
...props
|
||||
}: SheetProps) => {
|
||||
return (
|
||||
<SheetRoot open={isOpen} {...props}>
|
||||
<SheetContent side={position} className="rounded-t-2xl">
|
||||
<SheetHeader>
|
||||
{title ? <SheetTitle>{title}</SheetTitle> : null}
|
||||
{description ? (
|
||||
<SheetDescription>{description}</SheetDescription>
|
||||
) : null}
|
||||
</SheetHeader>
|
||||
<div className="max-h-[90vh] overflow-y-auto">{children}</div>
|
||||
</SheetContent>
|
||||
</SheetRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sheet;
|
@ -1,12 +1,15 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const useModal = () => {
|
||||
const useModal = <T = unknown>() => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [data, setData] = useState<T | undefined | null>(null);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
onOpen() {
|
||||
data,
|
||||
onOpen(_data?: T | null) {
|
||||
setOpen(true);
|
||||
setData(_data);
|
||||
},
|
||||
onClose() {
|
||||
setOpen(false);
|
||||
|
71
src/pages/artworks/page.tsx
Normal file
71
src/pages/artworks/page.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import pb from "@/utility/api";
|
||||
import { Howl } from "howler";
|
||||
import { useQuery } from "react-query";
|
||||
import { Link } from "react-router-dom";
|
||||
import playIcon from "@/assets/icons/play-outline.svg";
|
||||
import openingSfx from "@/assets/audio/VO_JA_Furina_Opening_Treasure_Chest_02.ogg";
|
||||
import ViewSheet from "./viewSheet";
|
||||
import useModal from "@/hooks/useModal";
|
||||
import LazyImage from "@/components/ui/LazyImage";
|
||||
|
||||
const openingChestSfx = new Howl({
|
||||
src: openingSfx,
|
||||
preload: true,
|
||||
});
|
||||
|
||||
const ArtworksPage = () => {
|
||||
const { data } = useQuery({
|
||||
queryKey: ["artworks"],
|
||||
queryFn: () =>
|
||||
pb.collection("artworks").getList(1, 100, { sort: "-created" }),
|
||||
});
|
||||
const viewItemModal = useModal<string>();
|
||||
|
||||
return (
|
||||
<div className="container py-16">
|
||||
<h1 className="text-2xl">Treasures</h1>
|
||||
<div>
|
||||
<p className="italic inline">Take it. Ahem... I allow you!</p>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-white rounded border-2 border-primary-300 hover:bg-gray-200 p-1 md:p-0.5 ml-2"
|
||||
>
|
||||
<img
|
||||
src={playIcon}
|
||||
alt="play"
|
||||
className="h-4"
|
||||
onClick={() => openingChestSfx.play()}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mt-8">
|
||||
{data?.items.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
// to={`/treasures/${item.id}`}
|
||||
to="#"
|
||||
className="bg-white rounded-lg shadow border border-gray-300 overflow-hidden hover:scale-105 transition-all relative"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
viewItemModal.onOpen(item.id);
|
||||
}}
|
||||
>
|
||||
<LazyImage
|
||||
lazySrc={pb.files.getUrl(item, item.image, { thumb: "32x48" })}
|
||||
src={pb.files.getUrl(item, item.image)}
|
||||
className="w-full aspect-[0.8] object-cover"
|
||||
/>
|
||||
<div className="absolute bottom-2 left-2 px-3 py-1 rounded-md bg-black/20 backdrop-blur-sm">
|
||||
<p className="text-white">{item.artistName}</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<ViewSheet modal={viewItemModal} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArtworksPage;
|
75
src/pages/artworks/viewSheet.tsx
Normal file
75
src/pages/artworks/viewSheet.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import Sheet from "@/components/ui/Sheet";
|
||||
import useModal from "@/hooks/useModal";
|
||||
import pb from "@/utility/api";
|
||||
import { useQuery } from "react-query";
|
||||
import loadingIllust from "@/assets/images/l9fsdoa2j7vb1.gif";
|
||||
import Badge from "@/components/ui/Badge";
|
||||
import Button from "@/components/ui/Button";
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
|
||||
type Props = {
|
||||
modal: ReturnType<typeof useModal<string>>;
|
||||
};
|
||||
|
||||
const ViewSheet = ({ modal }: Props) => {
|
||||
const id = modal.data;
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ["artwork", id],
|
||||
queryFn: () => pb.collection("artworks").getOne(id || ""),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
return (
|
||||
<Sheet {...modal} title="View Item" position="bottom">
|
||||
{isLoading ? (
|
||||
<div className="min-h-[320px] flex flex-col items-center justify-center text-center">
|
||||
<img src={loadingIllust} className="h-40 animate-bounce" />
|
||||
<p className="mt-2">Please wait a moment...</p>
|
||||
</div>
|
||||
) : isError || !data ? (
|
||||
<div className="min-h-[320px] flex flex-col items-center justify-center text-center">
|
||||
<h1 className="text-2xl">An error occured.</h1>
|
||||
<p className="mt-2">Cannot load item</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="flex-1 bg-gray-50">
|
||||
<a href={data.srcUrl} target="_blank">
|
||||
<img
|
||||
src={pb.files.getUrl(data, data.image)}
|
||||
className="w-full max-h-[80vh] object-contain"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="md:w-1/3 border-t md:border-l md:border-t-0 py-4 md:pt-0 pl-4 lg:pl-8 overflow-y-auto overflow-x-hidden truncate">
|
||||
<Button
|
||||
className="hidden md:flex pl-2 md:mb-6"
|
||||
onClick={modal.onClose}
|
||||
>
|
||||
<ChevronLeft /> Back
|
||||
</Button>
|
||||
|
||||
<Badge>Artist Name</Badge>
|
||||
<p className="mt-1 truncate">{data.artistName}</p>
|
||||
|
||||
<Badge className="mt-4">Source</Badge>
|
||||
<a
|
||||
href={data.srcUrl}
|
||||
target="_blank"
|
||||
className="block mt-1 text-primary-500 font-medium hover:underline truncate"
|
||||
>
|
||||
{cleanUrl(data.srcUrl)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Sheet>
|
||||
);
|
||||
};
|
||||
|
||||
const cleanUrl = (url: string) => {
|
||||
return url.replace("https://", "").replace("http://", "").replace("www.", "");
|
||||
};
|
||||
|
||||
export default ViewSheet;
|
@ -65,7 +65,7 @@ const Credits = () => {
|
||||
const modal = useModal();
|
||||
|
||||
return (
|
||||
<div className="container pt-4 pb-16 border-t">
|
||||
<div className="container pt-4 pb-16">
|
||||
<Button onClick={modal.onOpen}>Assets Credits</Button>
|
||||
|
||||
<Modal {...modal} title="Big Thanks to:" size="xl">
|
||||
|
@ -4,13 +4,18 @@ import buildImg from "@/assets/images/furina-build.webp";
|
||||
import furinaChibiImg from "@/assets/images/113932900_p0_master1200.webp";
|
||||
import copyIcon from "@/assets/icons/copy-outline.svg";
|
||||
import playIcon from "@/assets/icons/play-outline.svg";
|
||||
import skillAudio from "@/assets/audio/VO_JA_Furina_Elemental_Skill_1_04.ogg";
|
||||
import skillAudio from "@/assets/audio/VO_JA_Furina_Elemental_Burst_03.ogg";
|
||||
import { cn, copyToClipboard, showToast } from "@/utility/utils";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Howl } from "howler";
|
||||
import styles from "./style.module.css";
|
||||
import PageMetadata from "@/components/containers/PageMetadata";
|
||||
|
||||
const gameUID = "828243224";
|
||||
const skillAudioSfx = new Howl({
|
||||
src: skillAudio,
|
||||
preload: true,
|
||||
});
|
||||
|
||||
const FurinaBelovedPage = () => {
|
||||
const onCopyUID = () => {
|
||||
@ -132,47 +137,22 @@ const HeroBackground = () => {
|
||||
};
|
||||
|
||||
const SkillAudioPlayer = () => {
|
||||
const skillAudioRef = useRef<HTMLAudioElement>(null);
|
||||
const playingTimeout = useRef<any>(null);
|
||||
const [isPlaying, setPlaying] = useState(false);
|
||||
|
||||
const onPlayAudio = () => {
|
||||
skillAudioRef.current?.play();
|
||||
setPlaying(true);
|
||||
|
||||
if (playingTimeout.current) {
|
||||
clearTimeout(playingTimeout.current);
|
||||
}
|
||||
|
||||
playingTimeout.current = setTimeout(() => {
|
||||
setPlaying(false);
|
||||
playingTimeout.current = null;
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={`group flex flex-col items-center justify-center text-center border border-transparent hover:border-primary-500
|
||||
<button
|
||||
type="button"
|
||||
className={`group flex flex-col items-center justify-center text-center border border-transparent hover:border-primary-500
|
||||
rounded-xl px-6 py-4 hover:shadow transition-all hover:text-orange-500 hover:scale-105 active:scale-100 active:translate-y-5`}
|
||||
onClick={onPlayAudio}
|
||||
>
|
||||
<img
|
||||
src={playIcon}
|
||||
className={cn("h-8", isPlaying ? styles.playAnims : "")}
|
||||
/>
|
||||
<p className="text-xl font-light mt-4">
|
||||
✮⋆{" "}
|
||||
<span className="group-hover:text-purple-500">The curtain rises</span>{" "}
|
||||
✩ ₊˚
|
||||
</p>
|
||||
</button>
|
||||
|
||||
<audio ref={skillAudioRef}>
|
||||
<source src={skillAudio} type="audio/ogg" />
|
||||
</audio>
|
||||
</>
|
||||
onClick={() => skillAudioSfx.play()}
|
||||
>
|
||||
<img src={playIcon} className="h-8" />
|
||||
<p className="text-xl font-light mt-4">
|
||||
✮⋆{" "}
|
||||
<span className="group-hover:text-purple-500">
|
||||
Let my name echo in song!
|
||||
</span>{" "}
|
||||
✩ ₊˚
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
|
6
src/utility/api.ts
Normal file
6
src/utility/api.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import PocketBase from "pocketbase";
|
||||
|
||||
export const baseUrl = "http://127.0.0.1:8090";
|
||||
const pb = new PocketBase(baseUrl);
|
||||
|
||||
export default pb;
|
90
yarn.lock
90
yarn.lock
@ -184,6 +184,13 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.22.5"
|
||||
|
||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
|
||||
version "7.23.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650"
|
||||
integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.13.10", "@babel/runtime@^7.23.5":
|
||||
version "7.23.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.7.tgz#dd7c88deeb218a0f8bd34d5db1aa242e0f203193"
|
||||
@ -1209,6 +1216,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
big-integer@^1.6.16:
|
||||
version "1.6.52"
|
||||
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85"
|
||||
integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
@ -1236,6 +1248,20 @@ braces@^3.0.2, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
broadcast-channel@^3.4.1:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
|
||||
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.7.2"
|
||||
detect-node "^2.1.0"
|
||||
js-sha3 "0.8.0"
|
||||
microseconds "0.2.0"
|
||||
nano-time "1.0.0"
|
||||
oblivious-set "1.0.0"
|
||||
rimraf "3.0.2"
|
||||
unload "2.2.0"
|
||||
|
||||
browserslist@^4.21.10, browserslist@^4.22.2:
|
||||
version "4.22.2"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b"
|
||||
@ -1408,6 +1434,11 @@ detect-node-es@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||
|
||||
detect-node@^2.0.4, detect-node@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||
@ -1938,6 +1969,11 @@ jiti@^1.19.1:
|
||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
|
||||
integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
|
||||
|
||||
js-sha3@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -2048,6 +2084,14 @@ lucide-react@^0.306.0:
|
||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.306.0.tgz#07e427c1c0e6c6d50712b746b889ff807606131c"
|
||||
integrity sha512-eShuk2PI3vxN4YN8kNPmhAsroSvPXbtaxU/UX/zrBcLLg8FeFH9MG7C2EYzYsT2rNrPVjbP7rpBz3mOviZYN3A==
|
||||
|
||||
match-sorter@^6.0.2:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
|
||||
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
remove-accents "0.4.2"
|
||||
|
||||
merge2@^1.3.0, merge2@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
@ -2061,6 +2105,11 @@ micromatch@^4.0.4, micromatch@^4.0.5:
|
||||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
microseconds@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
|
||||
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
|
||||
|
||||
minimatch@9.0.3, minimatch@^9.0.1:
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
||||
@ -2094,6 +2143,13 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nano-time@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
|
||||
integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==
|
||||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanoid@^3.3.7:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||
@ -2134,6 +2190,11 @@ object-inspect@^1.9.0:
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
|
||||
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
|
||||
|
||||
oblivious-set@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
|
||||
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@ -2263,6 +2324,11 @@ pixi.js@^7.3.3:
|
||||
"@pixi/text-bitmap" "7.3.3"
|
||||
"@pixi/text-html" "7.3.3"
|
||||
|
||||
pocketbase@^0.20.1:
|
||||
version "0.20.1"
|
||||
resolved "https://registry.yarnpkg.com/pocketbase/-/pocketbase-0.20.1.tgz#e34a0113306322f9382a8b775de3b83f7304929f"
|
||||
integrity sha512-Gl51UBc1U03JlwmwMkUIa1OHbcTmmdhyMPV1aJyHp9HuY5VUlh0t4hcx6D1fdhYsJcoh3kc6mpwhTBfXDoyn8w==
|
||||
|
||||
postcss-import@^15.1.0:
|
||||
version "15.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70"
|
||||
@ -2380,6 +2446,15 @@ react-is@^16.13.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-query@^3.39.3:
|
||||
version "3.39.3"
|
||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35"
|
||||
integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
broadcast-channel "^3.4.1"
|
||||
match-sorter "^6.0.2"
|
||||
|
||||
react-refresh@^0.14.0:
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
|
||||
@ -2466,6 +2541,11 @@ regenerator-runtime@^0.14.0:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
|
||||
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
|
||||
|
||||
remove-accents@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
||||
integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
@ -2485,7 +2565,7 @@ reusify@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
rimraf@^3.0.2:
|
||||
rimraf@3.0.2, rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
@ -2764,6 +2844,14 @@ undici-types@~5.26.4:
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
unload@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
|
||||
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
detect-node "^2.0.4"
|
||||
|
||||
update-browserslist-db@^1.0.13:
|
||||
version "1.0.13"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
|
||||
|
Loading…
x
Reference in New Issue
Block a user