add /s/
This commit is contained in:
parent
86b2b7625f
commit
3641c40984
|
@ -6,7 +6,7 @@ import CalHeatmap from 'cal-heatmap';
|
|||
import Legend from 'cal-heatmap/plugins/Legend';
|
||||
// @ts-ignore cal-heatmap is jenk
|
||||
import Tooltip from 'cal-heatmap/plugins/Tooltip';
|
||||
import { DataRecord } from 'cal-heatmap/src/options/Options'
|
||||
import { DataRecord } from 'cal-heatmap/src/options/Options';
|
||||
import 'cal-heatmap/cal-heatmap.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useState, useRef } from 'react';
|
|
@ -14,13 +14,6 @@ export default async function FundingGoal(): Promise<React.JSX.Element> {
|
|||
const goals = await getGoals(pledgeSum);
|
||||
if (!goals || !goals?.featuredFunded?.amountCents || !goals?.featuredUnfunded?.amountCents || !goals?.featuredFunded?.amountCents || !goals?.featuredUnfunded?.completedPercentage || !goals?.featuredFunded?.completedPercentage ) return <></>
|
||||
|
||||
// return (
|
||||
// <pre>
|
||||
// <code>
|
||||
// {JSON.stringify(goals, null, 2)}
|
||||
// </code>
|
||||
// </pre>
|
||||
// )
|
||||
return (
|
||||
<>
|
||||
{/* <p>
|
||||
|
|
|
@ -5,9 +5,7 @@ interface ILocalizedDateProps {
|
|||
}
|
||||
|
||||
export function LocalizedDate ({ date }: ILocalizedDateProps) {
|
||||
return (
|
||||
<span>
|
||||
{new Date(date).toLocaleDateString()}
|
||||
</span>
|
||||
)
|
||||
const isoString = date.toISOString();
|
||||
const localeString = date.toLocaleDateString();
|
||||
return <time dateTime={isoString}>{localeString}</time>
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
'use client';
|
||||
|
||||
import { IStream } from "@/lib/streams";
|
||||
import NotFound from "app/s/[cuid]/not-found";
|
||||
import { IVod } from "@/lib/vods";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { LocalizedDate } from "./localized-date";
|
||||
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faTriangleExclamation, faCircleInfo, faThumbsUp, IconDefinition } from "@fortawesome/free-solid-svg-icons";
|
||||
import styles from '@/assets/styles/fp.module.css'
|
||||
import { Hemisphere, Moon } from "lunarphase-js";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
|
||||
export interface IStreamProps {
|
||||
stream: IStream;
|
||||
}
|
||||
type Status = 'missing' | 'issue' | 'good';
|
||||
interface StyleDef {
|
||||
heading: string;
|
||||
icon: IconDefinition;
|
||||
desc1: string;
|
||||
desc2: string;
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(string: string): string {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
export default function StreamPage({ stream }: IStreamProps) {
|
||||
if (!stream) return <NotFound></NotFound>
|
||||
const displayName = stream.attributes.vtuber.data.attributes.displayName;
|
||||
const date = new Date(stream.attributes.date);
|
||||
const [hemisphere, setHemisphere] = useState(Hemisphere.NORTHERN);
|
||||
const [selectedStatus, setSelectedStatus] = useState<Status>('missing');
|
||||
|
||||
const styleMap: Record<Status, StyleDef> = {
|
||||
'missing': {
|
||||
heading: 'is-danger',
|
||||
icon: faTriangleExclamation,
|
||||
desc1: "We don't have a VOD for this stream.",
|
||||
desc2: 'Know someone who does?'
|
||||
},
|
||||
'issue': {
|
||||
heading: 'is-warning',
|
||||
icon: faCircleInfo,
|
||||
desc1: "We have a VOD for this stream, but it's not full quality.",
|
||||
desc2: 'Have a better copy?'
|
||||
},
|
||||
'good': {
|
||||
heading: 'is-success',
|
||||
icon: faThumbsUp,
|
||||
desc1: "We have a VOD for this stream, and we think it's the best quality possible.",
|
||||
desc2: "Have one that's even better?"
|
||||
}
|
||||
};
|
||||
const { heading, icon, desc1, desc2 } = styleMap[selectedStatus] || {};
|
||||
|
||||
useEffect(() => {
|
||||
const randomHemisphere = (Math.random() < 0.5 ? 0 : 1) ? Hemisphere.NORTHERN : Hemisphere.SOUTHERN;
|
||||
setHemisphere(randomHemisphere);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
<h1 className="title"><LocalizedDate date={date} /> {displayName} Stream Archive</h1>
|
||||
</div>
|
||||
|
||||
<div className="section columns is-multiline">
|
||||
<div className="column is-half">
|
||||
<div className="box">
|
||||
<h2 className="title is-3">Details</h2>
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-full">
|
||||
<span><b>ISO Date</b> </span><span>{date.toISOString()}</span><br></br>
|
||||
<span><b>Lunar Phase</b> </span><span>{Moon.lunarPhase(date)} {Moon.lunarPhaseEmoji(date, { hemisphere })}</span>
|
||||
<br></br>
|
||||
<select className="mt-5"
|
||||
value={selectedStatus}
|
||||
onChange={e => setSelectedStatus(e.target.value as Status)}
|
||||
>
|
||||
<option>good</option>
|
||||
<option>issue</option>
|
||||
<option>missing</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="column is-half">
|
||||
<article className={`message ${heading}`}>
|
||||
<div className="message-header">
|
||||
<span>VOD {capitalizeFirstLetter(selectedStatus)}</span>
|
||||
</div>
|
||||
<div className="message-body has-text-centered">
|
||||
<span className="title is-1"><FontAwesomeIcon icon={icon}></FontAwesomeIcon></span>
|
||||
<p className="mt-3">{desc1}</p>
|
||||
<p className="mt-5">{desc2}<br />
|
||||
<Link href={`/upload?cuid=${stream.attributes.cuid}`}>Upload it here.</Link></p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div className="section">
|
||||
<h1 className="title">VODs</h1>
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Thumbnail">Thmb</abbr></th>
|
||||
<th>Length</th>
|
||||
<th>Uploader</th>
|
||||
<th>Tags</th>
|
||||
<th><abbr title="Timestamps">TS</abbr></th>
|
||||
<th>Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{stream.attributes.vods.data.map((vod: IVod) => (
|
||||
<tr>
|
||||
<th>blah</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -11,7 +11,12 @@ export function Stream({ stream }: IStreamProps) {
|
|||
if (!stream) return <NotFound></NotFound>
|
||||
return (
|
||||
<div className="box">
|
||||
<h3 className="title is-3">Stream {stream.attributes.date}</h3>
|
||||
<pre>
|
||||
<code>
|
||||
{JSON.stringify(stream, null, 2)}
|
||||
</code>
|
||||
</pre>
|
||||
{/* <h3 className="title is-3">Stream {stream.attributes.date}</h3> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link';
|
||||
import VodCard from './vod-card';
|
||||
import { IVtuber } from '@/lib/vtubers';
|
||||
import { IVod } from '@/lib/vods';
|
||||
import { getVodTitle } from './vod-page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { IStream, getStreamsForVtuber } from '@/lib/streams';
|
||||
|
||||
interface IStreamsListProps {
|
||||
vtuber: IVtuber;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
|
||||
interface IStreamsListHeadingProps {
|
||||
slug: string;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export function StreamsListHeading({ slug, displayName }: IStreamsListHeadingProps): React.JSX.Element {
|
||||
return (
|
||||
<div className='box'>
|
||||
<h3 className='title'>
|
||||
<Link href={`/vt/${slug}`}>{displayName}</Link> Streams
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default async function StreamsList({ vtuber, page = 1, pageSize = 24 }: IStreamsListProps): Promise<React.JSX.Element> {
|
||||
if (!vtuber) return <div>vtuber is not defined. vtuber:{JSON.stringify(vtuber, null, 2)}</div>
|
||||
// if (!vods) return <div>failed to load vods</div>;
|
||||
const streams = await getStreamsForVtuber(vtuber.id);
|
||||
if (!streams) return notFound();
|
||||
|
||||
// @todo [x] pagination
|
||||
// @todo [x] sortability
|
||||
return (
|
||||
<>
|
||||
{/* <p>VodsList on page {page}, pageSize {pageSize}, with {vods.data.length} vods</p> */}
|
||||
|
||||
{/* <pre>
|
||||
<code>
|
||||
{JSON.stringify(vods.data, null, 2)}
|
||||
</code>
|
||||
</pre> */}
|
||||
|
||||
|
||||
<div className="columns is-multiline is-mobile">
|
||||
{streams.data.map((stream: IStream) => (
|
||||
<p>@todo StreamCard</p>
|
||||
// <StreamCard
|
||||
// key={vod.id}
|
||||
// id={vod.id}
|
||||
// title={getVodTitle(vod)}
|
||||
// date={vod.attributes.date2}
|
||||
// muxAsset={vod.attributes.muxAsset?.data?.attributes.playbackId}
|
||||
// vtuber={vod.attributes.vtuber.data}
|
||||
// thumbnail={vod.attributes.thumbnail?.data?.attributes?.cdnUrl}
|
||||
// />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -49,7 +49,7 @@ export function Tagger({ vod, setTimestamps }: ITaggerProps): React.JSX.Element
|
|||
}
|
||||
});
|
||||
const [isEditor, setIsEditor] = useState(false);
|
||||
const [tagSuggestions, setTagSuggestions] = useState<ITagSuggestion[]>([])
|
||||
const [tagSuggestions, setTagSuggestions] = useState<ITagSuggestion[]>([]);
|
||||
const { authData } = useAuth();
|
||||
const { timeStamp, tvrs, setTvrs } = useContext(VideoContext);
|
||||
const router = useRouter();
|
||||
|
@ -149,7 +149,8 @@ export function Tagger({ vod, setTimestamps }: ITaggerProps): React.JSX.Element
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (!authData?.accessToken) return <></>
|
||||
|
||||
if (isEditor) {
|
||||
return (
|
||||
<div className='card mt-2' style={{ width: '100%' }}>
|
||||
|
@ -225,5 +226,6 @@ export function Tagger({ vod, setTimestamps }: ITaggerProps): React.JSX.Element
|
|||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||
import { faTags, faLink } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Tag } from './tag';
|
||||
import Link from 'next/link';
|
||||
import VodNav from "./vod-nav";
|
||||
import VodNav from './vod-nav';
|
||||
|
||||
|
||||
export interface IVideoInteractiveProps {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import { faVideo, faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faTwitter } from '@fortawesome/free-brands-svg-icons';
|
||||
import { faVideo, faExternalLinkAlt, faShareAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXTwitter } from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link';
|
||||
|
@ -19,7 +19,6 @@ export interface IVodNavProps {
|
|||
}
|
||||
|
||||
export default function VodNav ({ vod, safeDate }: IVodNavProps) {
|
||||
|
||||
return (
|
||||
<nav className='level'>
|
||||
<div className='level-left'>
|
||||
|
@ -44,7 +43,7 @@ export default function VodNav ({ vod, safeDate }: IVodNavProps) {
|
|||
<div className='level-item'>
|
||||
<Link
|
||||
download={getDownloadLink(vod.attributes.videoSrcHash, safeDate, vod.attributes.vtuber.data.attributes.slug, 'source')}
|
||||
className='button is-info is-small mb-1'
|
||||
className='button is-info is-small'
|
||||
target="_blank"
|
||||
prefetch={false}
|
||||
href={getDownloadLink(vod.attributes.videoSrcHash, safeDate, vod.attributes.vtuber.data.attributes.slug, 'source')}
|
||||
|
@ -61,7 +60,7 @@ export default function VodNav ({ vod, safeDate }: IVodNavProps) {
|
|||
<span>
|
||||
<Link
|
||||
download={getDownloadLink(vod.attributes.video240Hash, safeDate, vod.attributes.vtuber.data.attributes.slug, '240p')}
|
||||
className='button is-info is-small mb-1'
|
||||
className='button is-info is-small'
|
||||
target="_blank"
|
||||
prefetch={false}
|
||||
href={getDownloadLink(vod.attributes.video240Hash, safeDate, vod.attributes.vtuber.data.attributes.slug, '240p')}
|
||||
|
@ -72,16 +71,15 @@ export default function VodNav ({ vod, safeDate }: IVodNavProps) {
|
|||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
)}
|
||||
{vod.attributes.announceUrl && (
|
||||
<div className='level-item'>
|
||||
<Link
|
||||
target="_blank"
|
||||
href={vod.attributes.announceUrl}
|
||||
className="button is-small mb-1"
|
||||
className="button is-small"
|
||||
>
|
||||
<span className="mr-2"><FontAwesomeIcon icon={faTwitter} className="fab fa-x-twitter" /></span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||
<span className="mr-2"><FontAwesomeIcon icon={faXTwitter} className="fab fa-x-twitter" /></span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Link from 'next/link';
|
|||
import { getVtuberBySlug } from '../lib/vtubers'
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faPatreon, faTwitter, faYoutube, faTwitch, faTiktok } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faPatreon, faXTwitter, faYoutube, faTwitch, faTiktok } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faLink } from '@fortawesome/free-solid-svg-icons';
|
||||
import { projektMelodyEpoch } from '@/lib/constants';
|
||||
import LinkableHeading from '@/components/linkable-heading';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import VodsList from '@/components/vods-list'
|
||||
import { IVodsResponse } from '@/lib/vods'
|
||||
import Pager from '@/components/pager'
|
||||
import { getVods } from '@/lib/vods'
|
||||
import VodsList from '@/components/vods-list';
|
||||
import { IVodsResponse } from '@/lib/vods';
|
||||
import Pager from '@/components/pager';
|
||||
import { getVods } from '@/lib/vods';
|
||||
|
||||
interface IPageParams {
|
||||
params: {
|
||||
|
@ -11,7 +11,7 @@ interface IPageParams {
|
|||
}
|
||||
|
||||
export default async function Page({ params }: IPageParams) {
|
||||
const vods: IVodsResponse = await getVods(1, 24)
|
||||
const vods: IVodsResponse = await getVods(1, 24);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -11,24 +11,24 @@ import NotificationCenter from './components/notification-center';
|
|||
|
||||
|
||||
|
||||
// export const metadata: Metadata = {
|
||||
// title: 'Futureporn.net',
|
||||
// description: "The Galaxy's Best VTuber Hentai Site",
|
||||
// other: {
|
||||
// RATING: 'RTA-5042-1996-1400-1577-RTA'
|
||||
// },
|
||||
// twitter: {
|
||||
// site: '@futureporn_net',
|
||||
// creator: '@cj_clippy'
|
||||
// },
|
||||
// alternates: {
|
||||
// types: {
|
||||
// 'application/atom+xml': '/feed/feed.xml',
|
||||
// 'application/rss+xml': '/feed/rss.xml',
|
||||
// 'application/json': '/feed/feed.json'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
export const metadata: Metadata = {
|
||||
title: 'Futureporn.net',
|
||||
description: "The Galaxy's Best VTuber Hentai Site",
|
||||
other: {
|
||||
RATING: 'RTA-5042-1996-1400-1577-RTA'
|
||||
},
|
||||
twitter: {
|
||||
site: '@futureporn_net',
|
||||
creator: '@cj_clippy'
|
||||
},
|
||||
alternates: {
|
||||
types: {
|
||||
'application/atom+xml': '/feed/feed.xml',
|
||||
'application/rss+xml': '/feed/rss.xml',
|
||||
'application/json': '/feed/feed.json'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
import { strapiUrl, siteUrl } from './constants'
|
||||
import { getSafeDate } from './dates'
|
||||
import { IVtuber } from './vtubers'
|
||||
import qs from 'qs'
|
||||
import { strapiUrl, siteUrl } from './constants';
|
||||
import { getSafeDate } from './dates';
|
||||
import { IVodsResponse } from './vods';
|
||||
import { IVtuber, IVtuberResponse } from './vtubers';
|
||||
import qs from 'qs';
|
||||
|
||||
|
||||
export interface IStream {
|
||||
|
@ -10,16 +11,21 @@ export interface IStream {
|
|||
attributes: {
|
||||
date: string;
|
||||
archiveStatus: 'good' | 'issue' | 'missing';
|
||||
vods: IVodsResponse;
|
||||
cuid: string;
|
||||
vtuber: IVtuberResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IStreamsResponse {
|
||||
data: IStream[];
|
||||
pagination: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
pageCount: number;
|
||||
total: number;
|
||||
meta: {
|
||||
pagination: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
pageCount: number;
|
||||
total: number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +37,28 @@ const fetchStreamsOptions = {
|
|||
}
|
||||
|
||||
|
||||
export async function getStreamByCuid(cuid: string): Promise<IStream> {
|
||||
const query = qs.stringify({
|
||||
filters: {
|
||||
cuid: {
|
||||
$eq: cuid
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
limit: 1
|
||||
},
|
||||
populate: {
|
||||
vtuber: {
|
||||
fields: ['slug', 'displayName', 'image', 'imageBlur'],
|
||||
},
|
||||
vods: '*'
|
||||
}
|
||||
});
|
||||
const res = await fetch(`${strapiUrl}/api/streams?${query}`);
|
||||
const json = await res.json();
|
||||
return json.data[0];
|
||||
}
|
||||
|
||||
export function getUrl(stream: IStream, slug: string, date: string): string {
|
||||
return `${siteUrl}/vt/${slug}/stream/${getSafeDate(date)}`
|
||||
}
|
||||
|
@ -227,7 +255,7 @@ export async function getAllStreamsForVtuber(vtuberId: number, page: number = 1,
|
|||
return allStreams;
|
||||
}
|
||||
|
||||
export async function getStreamsForVtuber(vtuberId: number, page: number = 1, pageSize: number = 25, sortDesc = true): Promise<IStream> {
|
||||
export async function getStreamsForVtuber(vtuberId: number, page: number = 1, pageSize: number = 25, sortDesc = true): Promise<IStreamsResponse> {
|
||||
const query = qs.stringify(
|
||||
{
|
||||
populate: {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { notFound } from "next/navigation";
|
|||
export default async function Page() {
|
||||
const vods = await getVods(1, 9, true);
|
||||
const vtubers = await getVtubers();
|
||||
if (!vtubers) notFound()
|
||||
if (!vtubers) notFound();
|
||||
|
||||
// return (
|
||||
// <pre>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className='section'>
|
||||
<h2 className='title is-2'>404 Not Found</h2>
|
||||
<p>Could not find that stream.</p>
|
||||
|
||||
<Link href="/s">Return to streams list</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import StreamPage from '@/components/stream-page';
|
||||
import { getStreamByCuid } from '@/lib/streams';
|
||||
|
||||
|
||||
interface IPageParams {
|
||||
params: {
|
||||
cuid: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default async function Page ({ params: { cuid } }: IPageParams) {
|
||||
const stream = await getStreamByCuid(cuid);
|
||||
return (
|
||||
<>
|
||||
<StreamPage stream={stream} />
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,74 +1,70 @@
|
|||
|
||||
// import { getVtuberBySlug } from '@/lib/vtubers';
|
||||
// import { getAllStreamsForVtuber } from '@/lib/streams';
|
||||
// import NotFound from '../not-found';
|
||||
// import { DataRecord } from 'cal-heatmap/src/options/Options'
|
||||
// import { Cal } from '@/components/cal';
|
||||
import { getVtuberBySlug } from '@/lib/vtubers';
|
||||
import { getAllStreamsForVtuber } from '@/lib/streams';
|
||||
import NotFound from '../not-found';
|
||||
import { DataRecord } from 'cal-heatmap/src/options/Options';
|
||||
import { Cal } from '@/components/cal';
|
||||
|
||||
// interface IPageProps {
|
||||
// params: {
|
||||
// slug: string;
|
||||
// };
|
||||
// }
|
||||
interface IPageProps {
|
||||
params: {
|
||||
slug: string;
|
||||
};
|
||||
}
|
||||
|
||||
// function getArchiveStatusValue(archiveStatus: string): number {
|
||||
// if (archiveStatus === 'good') return 2;
|
||||
// if (archiveStatus === 'issue') return 1;
|
||||
// else return 0 // missing
|
||||
// }
|
||||
function getArchiveStatusValue(archiveStatus: string): number {
|
||||
if (archiveStatus === 'good') return 2;
|
||||
if (archiveStatus === 'issue') return 1;
|
||||
else return 0 // missing
|
||||
}
|
||||
|
||||
// function sortDataRecordsByDate(records: DataRecord[]) {
|
||||
// return records.sort((a, b) => {
|
||||
// if (typeof a.date === 'string' && typeof b.date === 'string') {
|
||||
// return a.date.localeCompare(b.date);
|
||||
// } else {
|
||||
// // Handle comparison when date is not a string (e.g., when it's a number)
|
||||
// // For instance, you might want to convert numbers to strings or use a different comparison logic.
|
||||
// // Example assuming number to string conversion:
|
||||
// return String(a.date).localeCompare(String(b.date));
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
function sortDataRecordsByDate(records: DataRecord[]) {
|
||||
return records.sort((a, b) => {
|
||||
if (typeof a.date === 'string' && typeof b.date === 'string') {
|
||||
return a.date.localeCompare(b.date);
|
||||
} else {
|
||||
// Handle comparison when date is not a string (e.g., when it's a number)
|
||||
// For instance, you might want to convert numbers to strings or use a different comparison logic.
|
||||
// Example assuming number to string conversion:
|
||||
return String(a.date).localeCompare(String(b.date));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// export default async function Page({ params: { slug } }: IPageProps) {
|
||||
// const vtuber = await getVtuberBySlug(slug);
|
||||
// if (!vtuber) return <NotFound></NotFound>
|
||||
// const streams = await getAllStreamsForVtuber(vtuber.id);
|
||||
// const streamsByYear: { [year: string]: DataRecord[] } = {};
|
||||
// streams.forEach((stream) => {
|
||||
// const date = new Date(stream.attributes.date);
|
||||
// const year = date.getFullYear();
|
||||
// if (!streamsByYear[year]) {
|
||||
// streamsByYear[year] = [];
|
||||
// }
|
||||
// streamsByYear[year].push({
|
||||
// date: new Date(stream.attributes.date).toISOString(),
|
||||
// value: stream.attributes.archiveStatus,
|
||||
// });
|
||||
// });
|
||||
// // Sort the data records within each year's array
|
||||
// for (const year in streamsByYear) {
|
||||
// streamsByYear[year] = sortDataRecordsByDate(streamsByYear[year]);
|
||||
// }
|
||||
export default async function Page({ params: { slug } }: IPageProps) {
|
||||
const vtuber = await getVtuberBySlug(slug);
|
||||
if (!vtuber) return <NotFound></NotFound>
|
||||
const streams = await getAllStreamsForVtuber(vtuber.id);
|
||||
const streamsByYear: { [year: string]: DataRecord[] } = {};
|
||||
streams.forEach((stream) => {
|
||||
const date = new Date(stream.attributes.date);
|
||||
const year = date.getFullYear();
|
||||
if (!streamsByYear[year]) {
|
||||
streamsByYear[year] = [];
|
||||
}
|
||||
streamsByYear[year].push({
|
||||
date: new Date(stream.attributes.date).toISOString(),
|
||||
value: stream.attributes.archiveStatus,
|
||||
});
|
||||
});
|
||||
// Sort the data records within each year's array
|
||||
for (const year in streamsByYear) {
|
||||
streamsByYear[year] = sortDataRecordsByDate(streamsByYear[year]);
|
||||
}
|
||||
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// {Object.keys(streamsByYear).map((year) => {
|
||||
// return (
|
||||
// <div key={year} className='section'>
|
||||
// <h2 className='title'>{year}</h2>
|
||||
// {/* <pre><code>{JSON.stringify(streamsByYear[year], null, 2)}</code></pre> */}
|
||||
// <Cal slug={slug} data={streamsByYear[year]} />
|
||||
// </div>
|
||||
// )
|
||||
// })}
|
||||
return (
|
||||
<div>
|
||||
{Object.keys(streamsByYear).map((year) => {
|
||||
return (
|
||||
<div key={year} className='section'>
|
||||
<h2 className='title'>{year}</h2>
|
||||
{/* <pre><code>{JSON.stringify(streamsByYear[year], null, 2)}</code></pre> */}
|
||||
<Cal slug={slug} data={streamsByYear[year]} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
export default function Page() {
|
||||
return <p></p>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import VodsList from '@/components/vods-list';
|
||||
import StreamsList from '@/components/streams-list';
|
||||
import Link from 'next/link';
|
||||
import { getVtuberBySlug } from '@/lib/vtubers'
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faExternalLinkAlt, faBagShopping } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faFacebook, faInstagram, faPatreon, faYoutube, faTwitch, faTiktok, faTwitter, faReddit, faDiscord } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faFacebook, faInstagram, faPatreon, faYoutube, faTwitch, faTiktok, faXTwitter, faReddit, faDiscord } from "@fortawesome/free-brands-svg-icons";
|
||||
import Image from 'next/image'
|
||||
import {
|
||||
FanslyIcon,
|
||||
|
@ -18,6 +19,7 @@ import { getVodsForVtuber } from '@/lib/vods';
|
|||
import { notFound } from 'next/navigation';
|
||||
|
||||
|
||||
|
||||
export default async function Page({ params }: { params: { slug: string } }) {
|
||||
const toySampleCount = 15
|
||||
const vtuber = await getVtuberBySlug(params.slug);
|
||||
|
@ -79,7 +81,7 @@ export default async function Page({ params }: { params: { slug: string } }) {
|
|||
{vtuber.attributes.twitter && (
|
||||
<div className="column is-3 is-narrow">
|
||||
<Link target="_blank" href={vtuber.attributes.twitter} className="subtitle is-5">
|
||||
<span className="mr-2"><FontAwesomeIcon icon={faTwitter} className="fab fa-x-twitter" /></span><span className="mr-2">Twitter</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||
<span className="mr-2"><FontAwesomeIcon icon={faXTwitter} className="fab fa-x-twitter" /></span><span className="mr-2">Twitter</span><span><FontAwesomeIcon icon={faExternalLinkAlt} className="fas fa-external-link-alt" /></span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
@ -206,9 +208,15 @@ export default async function Page({ params }: { params: { slug: string } }) {
|
|||
</h2>
|
||||
|
||||
<VodsList vtuber={vtuber} vods={vods.data} page={1} pageSize={9} />
|
||||
<Link className='button' href={`/vt/${vtuber.attributes.slug}/vods/1`}>See all {vtuber.attributes.displayName} vods</Link>
|
||||
<Link className='button mb-5' href={`/vt/${vtuber.attributes.slug}/vods/1`}>See all {vtuber.attributes.displayName} vods</Link>
|
||||
{/* <Pager getPagePath={getPaginatedUrl} slug={vtuber.slug} page={parseInt(1, 10)} pageSize={vods.pagination.pageSize} /> */}
|
||||
|
||||
<h2 id="streams" className='title is-3'>
|
||||
<Link href="#streams">Streams</Link>
|
||||
</h2>
|
||||
<StreamsList vtuber={vtuber} />
|
||||
<Link className='button mb-5' href={`/vt/${vtuber.attributes.slug}/streams/1`}>See all {vtuber.attributes.displayName} streams</Link>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
import StreamsList, { StreamsListHeading } from '@/components/streams-list'
|
||||
import { getVtuberBySlug } from '@/lib/vtubers'
|
||||
import { getStreamsForVtuber } from '@/lib/streams'
|
||||
import Pager from '@/components/pager'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
interface IPageParams {
|
||||
params: {
|
||||
slug: string;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page({ params }: IPageParams) {
|
||||
const vtuber = await getVtuberBySlug(params.slug);
|
||||
if (!vtuber) return <p>vtuber {params.slug} not found</p>
|
||||
const streams = await getStreamsForVtuber(vtuber.id, 1, 24);
|
||||
if (!streams) return <p>streams not found</p>;
|
||||
return (
|
||||
<>
|
||||
<StreamsListHeading slug={vtuber.attributes.slug} displayName={vtuber.attributes.displayName}></StreamsListHeading>
|
||||
<StreamsList vtuber={vtuber} streams={streams.data} page={1} pageSize={24} />
|
||||
<Pager baseUrl={`/vt/${params.slug}/stream`} page={1} pageCount={streams.meta.pagination.pageCount} />
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
$cell-height : 10px;
|
||||
$cell-width : 10px;
|
||||
$cell-margin:2px;
|
||||
$cell-weekdays-width: 30px;
|
||||
|
||||
html {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#container {
|
||||
height: 514px;
|
||||
width: 930px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
margin: 20px;
|
||||
margin-bottom: 60px;
|
||||
|
||||
.timeline-months {
|
||||
display: flex;
|
||||
padding-left: $cell-weekdays-width;
|
||||
|
||||
&-month {
|
||||
width: $cell-width;
|
||||
margin: $cell-margin;
|
||||
border: 1px solid transparent;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.Jan~.Jan,
|
||||
.Feb~.Feb,
|
||||
.Mar~.Mar,
|
||||
.Apr~.Apr,
|
||||
.May~.May,
|
||||
.Jun~.Jun,
|
||||
.Jul~.Jul,
|
||||
.Aug~.Aug,
|
||||
.Sep~.Sep,
|
||||
.Oct~.Oct,
|
||||
.Nov~.Nov,
|
||||
.Dec~.Dec {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
display: flex;
|
||||
|
||||
.timeline-weekdays {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
width: $cell-weekdays-width;
|
||||
|
||||
&-weekday {
|
||||
font-size: 10px;
|
||||
height: $cell-height;
|
||||
border: 1px solid transparent;
|
||||
margin: $cell-margin;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-cells {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
height: #{(10 + 4) * 8}px;
|
||||
|
||||
&-cell {
|
||||
height: $cell-height;
|
||||
width: $cell-width;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin: $cell-margin;
|
||||
border-radius: 2px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,4 +11,10 @@
|
|||
|
||||
.isTiny {
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.grade {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 8rem;
|
||||
font-weight: bolder;
|
||||
}
|
|
@ -24,13 +24,16 @@
|
|||
"@types/react-dom": "^18.2.17",
|
||||
"bulma": "^0.9.4",
|
||||
"bulma-prefers-dark": "0.1.0-beta.1",
|
||||
"cal-heatmap": "^4.2.3",
|
||||
"cid": "github:multiformats/cid",
|
||||
"date-fns": "^2.30.0",
|
||||
"date-fns-tz": "^2.0.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"feed": "^4.2.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hls.js": "^1.4.13",
|
||||
"lodash": "^4.17.21",
|
||||
"lunarphase-js": "^2.0.1",
|
||||
"next": "14.0.4-canary.49",
|
||||
"next-goatcounter": "^1.0.3",
|
||||
"next-react-svg": "^1.2.0",
|
||||
|
|
349
pnpm-lock.yaml
349
pnpm-lock.yaml
|
@ -50,6 +50,9 @@ dependencies:
|
|||
bulma-prefers-dark:
|
||||
specifier: 0.1.0-beta.1
|
||||
version: 0.1.0-beta.1
|
||||
cal-heatmap:
|
||||
specifier: ^4.2.3
|
||||
version: 4.2.3
|
||||
cid:
|
||||
specifier: github:multiformats/cid
|
||||
version: github.com/multiformats/cid/e5b6a3636d05234bc34bef873926c706afc1bd89
|
||||
|
@ -59,6 +62,9 @@ dependencies:
|
|||
date-fns-tz:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(date-fns@2.30.0)
|
||||
dayjs:
|
||||
specifier: ^1.11.10
|
||||
version: 1.11.10
|
||||
feed:
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
|
@ -71,6 +77,9 @@ dependencies:
|
|||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
lunarphase-js:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
next:
|
||||
specifier: 14.0.4-canary.49
|
||||
version: 14.0.4-canary.49(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5)
|
||||
|
@ -423,6 +432,19 @@ packages:
|
|||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/@observablehq/plot@0.6.13:
|
||||
resolution: {integrity: sha512-ebQS4ENodOy+O3WUjhqv9jNPZENAZRQMIdO3ziOlAKfUzSf69+gaFAqqc04SGrQA6JwJjPYnbfeN3YIpNsCF/A==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3: 7.8.5
|
||||
interval-tree-1d: 1.0.4
|
||||
isoformat: 0.2.1
|
||||
dev: false
|
||||
|
||||
/@popperjs/core@2.11.8:
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
dev: false
|
||||
|
||||
/@react-hookz/deep-equal@1.0.4:
|
||||
resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==}
|
||||
dev: false
|
||||
|
@ -753,6 +775,10 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/binary-search-bounds@2.0.5:
|
||||
resolution: {integrity: sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==}
|
||||
dev: false
|
||||
|
||||
/bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
dependencies:
|
||||
|
@ -800,6 +826,20 @@ packages:
|
|||
streamsearch: 1.1.0
|
||||
dev: false
|
||||
|
||||
/cal-heatmap@4.2.3:
|
||||
resolution: {integrity: sha512-NOkBb85xDjgGfP3/PPeRHp/54ookjjspzCmkPEK+amnals9xY2I3rxgnzPNYjbw5/r2xWLfFofea/VCVRQc2SA==}
|
||||
dependencies:
|
||||
'@observablehq/plot': 0.6.13
|
||||
'@popperjs/core': 2.11.8
|
||||
d3-color: 3.1.0
|
||||
d3-fetch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
dayjs: 1.11.10
|
||||
eventemitter3: 5.0.1
|
||||
lodash-es: 4.17.21
|
||||
dev: false
|
||||
|
||||
/call-bind@1.0.5:
|
||||
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
|
||||
dependencies:
|
||||
|
@ -885,6 +925,11 @@ packages:
|
|||
color-string: 1.9.1
|
||||
dev: false
|
||||
|
||||
/commander@7.2.0:
|
||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||
engines: {node: '>= 10'}
|
||||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
@ -924,6 +969,254 @@ packages:
|
|||
resolution: {integrity: sha512-N++UzR1COFip4DrtZkS99VDCcQvVCJmbqI7qaxZtHWKdAv/RUYIOnmJbP/HHk2un0PjvtFNKHfNLiFsd9BmnMQ==}
|
||||
dev: false
|
||||
|
||||
/d3-array@3.2.4:
|
||||
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
internmap: 2.0.3
|
||||
dev: false
|
||||
|
||||
/d3-axis@3.0.0:
|
||||
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-brush@3.0.0:
|
||||
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
dev: false
|
||||
|
||||
/d3-chord@3.0.1:
|
||||
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-path: 3.1.0
|
||||
dev: false
|
||||
|
||||
/d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-contour@4.0.2:
|
||||
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
dev: false
|
||||
|
||||
/d3-delaunay@6.0.4:
|
||||
resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
delaunator: 5.0.0
|
||||
dev: false
|
||||
|
||||
/d3-dispatch@3.0.1:
|
||||
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-drag@3.0.0:
|
||||
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
dev: false
|
||||
|
||||
/d3-dsv@3.0.1:
|
||||
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
commander: 7.2.0
|
||||
iconv-lite: 0.6.3
|
||||
rw: 1.3.3
|
||||
dev: false
|
||||
|
||||
/d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-fetch@3.0.1:
|
||||
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dsv: 3.0.1
|
||||
dev: false
|
||||
|
||||
/d3-force@3.0.0:
|
||||
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-quadtree: 3.0.1
|
||||
d3-timer: 3.0.1
|
||||
dev: false
|
||||
|
||||
/d3-format@3.1.0:
|
||||
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-geo@3.1.0:
|
||||
resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
dev: false
|
||||
|
||||
/d3-hierarchy@3.1.2:
|
||||
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
dev: false
|
||||
|
||||
/d3-path@3.1.0:
|
||||
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-polygon@3.0.1:
|
||||
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-quadtree@3.0.1:
|
||||
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-random@3.0.1:
|
||||
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-scale-chromatic@3.0.0:
|
||||
resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-interpolate: 3.0.1
|
||||
dev: false
|
||||
|
||||
/d3-scale@4.0.2:
|
||||
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
d3-format: 3.1.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-time: 3.1.0
|
||||
d3-time-format: 4.1.0
|
||||
dev: false
|
||||
|
||||
/d3-selection@3.0.0:
|
||||
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-shape@3.2.0:
|
||||
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-path: 3.1.0
|
||||
dev: false
|
||||
|
||||
/d3-time-format@4.1.0:
|
||||
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-time: 3.1.0
|
||||
dev: false
|
||||
|
||||
/d3-time@3.1.0:
|
||||
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
dev: false
|
||||
|
||||
/d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/d3-transition@3.0.1(d3-selection@3.0.0):
|
||||
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
d3-selection: 2 - 3
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-dispatch: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-timer: 3.0.1
|
||||
dev: false
|
||||
|
||||
/d3-zoom@3.0.0:
|
||||
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
dev: false
|
||||
|
||||
/d3@7.8.5:
|
||||
resolution: {integrity: sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
d3-axis: 3.0.0
|
||||
d3-brush: 3.0.0
|
||||
d3-chord: 3.0.1
|
||||
d3-color: 3.1.0
|
||||
d3-contour: 4.0.2
|
||||
d3-delaunay: 6.0.4
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-dsv: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-fetch: 3.0.1
|
||||
d3-force: 3.0.0
|
||||
d3-format: 3.1.0
|
||||
d3-geo: 3.1.0
|
||||
d3-hierarchy: 3.1.2
|
||||
d3-interpolate: 3.0.1
|
||||
d3-path: 3.1.0
|
||||
d3-polygon: 3.0.1
|
||||
d3-quadtree: 3.0.1
|
||||
d3-random: 3.0.1
|
||||
d3-scale: 4.0.2
|
||||
d3-scale-chromatic: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
d3-shape: 3.2.0
|
||||
d3-time: 3.1.0
|
||||
d3-time-format: 4.1.0
|
||||
d3-timer: 3.0.1
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
d3-zoom: 3.0.0
|
||||
dev: false
|
||||
|
||||
/damerau-levenshtein@1.0.8:
|
||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||
dev: true
|
||||
|
@ -943,6 +1236,10 @@ packages:
|
|||
'@babel/runtime': 7.23.6
|
||||
dev: false
|
||||
|
||||
/dayjs@1.11.10:
|
||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||
dev: false
|
||||
|
||||
/debug@3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
|
@ -1004,6 +1301,12 @@ packages:
|
|||
object-keys: 1.1.1
|
||||
dev: true
|
||||
|
||||
/delaunator@5.0.0:
|
||||
resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==}
|
||||
dependencies:
|
||||
robust-predicates: 3.0.2
|
||||
dev: false
|
||||
|
||||
/dequal@2.0.3:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -1432,6 +1735,10 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
dev: false
|
||||
|
||||
/expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -1709,6 +2016,13 @@ packages:
|
|||
resolution: {integrity: sha512-7QGXXS0u/vu0mQqNPRBKR31ru4BLVabVSOnGaXoQZhMRNbfCNTPNJk9ToC0pzvRUfLK/71QjhQO2wdHrgbKeKg==}
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: false
|
||||
|
@ -1758,6 +2072,17 @@ packages:
|
|||
side-channel: 1.0.4
|
||||
dev: true
|
||||
|
||||
/internmap@2.0.3:
|
||||
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/interval-tree-1d@1.0.4:
|
||||
resolution: {integrity: sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==}
|
||||
dependencies:
|
||||
binary-search-bounds: 2.0.5
|
||||
dev: false
|
||||
|
||||
/is-array-buffer@3.0.2:
|
||||
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
|
||||
dependencies:
|
||||
|
@ -1933,6 +2258,10 @@ packages:
|
|||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
||||
/isoformat@0.2.1:
|
||||
resolution: {integrity: sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==}
|
||||
dev: false
|
||||
|
||||
/iterator.prototype@1.1.2:
|
||||
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
|
||||
dependencies:
|
||||
|
@ -2045,6 +2374,10 @@ packages:
|
|||
p-locate: 5.0.0
|
||||
dev: true
|
||||
|
||||
/lodash-es@4.17.21:
|
||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||
dev: false
|
||||
|
||||
/lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: true
|
||||
|
@ -2065,6 +2398,10 @@ packages:
|
|||
dependencies:
|
||||
yallist: 4.0.0
|
||||
|
||||
/lunarphase-js@2.0.1:
|
||||
resolution: {integrity: sha512-QcCF6UxtifeSCDjbMT7FsczG4lHnWp1WRUpBy3IS5cG8pxtypk0VW/gw7xz+2vWYoaiVrt4NADZ+NR+ly4GvPg==}
|
||||
dev: false
|
||||
|
||||
/media-chrome@1.7.0:
|
||||
resolution: {integrity: sha512-2xut3GBOePwzgYGFc9+4ktWYApuL2JnMshgiw7Tgngp5tZKZugdSUGyImAgwrKM8NGE7NIcaFT584axh9dxoJw==}
|
||||
dev: false
|
||||
|
@ -2631,12 +2968,20 @@ packages:
|
|||
glob: 7.2.3
|
||||
dev: true
|
||||
|
||||
/robust-predicates@3.0.2:
|
||||
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
|
||||
dev: false
|
||||
|
||||
/run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
dev: true
|
||||
|
||||
/rw@1.3.3:
|
||||
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
|
||||
dev: false
|
||||
|
||||
/rx@4.1.0:
|
||||
resolution: {integrity: sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==}
|
||||
dev: false
|
||||
|
@ -2663,6 +3008,10 @@ packages:
|
|||
is-regex: 1.1.4
|
||||
dev: true
|
||||
|
||||
/safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
dev: false
|
||||
|
||||
/sass@1.69.5:
|
||||
resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
|
Loading…
Reference in New Issue