1
0
mirror of https://github.com/uetchy/namae.git synced 2025-10-15 07:32:19 +09:00

dev: initial attempt

This commit is contained in:
2022-06-07 13:30:48 +09:00
parent 09f2755410
commit 4ff733e148
63 changed files with 1239 additions and 8090 deletions

40
pages/_app.tsx Normal file
View File

@@ -0,0 +1,40 @@
// https://nextjs.org/docs/advanced-features/custom-app
import { Global } from '@emotion/react';
import { StoreProvider } from 'easy-peasy';
import { appWithTranslation } from 'next-i18next';
import { useEffect } from 'react';
import { ToastContainer } from 'react-toastify';
import Footer from '../components/Footer';
import { globalStyle } from '../src/theme';
import { initSentry } from '../src/util/analytics';
import { initCrisp } from '../src/util/crisp';
import { useOpenSearch } from '../src/util/hooks';
import { FullScreenSuspense } from '../src/util/suspense';
import { store } from '../store';
import nextI18nConfig from '../next-i18next.config';
function MyApp({ Component, pageProps }) {
useEffect(() => {
// Client-side-only code
// TODO: https://docs.sentry.io/platforms/javascript/guides/nextjs/
initSentry();
initCrisp();
}, []);
const OpenSearch = useOpenSearch('/opensearch.xml');
return (
<StoreProvider store={store}>
<FullScreenSuspense>
<Global styles={globalStyle} />
<OpenSearch />
<Component {...pageProps} />;
<Footer />
</FullScreenSuspense>
<ToastContainer />
</StoreProvider>
);
}
export default appWithTranslation(MyApp, nextI18nConfig);

66
pages/_document.tsx Normal file
View File

@@ -0,0 +1,66 @@
// https://nextjs.org/docs/advanced-features/custom-document
import { Head, Html, Main, NextScript } from 'next/document';
import i18nextConfig from '../next-i18next.config';
export default function Document(props) {
const currentLocale =
props.__NEXT_DATA__.locale || i18nextConfig.i18n.defaultLocale;
return (
<Html lang={currentLocale}>
<Head>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="namae" />
<meta name="msapplication-TileColor" content="#5180fc" />
<meta name="theme-color" content="#632bec" />
<link rel="shortcut icon" href="/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5180fc" />
<link rel="manifest" href="/manifest.json" />
<meta property="og:title" content="namae — name new project" />
<meta
property="og:description"
content="Check availability of your new app name for major registries at once."
/>
<meta property="og:type" content="website" />
<meta property="og:image" content="https://namae.dev/social.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@uechz" />
<meta name="twitter:image" content="https://namae.dev/social.png" />
<link
href="https://fonts.googleapis.com/css?family=Montserrat:600&display=swap"
rel="stylesheet"
/>
<script
async
defer
data-website-id="a0bfb495-787b-4960-938d-c4a190aa7455"
src="https://analytics.uechi.io/umami.js"
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}

26
pages/index.tsx Normal file
View File

@@ -0,0 +1,26 @@
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import Form from '../components/Form';
import Welcome from '../components/Welcome';
import { Content, Header } from '../src/theme';
export default function App() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>namae {t('title')}</title>
</Helmet>
<Header>
<Form useSuggestion={false} />
</Header>
<Content>
<Welcome />
</Content>
</>
);
}

117
pages/s/[query].tsx Normal file
View File

@@ -0,0 +1,117 @@
import { useRouter } from 'next/router';
import Tooltip from 'rc-tooltip';
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { IoIosFlash, IoIosRocket } from 'react-icons/io';
import styled from '@emotion/styled';
import Cards from '../../components/cards';
import {
AvailableIcon,
COLORS as ResultColor,
ResultIcon,
ResultItem,
ResultName,
} from '../../components/cards/core';
import Form from '../../components/Form';
import { useStoreState } from '../../store';
import { Content, Header } from '../../src/theme';
import { mobile } from '../../src/util/css';
import { sanitize } from '../../src/util/text';
export default function Search() {
const router = useRouter();
const { query } = router.query;
const currentQuery = sanitize((query as string) ?? '');
const { t } = useTranslation();
return (
<>
<Helmet>
<title>Search for &quot;{currentQuery}&quot; namae</title>
</Helmet>
<Header>
<Form initialValue={currentQuery} />
</Header>
<Content>
<Legend>
<Stat />
<ResultItem color={ResultColor.available}>
<ResultIcon>
<IoIosRocket />
</ResultIcon>
<ResultName>{t('available')}</ResultName>
<AvailableIcon>
<IoIosFlash />
</AvailableIcon>
</ResultItem>
<ResultItem color={ResultColor.unavailable}>
<ResultIcon>
<IoIosRocket />
</ResultIcon>
<ResultName>{t('unavailable')}</ResultName>
</ResultItem>
</Legend>
<Cards query={currentQuery} />
</Content>
</>
);
}
function Stat() {
const totalCount = useStoreState((state) => state.stats.totalCount);
const availableCount = useStoreState((state) => state.stats.availableCount);
const { t } = useTranslation();
const uniqueness = availableCount !== 0 ? availableCount / totalCount : 0.0;
const uniquenessText = ((n) => {
if (n > 0.7 && n <= 1.0) {
return t('uniqueness.high');
} else if (n > 0.4 && n <= 0.7) {
return t('uniqueness.moderate');
} else {
return t('uniqueness.low');
}
})(uniqueness);
return (
<UniquenessIndicator>
<Tooltip
overlay={t('uniqueness.description')}
placement="top"
trigger={['hover']}
>
<span>
{uniquenessText} ({(uniqueness * 100).toFixed(1)} UNIQ)
</span>
</Tooltip>
</UniquenessIndicator>
);
}
export const Legend = styled.div`
margin-top: -100px;
padding: 100px 0 30px;
display: flex;
flex-direction: row;
justify-content: center;
user-select: none;
cursor: default;
background-color: #f6f6fa;
${mobile} {
flex-direction: column;
align-items: center;
margin-top: -80px;
padding: 70px 0 30px;
background-color: none;
}
> * {
margin: 0 10px 0;
}
`;
export const UniquenessIndicator = styled.div`
color: #7b7b7b;
`;