0
0
Fork 0
This commit is contained in:
Xeltica 2022-06-19 10:42:00 +09:00
parent 9168e362ad
commit ab58af8543
11 changed files with 246 additions and 51 deletions

View file

@ -39,6 +39,7 @@
"@types/react": "^17.0.19", "@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.1.8",
"@types/react-twemoji": "^0.4.0",
"@types/styled-components": "^5.1.13", "@types/styled-components": "^5.1.13",
"@types/uuid": "^8.0.0", "@types/uuid": "^8.0.0",
"axios": "^0.21.2", "axios": "^0.21.2",
@ -75,6 +76,7 @@
"react-modal-hook": "^3.0.0", "react-modal-hook": "^3.0.0",
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
"react-router-dom": "^5.2.1", "react-router-dom": "^5.2.1",
"react-twemoji": "^0.5.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rndstr": "^1.0.0", "rndstr": "^1.0.0",
"routing-controllers": "^0.9.0", "routing-controllers": "^0.9.0",

View file

@ -1,16 +1,14 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { NavLink } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import { NavigationMenu } from './components/NavigationMenu'; import { NavigationMenu } from './components/NavigationMenu';
import { IsMobileProp } from './misc/is-mobile-prop';
import { useGetMetaQuery, useGetSessionQuery } from './services/session'; import { useGetMetaQuery, useGetSessionQuery } from './services/session';
import { useSelector } from './store'; import { useSelector } from './store';
import { setDrawerShown } from './store/slices/screen'; import { setDrawerShown } from './store/slices/screen';
type IsMobileProp = { isMobile: boolean };
const Container = styled.div<IsMobileProp>` const Container = styled.div<IsMobileProp>`
padding: var(--margin); padding: var(--margin);
position: relative; position: relative;

View file

@ -1,5 +1,11 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
const Input = styled.input`
width: auto;
flex: 1;
`;
export const LoginForm: React.VFC = () => { export const LoginForm: React.VFC = () => {
const [host, setHost] = useState(''); const [host, setHost] = useState('');
@ -11,7 +17,7 @@ export const LoginForm: React.VFC = () => {
<strong>{t('instanceUrl')}</strong> <strong>{t('instanceUrl')}</strong>
</div> </div>
<div className="hgroup login-form"> <div className="hgroup login-form">
<input <Input
className="input-field" className="input-field"
type="text" type="text"
value={host} value={host}

View file

@ -22,38 +22,42 @@ export const NavigationMenu: React.VFC = () => {
<div className="menu"> <div className="menu">
<section> <section>
<NavLink className={navLinkClassName} to="/" exact onClick={onClickItem}> <NavLink className={navLinkClassName} to="/" exact onClick={onClickItem}>
<i className="icon fas fa-home"></i> <i className={`icon fas fa-${session ? 'home' : 'arrow-left'}`}></i>
{t('_sidebar.dashboard')} {t(session ? '_sidebar.dashboard' : '_sidebar.return')}
</NavLink> </NavLink>
</section> </section>
<section> {session && (
<h1>{t('_sidebar.tools')}</h1> <section>
<NavLink className={navLinkClassName} to="/apps/miss-hai" onClick={onClickItem}> <h1>{t('_sidebar.tools')}</h1>
<i className="icon fas fa-tower-broadcast"></i> <NavLink className={navLinkClassName} to="/apps/miss-hai" onClick={onClickItem}>
{t('_sidebar.missHaiAlert')} <i className="icon fas fa-tower-broadcast"></i>
</NavLink> {t('_sidebar.missHaiAlert')}
<NavLink className={navLinkClassName} to="/apps/avatar-cropper" onClick={onClickItem}> </NavLink>
<i className="icon fas fa-crop-simple"></i> <NavLink className={navLinkClassName} to="/apps/avatar-cropper" onClick={onClickItem}>
{t('_sidebar.cropper')} <i className="icon fas fa-crop-simple"></i>
</NavLink> {t('_sidebar.cropper')}
</section> </NavLink>
<section> </section>
{session && <h1>{session.username}@{session.host}</h1>} )}
{session && ( {session && (
<section>
<h1>{session.username}@{session.host}</h1>
<NavLink className={navLinkClassName} to="/account" onClick={onClickItem}> <NavLink className={navLinkClassName} to="/account" onClick={onClickItem}>
<i className="icon fas fa-circle-user"></i> <i className="icon fas fa-circle-user"></i>
{t('_sidebar.accounts')} {t('_sidebar.accounts')}
</NavLink> </NavLink>
)} <NavLink className={navLinkClassName} to="/settings" onClick={onClickItem}>
<NavLink className={navLinkClassName} to="/settings" onClick={onClickItem}> <i className="icon fas fa-gear"></i>
<i className="icon fas fa-gear"></i> {t('_sidebar.settings')}
{t('_sidebar.settings')} </NavLink>
</NavLink> {session.isAdmin && (
<NavLink className={navLinkClassName} to="/admin" onClick={onClickItem}> <NavLink className={navLinkClassName} to="/admin" onClick={onClickItem}>
<i className="icon fas fa-lock"></i> <i className="icon fas fa-lock"></i>
{t('_sidebar.admin')} {t('_sidebar.admin')}
</NavLink> </NavLink>
</section> )}
</section>
)}
</div> </div>
</> </>
); );

View file

@ -58,7 +58,8 @@
"accounts": "アカウント", "accounts": "アカウント",
"settings": "設定", "settings": "設定",
"admin": "管理画面", "admin": "管理画面",
"about": "Misskey Toolsについて" "about": "Misskey Toolsについて",
"return": "トップページに戻る"
}, },
"_welcomeMessage": { "_welcomeMessage": {
"pattern1": "ついついノートしすぎていませんか?", "pattern1": "ついついノートしすぎていませんか?",

View file

@ -0,0 +1,2 @@
export type IsMobileProp = { isMobile: boolean; };

View file

@ -0,0 +1,11 @@
import React from 'react';
import { useTitle } from '../../hooks/useTitle';
export const AnnouncementsPage: React.VFC = () => {
useTitle('announcements');
return (
<div className="fade">
</div>
);
};

View file

@ -1,28 +1,122 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { LoginForm } from '../components/LoginForm'; import { LoginForm } from '../components/LoginForm';
import { Header } from '../Header'; import styled from 'styled-components';
import { AnnouncementList } from '../components/AnnouncementList'; import { useSelector } from '../store';
import { IsMobileProp } from '../misc/is-mobile-prop';
import { IAnnouncement } from '../../common/types/announcement';
import { $get } from '../misc/api';
import Twemoji from 'react-twemoji';
const Hero = styled.div<IsMobileProp>`
display: flex;
position: relative;
background: linear-gradient(-135deg, rgb(1, 169, 46), rgb(134, 179, 0) 35%);
color: var(--white);
padding: ${f => f.isMobile ? '16px' : '60px 90px'};
overflow: hidden;
gap: var(--margin);
> .hero {
flex: 2;
min-width: 0;
position: relative;
z-index: 1000;
p {
${f => f.isMobile ? 'font-size: 1rem;' : ''}
}
}
> .announcements {
flex: 1;
min-width: 0;
max-height: 512px;
overflow: auto;
padding: var(--margin);
border-radius: var(--radius);
background: var(--black-50);
backdrop-filter: blur(4px) brightness(120%);
z-index: 1000;
@media screen and (max-width: 800px) {
display: none;
}
}
> .rects {
position: absolute;
display: grid;
right: 160px;
bottom: -120px;
width: 400px;
height: 400px;
gap: 8px;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
transform-origin: center center;
transform: rotate(45deg);
opacity: 0.5;
> .rect {
border: 2px solid var(--white);
border-radius: 24px;
box-shadow: 0 2px 4px var(--shadow-color);
}
}
`;
const FormWrapper = styled.div`
max-width: 500px;
color: var(--fg);
`;
export const IndexWelcomePage: React.VFC = () => { export const IndexWelcomePage: React.VFC = () => {
const [announcements, setAnnouncements] = useState<IAnnouncement[]>([]);
const {isMobile} = useSelector(state => state.screen);
const {t} = useTranslation(); const {t} = useTranslation();
const fetchAllAnnouncements = () => {
setAnnouncements([]);
$get<IAnnouncement[]>('announcements').then(announcements => {
setAnnouncements(announcements ?? []);
});
};
useEffect(() => {
fetchAllAnnouncements();
}, []);
return ( return (
<> <>
<Header> <Hero className="fluid shadow-2" isMobile={isMobile}>
<article className="mt-4"> <div className="hero">
<p>{t('description1')}</p> <h1 className="shadow-t">Misskey Tools</h1>
<p>{t('description2')}</p> <p className="shadow-t">{t('description1')}</p>
</article> <p className="shadow-t">{t('description2')}</p>
<LoginForm /> <FormWrapper className="bg-panel pa-2 mt-4 rounded shadow-2">
</Header> <LoginForm />
<article className="xarticle card"> </FormWrapper>
<div className="body">
<AnnouncementList />
</div> </div>
</article> <div className="announcements">
<hr /> <h2></h2>
<div className="menu large">
{announcements.map(a => (
<Link className="item fluid" key={a.id} to={`/announcements/${a.id}`}>
{a.title}
</Link>
))}
</div>
</div>
<div className="rects">
<div className="rect"></div>
<div className="rect"></div>
<div className="rect"></div>
<div className="rect"></div>
</div>
</Hero>
<Twemoji options={{className: 'twemoji'}}>
<div className="py-4 text-125 text-center">
👍&emsp;&emsp;😆&emsp;🎉&emsp;🍮
</div>
</Twemoji>
<article className="xarticle vstack pa-2"> <article className="xarticle vstack pa-2">
<header> <header>
<h2>{t('_welcome.title')}</h2> <h2>{t('_welcome.title')}</h2>
@ -30,7 +124,7 @@ export const IndexWelcomePage: React.VFC = () => {
</header> </header>
<div className="row"> <div className="row">
<article className="col-4 col-12-sm"> <article className="col-4 col-12-sm">
<h3><i className="fas fa-bullhone"/> {t('_welcome.misshaiAlertTitle')}</h3> <h3><i className="fas fa-bullhorn"/> {t('_welcome.misshaiAlertTitle')}</h3>
<p>{t('_welcome.misshaiAlertDescription')}</p> <p>{t('_welcome.misshaiAlertDescription')}</p>
</article> </article>
<article className="col-4 col-12-sm"> <article className="col-4 col-12-sm">
@ -38,10 +132,10 @@ export const IndexWelcomePage: React.VFC = () => {
<p>{t('_welcome.misshaiRankingDescription')}</p> <p>{t('_welcome.misshaiRankingDescription')}</p>
<Link to="/apps/miss-hai/ranking">{t('_missHai.showRanking')}</Link> <Link to="/apps/miss-hai/ranking">{t('_missHai.showRanking')}</Link>
</article> </article>
<div className="col-4 col-12-sm"> <article className="col-4 col-12-sm">
<h3><i className="fas fa-crop-simple"/> {t('catAdjuster')}</h3> <h3><i className="fas fa-crop-simple"/> {t('catAdjuster')}</h3>
<p>{t('_welcome.catAdjusterDescription')}</p> <p>{t('_welcome.catAdjusterDescription')}</p>
</div> </article>
</div> </div>
<article className="mt-5"> <article className="mt-5">
<h3>{t('_welcome.nextFeaturesTitle')}</h3> <h3>{t('_welcome.nextFeaturesTitle')}</h3>

View file

@ -42,7 +42,7 @@ export const SettingPage: React.VFC = () => {
onSelect(i) { onSelect(i) {
if (i === 0) { if (i === 0) {
localStorage.removeItem(LOCALSTORAGE_KEY_TOKEN); localStorage.removeItem(LOCALSTORAGE_KEY_TOKEN);
location.reload(); location.href = '/';
} }
}, },
})); }));
@ -72,7 +72,7 @@ export const SettingPage: React.VFC = () => {
message: t('_deactivate.success'), message: t('_deactivate.success'),
icon: 'info', icon: 'info',
onSelect() { onSelect() {
location.reload(); location.href = '/';
} }
})); }));
}).catch((e) => { }).catch((e) => {

View file

@ -144,3 +144,9 @@ small {
} }
} }
} }
.twemoji {
height: 1em;
width: 1em;
vertical-align: -0.1em;
}

View file

@ -622,6 +622,13 @@
"@types/history" "*" "@types/history" "*"
"@types/react" "*" "@types/react" "*"
"@types/react-twemoji@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@types/react-twemoji/-/react-twemoji-0.4.0.tgz#3f3ce96e273ec0aa4b4ddca2913951b168fd72ee"
integrity sha512-OVkDaNTg9WqqM2MBqL68FNPsn+5aabQIbL9KY+ofK/Q4ENOuaHOWsg/jRD9zQ+GX5L+7LC1Ztgr4iK0/qZd17w==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.19": "@types/react@*", "@types/react@^17.0.19":
version "17.0.27" version "17.0.27"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.27.tgz#6498ed9b3ad117e818deb5525fa1946c09f2e0e6" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.27.tgz#6498ed9b3ad117e818deb5525fa1946c09f2e0e6"
@ -2376,6 +2383,15 @@ fresh@0.5.2, fresh@~0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-extra@^8.0.1:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -2546,6 +2562,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
has-ansi@^2.0.0: has-ansi@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@ -3195,6 +3216,22 @@ json5@^2.1.2, json5@^2.1.3:
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922"
integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==
dependencies:
universalify "^0.1.2"
optionalDependencies:
graceful-fs "^4.1.6"
jstransformer@1.0.0: jstransformer@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3"
@ -3428,6 +3465,11 @@ lodash.clonedeep@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
lodash.merge@^4.6.2: lodash.merge@^4.6.2:
version "4.6.2" version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@ -4822,6 +4864,15 @@ react-router@5.2.1:
tiny-invariant "^1.0.2" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0" tiny-warning "^1.0.0"
react-twemoji@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/react-twemoji/-/react-twemoji-0.5.0.tgz#0565f8e427fc4c9ef3680977c4a88fbdef79f874"
integrity sha512-xz3NLWTFCfWOmPd559jcFX4f976ORIPpL9SwdBQO5BZwIYD1U1vpbY2E6k2vwPCVH78s2m1GbG5jpHKGUPZ+gw==
dependencies:
lodash.isequal "^4.5.0"
prop-types "^15.7.2"
twemoji "14.0.1"
react@^17.0.2: react@^17.0.2:
version "17.0.2" version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
@ -5762,6 +5813,21 @@ tsutils@^3.21.0:
dependencies: dependencies:
tslib "^1.8.1" tslib "^1.8.1"
twemoji-parser@14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-14.0.0.tgz#13dabcb6d3a261d9efbf58a1666b182033bf2b62"
integrity sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==
twemoji@14.0.1:
version "14.0.1"
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-14.0.1.tgz#0640887ef149403ae577081cbc2480a026e55ed6"
integrity sha512-eoqhea0sUhmC10iTacksyp1v9O4BP1jKmVqtK+Nztw40/dzawSHkXL3/xCpyh+mukmEvJ0Gw9VLvwZfQ9HKXDw==
dependencies:
fs-extra "^8.0.1"
jsonfile "^5.0.0"
twemoji-parser "14.0.0"
universalify "^0.1.2"
type-check@^0.4.0, type-check@~0.4.0: type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@ -5937,6 +6003,11 @@ unist-util-visit@^4.0.0:
unist-util-is "^5.0.0" unist-util-is "^5.0.0"
unist-util-visit-parents "^5.0.0" unist-util-visit-parents "^5.0.0"
universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
unpipe@1.0.0, unpipe@~1.0.0: unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"