mirror of
https://github.com/uetchy/namae.git
synced 2025-08-20 09:58:13 +09:00
feat: location based domain suggestion
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import fetch from 'isomorphic-unfetch';
|
||||
import fetch from 'cross-fetch';
|
||||
import { TiArrowSync } from 'react-icons/ti';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
|
@@ -7,9 +7,10 @@ import { Card, Result } from '../core';
|
||||
|
||||
const Search: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const term = encodeURIComponent(query);
|
||||
const response = useFetch(
|
||||
`/api/services/appstore/${term}?country=${t('countryCode')}`
|
||||
`/api/services/appstore/${encodeURIComponent(query)}?country=${t(
|
||||
'countryCode'
|
||||
)}`
|
||||
) as {
|
||||
result: Array<{ name: string; viewURL: string; price: number; id: string }>;
|
||||
};
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SiRust } from 'react-icons/si';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const CratesioCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query);
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,17 +1,35 @@
|
||||
import fetch from 'cross-fetch';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdDomain } from 'react-icons/md';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
import useSWR from 'swr';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { zones } from '../../../util/zones';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
export interface DomainrResponse {
|
||||
results: {
|
||||
domain: string;
|
||||
host: string;
|
||||
subdomain: string;
|
||||
zone: string;
|
||||
path: string;
|
||||
registerURL: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
function fetcher(url: string) {
|
||||
return fetch(url, {}).then((res) => res.json());
|
||||
}
|
||||
|
||||
const DomainCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
alphanumeric: false,
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const domainHackSuggestions = zones
|
||||
.map((zone) => new RegExp(`${zone}$`).exec(lowerCase.slice(1)))
|
||||
@@ -23,37 +41,54 @@ const DomainCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
lowerCase.substring(m.index + 1)
|
||||
);
|
||||
|
||||
const names = [
|
||||
`${lowerCase}.com`,
|
||||
`${lowerCase}.org`,
|
||||
`${lowerCase}.app`,
|
||||
`${lowerCase}.dev`,
|
||||
`${lowerCase}.io`,
|
||||
`${lowerCase}.sh`,
|
||||
...domainHackSuggestions,
|
||||
];
|
||||
const moreNames = [
|
||||
const { data } = useSWR<DomainrResponse>(
|
||||
`/api/list/domain/${encodeURIComponent(query)}`,
|
||||
fetcher
|
||||
);
|
||||
|
||||
const cctldSuggestions =
|
||||
data?.results
|
||||
?.filter((res) => res.subdomain !== '' && res.path === '')
|
||||
?.map((res) => res.domain) ?? [];
|
||||
|
||||
const names =
|
||||
// use Set() to eliminate dupes
|
||||
new Set([
|
||||
...['com', 'org', 'app', 'io'].map((tld) => lowerCase + '.' + tld),
|
||||
...domainHackSuggestions,
|
||||
...cctldSuggestions,
|
||||
]);
|
||||
|
||||
const moreNames = new Set([
|
||||
`${lowerCase}app.com`,
|
||||
`get${lowerCase}.com`,
|
||||
`${lowerCase}.co`,
|
||||
`${lowerCase}.tools`,
|
||||
`${lowerCase}.build`,
|
||||
`${lowerCase}.run`,
|
||||
`${lowerCase}.ai`,
|
||||
`${lowerCase}.design`,
|
||||
`${lowerCase}.directory`,
|
||||
`${lowerCase}.guru`,
|
||||
`${lowerCase}.ninja`,
|
||||
`${lowerCase}.net`,
|
||||
`${lowerCase}.info`,
|
||||
`${lowerCase}.biz`,
|
||||
`${lowerCase}.website`,
|
||||
`${lowerCase}.eu`,
|
||||
];
|
||||
...[
|
||||
'co',
|
||||
'dev',
|
||||
'sh',
|
||||
'cloud',
|
||||
'tools',
|
||||
'build',
|
||||
'run',
|
||||
'ai',
|
||||
'design',
|
||||
'directory',
|
||||
'guru',
|
||||
'ninja',
|
||||
'info',
|
||||
'biz',
|
||||
].map((tld) => lowerCase + '.' + tld),
|
||||
]);
|
||||
|
||||
for (const name of moreNames) {
|
||||
if (names.has(name)) {
|
||||
moreNames.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card title={t('providers.domains')}>
|
||||
<Repeater items={names} moreItems={moreNames}>
|
||||
<Repeater items={Array.from(names)} moreItems={Array.from(moreNames)}>
|
||||
{(name) => (
|
||||
<DedicatedAvailability
|
||||
name={name}
|
||||
|
@@ -1,15 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SiFirebase } from 'react-icons/si';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const FirebaseCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaGithub } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const GithubCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [query, `${lowerCase}-dev`, `${lowerCase}-org`];
|
||||
const names = [normalizedQuery, `${lowerCase}-dev`, `${lowerCase}-org`];
|
||||
const moreNames = [
|
||||
`${lowerCase}hq`,
|
||||
`${lowerCase}-team`,
|
||||
|
@@ -7,10 +7,12 @@ import { Card, Result } from '../core';
|
||||
|
||||
const Search: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const searchQuery = encodeURIComponent(`${query} in:name`);
|
||||
const searchQuery = `${query} in:name`;
|
||||
const limit = 10;
|
||||
const response = useFetch(
|
||||
`https://api.github.com/search/repositories?q=${searchQuery}&per_page=${limit}`
|
||||
`https://api.github.com/search/repositories?q=${encodeURIComponent(
|
||||
searchQuery
|
||||
)}&per_page=${limit}`
|
||||
) as {
|
||||
items: Array<{
|
||||
full_name: string;
|
||||
|
@@ -1,12 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaGitlab } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const GitLabCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DiHeroku } from 'react-icons/di';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const HerokuCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IoIosBeer } from 'react-icons/io';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, Repeater, ExistentialAvailability } from '../core';
|
||||
|
||||
const HomebrewCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query);
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaInstagram } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, Repeater, ExistentialAvailability } from '../core';
|
||||
|
||||
const InstagramCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query, { allowHyphens: false });
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [query];
|
||||
const names = [normalizedQuery];
|
||||
const moreNames = [`${lowerCase}app`, `${lowerCase}_hq`, `get.${lowerCase}`];
|
||||
|
||||
return (
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaJsSquare } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const JsOrgCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
const names = [lowerCase];
|
||||
|
||||
return (
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SiDebian, SiUbuntu } from 'react-icons/si';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const LinuxCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query);
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { NetlifyIcon } from '../../Icons';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const NetlifyCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiNpmjsFill, RiNpmjsLine } from 'react-icons/ri';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const NpmCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase, `${lowerCase}-js`];
|
||||
const moreNames = [`${lowerCase}js`];
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { OcamlIcon } from '../../Icons';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const OcamlCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const lowerCase = query.toLowerCase();
|
||||
const normalizedQuery = normalize(query, { allowUnderscore: false });
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -2,14 +2,15 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaPython } from 'react-icons/fa';
|
||||
|
||||
import { capitalize } from '../../../util/text';
|
||||
import { capitalize, normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const PypiCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const names = [query];
|
||||
const moreNames = [`Py${capitalize(query)}`];
|
||||
const normalizedQuery = normalize(query);
|
||||
const capitalCase = capitalize(normalizedQuery);
|
||||
const names = [normalizedQuery];
|
||||
const moreNames = [`Py${capitalCase}`];
|
||||
|
||||
return (
|
||||
<Card title={t('providers.pypi')}>
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SiRubygems } from 'react-icons/si';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const RubyGemsCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const names = [query];
|
||||
const moreNames = [`${query.toLowerCase()}-rb`];
|
||||
const normalizedQuery = normalize(query);
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
const names = [normalizedQuery];
|
||||
const moreNames = [`${lowerCase}-rb`];
|
||||
|
||||
return (
|
||||
<Card title={t('providers.rubygems')}>
|
||||
|
@@ -1,16 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaAws } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const S3Card: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -1,16 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaSlack } from 'react-icons/fa';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||
|
||||
const SlackCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, { allowUnderscore: false });
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
@@ -2,10 +2,12 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
import { SpectrumIcon } from '../../Icons';
|
||||
import { normalize } from '../../../util/text';
|
||||
|
||||
const SpectrumCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
const names = [query];
|
||||
const normalizedQuery = normalize(query);
|
||||
const names = [normalizedQuery];
|
||||
|
||||
return (
|
||||
<Card title={t('providers.spectrum')}>
|
||||
|
@@ -2,19 +2,17 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaTwitter } from 'react-icons/fa';
|
||||
|
||||
import { capitalize } from '../../../util/text';
|
||||
import { capitalize, normalize } from '../../../util/text';
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const TwitterCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/-/g, '_');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const capitalCase = capitalize(sanitizedQuery);
|
||||
const normalizedQuery = normalize(query, { allowHyphens: false });
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
const capitalCase = capitalize(normalizedQuery);
|
||||
|
||||
const names = [sanitizedQuery, `${capitalCase}App`, `${lowerCase}hq`];
|
||||
const names = [normalizedQuery, `${capitalCase}App`, `${lowerCase}hq`];
|
||||
const moreNames = [
|
||||
`hey${lowerCase}`,
|
||||
`${capitalCase}Team`,
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { normalize } from '../../../util/text';
|
||||
import { NowIcon } from '../../Icons';
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../core';
|
||||
|
||||
const VercelCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sanitizedQuery = query
|
||||
.replace(/[^0-9a-zA-Z_-]/g, '')
|
||||
.replace(/_/g, '-');
|
||||
const lowerCase = sanitizedQuery.toLowerCase();
|
||||
const normalizedQuery = normalize(query, {
|
||||
allowUnderscore: false,
|
||||
});
|
||||
const lowerCase = normalizedQuery.toLowerCase();
|
||||
|
||||
const names = [lowerCase];
|
||||
|
||||
|
Reference in New Issue
Block a user