mirror of
https://github.com/uetchy/namae.git
synced 2025-08-20 18:08:11 +09:00
feat(web): typescript
This commit is contained in:
@@ -16,7 +16,7 @@ const COLORS = {
|
||||
error: '#ff388b',
|
||||
}
|
||||
|
||||
export function Card({ title, children }) {
|
||||
export const Card: React.FC<{ title: string }> = ({ title, children }) => {
|
||||
return (
|
||||
<CardContainer>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
@@ -36,7 +36,11 @@ export function Card({ title, children }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function Repeater({ items = [], moreItems = [], children }) {
|
||||
export const Repeater: React.FC<{
|
||||
items: string[]
|
||||
moreItems?: string[]
|
||||
children: (name: string) => React.ReactNode
|
||||
}> = ({ items = [], moreItems = [], children }) => {
|
||||
const [revealAlternatives, setRevealAlternatives] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -66,7 +70,23 @@ export function Repeater({ items = [], moreItems = [], children }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function DedicatedAvailability({
|
||||
interface Response {
|
||||
error?: string
|
||||
availability: boolean
|
||||
}
|
||||
|
||||
export const DedicatedAvailability: React.FC<{
|
||||
name: string
|
||||
query?: string
|
||||
message?: string
|
||||
messageIfTaken?: string
|
||||
service: string
|
||||
link: string
|
||||
linkIfTaken?: string
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
icon: React.ReactNode
|
||||
}> = ({
|
||||
name,
|
||||
query = undefined,
|
||||
message = '',
|
||||
@@ -77,10 +97,10 @@ export function DedicatedAvailability({
|
||||
prefix = '',
|
||||
suffix = '',
|
||||
icon,
|
||||
}) {
|
||||
}) => {
|
||||
const response = useFetch(
|
||||
`/availability/${service}/${encodeURIComponent(query || name)}`
|
||||
)
|
||||
) as Response
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`${service}: ${response.error}`)
|
||||
@@ -99,7 +119,17 @@ export function DedicatedAvailability({
|
||||
)
|
||||
}
|
||||
|
||||
export function ExistentialAvailability({
|
||||
export const ExistentialAvailability: React.FC<{
|
||||
name: string
|
||||
target: string
|
||||
message?: string
|
||||
messageIfTaken?: string
|
||||
link: string
|
||||
linkIfTaken?: string
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
icon: React.ReactNode
|
||||
}> = ({
|
||||
name,
|
||||
message = '',
|
||||
messageIfTaken = undefined,
|
||||
@@ -109,8 +139,8 @@ export function ExistentialAvailability({
|
||||
prefix = '',
|
||||
suffix = '',
|
||||
icon,
|
||||
}) {
|
||||
const response = useFetch(target, null, { metadata: true })
|
||||
}) => {
|
||||
const response = useFetch(target, undefined, { metadata: true })
|
||||
|
||||
if (response.status !== 404 && response.status !== 200) {
|
||||
throw new Error(`${name}: ${response.status}`)
|
||||
@@ -131,7 +161,15 @@ export function ExistentialAvailability({
|
||||
)
|
||||
}
|
||||
|
||||
export const Result = ({
|
||||
export const Result: React.FC<{
|
||||
title: string
|
||||
message?: string
|
||||
link?: string
|
||||
icon: React.ReactNode
|
||||
color?: string
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
}> = ({
|
||||
title,
|
||||
message = '',
|
||||
link,
|
||||
@@ -170,13 +208,16 @@ export const Result = ({
|
||||
)
|
||||
}
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
class ErrorBoundary extends React.Component<
|
||||
{},
|
||||
{ hasError: boolean; message: string }
|
||||
> {
|
||||
constructor(props: {}) {
|
||||
super(props)
|
||||
this.state = { hasError: false }
|
||||
this.state = { hasError: false, message: '' }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { hasError: true, message: error.message }
|
||||
}
|
||||
|
||||
@@ -204,7 +245,7 @@ class ErrorBoundary extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const CellError = ({ children }) => (
|
||||
const CellError: React.FC = ({ children }) => (
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
@@ -7,8 +7,10 @@ import { TiArrowSync } from 'react-icons/ti'
|
||||
import { capitalize } from '../util/text'
|
||||
import { mobile } from '../util/css'
|
||||
|
||||
type Modifier = (word: string) => string
|
||||
|
||||
const maximumCount = 3
|
||||
const modifiers = [
|
||||
const modifiers: Modifier[] = [
|
||||
(word) => `${capitalize(word)}ify`,
|
||||
(word) => `lib${lower(word)}`,
|
||||
(word) => `Omni${capitalize(word)}`,
|
||||
@@ -43,11 +45,11 @@ const modifiers = [
|
||||
(word) => `${capitalize(word)}`,
|
||||
]
|
||||
|
||||
function lower(word) {
|
||||
function lower(word: string) {
|
||||
return word.toLowerCase()
|
||||
}
|
||||
|
||||
function shuffleArray(array) {
|
||||
function shuffleArray(array: any[]) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1))
|
||||
const temp = array[i]
|
||||
@@ -57,15 +59,15 @@ function shuffleArray(array) {
|
||||
return array
|
||||
}
|
||||
|
||||
function sampleFromArray(array, maximum) {
|
||||
function sampleFromArray(array: any[], maximum: number) {
|
||||
return shuffleArray(array).slice(0, maximum)
|
||||
}
|
||||
|
||||
function modifyWord(word) {
|
||||
function modifyWord(word: string) {
|
||||
return modifiers[Math.floor(Math.random() * modifiers.length)](word)
|
||||
}
|
||||
|
||||
function fillArray(array, filler, maximum) {
|
||||
function fillArray(array: any[], filler: string, maximum: number) {
|
||||
const deficit = maximum - array.length
|
||||
if (deficit > 0) {
|
||||
array = [...array, ...Array(deficit).fill(filler)]
|
||||
@@ -73,33 +75,37 @@ function fillArray(array, filler, maximum) {
|
||||
return array
|
||||
}
|
||||
|
||||
async function findSynonyms(word) {
|
||||
async function findSynonyms(word: string) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&dt=ss&ie=UTF-8&oe=UTF-8&dj=1&q=${encodeURIComponent(
|
||||
word
|
||||
)}`
|
||||
)
|
||||
const json = await response.json()
|
||||
const synonyms = [
|
||||
...new Set(
|
||||
const json: {
|
||||
synsets: Array<{ entry: Array<{ synonym: string[] }> }>
|
||||
} = await response.json()
|
||||
const synonyms = Array.from(
|
||||
new Set<string>(
|
||||
json.synsets.reduce(
|
||||
(sum, synset) =>
|
||||
(sum = [...sum, ...synset.entry.map((e) => e.synonym[0])]),
|
||||
[]
|
||||
(sum, synset) => [...sum, ...synset.entry.map((e) => e.synonym[0])],
|
||||
[] as string[]
|
||||
)
|
||||
),
|
||||
].filter((word) => !word.match(/[\s-]/))
|
||||
)
|
||||
).filter((word) => !word.match(/[\s-]/))
|
||||
return synonyms
|
||||
} catch (err) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default function Suggestion({ query, onSubmit }) {
|
||||
const Suggestion: React.FC<{
|
||||
query: string
|
||||
onSubmit: (name: string) => void
|
||||
}> = ({ query, onSubmit }) => {
|
||||
const { t } = useTranslation()
|
||||
const synonymRef = useRef([])
|
||||
const [bestWords, setBestWords] = useState([])
|
||||
const synonymRef = useRef<string[]>([])
|
||||
const [bestWords, setBestWords] = useState<string[]>([])
|
||||
|
||||
function shuffle() {
|
||||
const best = fillArray(
|
||||
@@ -110,7 +116,7 @@ export default function Suggestion({ query, onSubmit }) {
|
||||
setBestWords(best)
|
||||
}
|
||||
|
||||
function applyQuery(name) {
|
||||
function applyQuery(name: string) {
|
||||
onSubmit(name)
|
||||
}
|
||||
|
||||
@@ -148,6 +154,8 @@ export default function Suggestion({ query, onSubmit }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default Suggestion
|
||||
|
||||
const Container = styled.div`
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
@@ -5,12 +5,14 @@ import { FaAppStore, FaInfoCircle } from 'react-icons/fa'
|
||||
|
||||
import { Card, Result } from '../Cards'
|
||||
|
||||
function Search({ query }) {
|
||||
const Search: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const term = encodeURIComponent(query)
|
||||
const response = useFetch(
|
||||
`/availability/appstore/${term}?country=${t('countryCode')}`
|
||||
)
|
||||
) as {
|
||||
result: Array<{ name: string; viewURL: string; price: number; id: string }>
|
||||
}
|
||||
const apps = response.result
|
||||
|
||||
return (
|
||||
@@ -32,7 +34,7 @@ function Search({ query }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function AppStoreCard({ query }) {
|
||||
const AppStoreCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@@ -41,3 +43,5 @@ export default function AppStoreCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppStoreCard
|
@@ -4,7 +4,7 @@ import { DiRust } from 'react-icons/di'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function CratesioCard({ query }) {
|
||||
const CratesioCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -26,3 +26,5 @@ export default function CratesioCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default CratesioCard
|
@@ -4,7 +4,7 @@ import { FaMapSigns } from 'react-icons/fa'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function DomainCard({ query }) {
|
||||
const DomainCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -33,3 +33,5 @@ export default function DomainCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default DomainCard
|
@@ -4,7 +4,7 @@ import { FaGithub } from 'react-icons/fa'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function GithubCard({ query }) {
|
||||
const GithubCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -36,3 +36,5 @@ export default function GithubCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default GithubCard
|
@@ -5,13 +5,21 @@ import { FaGithub, FaInfoCircle } from 'react-icons/fa'
|
||||
|
||||
import { Card, Result } from '../Cards'
|
||||
|
||||
function Search({ query }) {
|
||||
const Search: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const searchQuery = encodeURIComponent(`${query} in:name`)
|
||||
const limit = 10
|
||||
const response = useFetch(
|
||||
`https://api.github.com/search/repositories?q=${searchQuery}&per_page=${limit}`
|
||||
)
|
||||
) as {
|
||||
items: Array<{
|
||||
full_name: string
|
||||
description: string
|
||||
stargazers_count: number
|
||||
html_url: string
|
||||
id: string
|
||||
}>
|
||||
}
|
||||
const repos = response.items
|
||||
|
||||
return (
|
||||
@@ -35,7 +43,7 @@ function Search({ query }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function GithubSearchCard({ query }) {
|
||||
const GithubSearchCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@@ -44,3 +52,5 @@ export default function GithubSearchCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default GithubSearchCard
|
@@ -4,7 +4,7 @@ import { DiHeroku } from 'react-icons/di'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function HerokuCard({ query }) {
|
||||
const HerokuCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -26,3 +26,5 @@ export default function HerokuCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default HerokuCard
|
@@ -4,7 +4,7 @@ import { IoIosBeer } from 'react-icons/io'
|
||||
|
||||
import { Card, Repeater, ExistentialAvailability } from '../Cards'
|
||||
|
||||
export default function HomebrewCard({ query }) {
|
||||
const HomebrewCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -40,3 +40,5 @@ export default function HomebrewCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomebrewCard
|
@@ -4,7 +4,7 @@ import { FaJsSquare } from 'react-icons/fa'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function JsOrgCard({ query }) {
|
||||
const JsOrgCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -28,3 +28,5 @@ export default function JsOrgCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default JsOrgCard
|
@@ -5,7 +5,7 @@ import { DiDebian } from 'react-icons/di'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function LinuxCard({ query }) {
|
||||
const LinuxCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -34,3 +34,5 @@ export default function LinuxCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default LinuxCard
|
@@ -4,7 +4,7 @@ import { NowIcon } from '../Icons'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function NowCard({ query }) {
|
||||
const NowCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -26,3 +26,5 @@ export default function NowCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default NowCard
|
@@ -4,7 +4,7 @@ import { FaNpm } from 'react-icons/fa'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function NpmCard({ query }) {
|
||||
const NpmCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -42,3 +42,5 @@ export default function NpmCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default NpmCard
|
@@ -5,10 +5,12 @@ import { FaBuilding, FaInfoCircle } from 'react-icons/fa'
|
||||
|
||||
import { Card, Result } from '../Cards'
|
||||
|
||||
function Search({ query }) {
|
||||
const Search: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const term = encodeURIComponent(query)
|
||||
const response = useFetch(`/availability/nta/${term}`)
|
||||
const response = useFetch(`/availability/nta/${term}`) as {
|
||||
result: Array<{ name: string; phoneticName: string }>
|
||||
}
|
||||
const apps = response.result
|
||||
|
||||
return (
|
||||
@@ -29,7 +31,7 @@ function Search({ query }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default function NtaCard({ query }) {
|
||||
const NtaCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@@ -38,3 +40,5 @@ export default function NtaCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default NtaCard
|
@@ -5,7 +5,7 @@ import { FaPython } from 'react-icons/fa'
|
||||
import { capitalize } from '../../util/text'
|
||||
import { Card, DedicatedAvailability, Repeater } from '../Cards'
|
||||
|
||||
export default function PypiCard({ query }) {
|
||||
const PypiCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const names = [query]
|
||||
@@ -30,3 +30,5 @@ export default function PypiCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default PypiCard
|
@@ -4,7 +4,7 @@ import { FaGem } from 'react-icons/fa'
|
||||
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function RubyGemsCard({ query }) {
|
||||
const RubyGemsCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const names = [query]
|
||||
@@ -29,3 +29,5 @@ export default function RubyGemsCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default RubyGemsCard
|
@@ -4,7 +4,7 @@ import { FaAws } from 'react-icons/fa'
|
||||
|
||||
import { Card, DedicatedAvailability, Repeater } from '../Cards'
|
||||
|
||||
export default function S3Card({ query }) {
|
||||
const S3Card: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -30,3 +30,5 @@ export default function S3Card({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default S3Card
|
@@ -4,7 +4,7 @@ import { FaSlack } from 'react-icons/fa'
|
||||
|
||||
import { Card, DedicatedAvailability, Repeater } from '../Cards'
|
||||
|
||||
export default function SlackCard({ query }) {
|
||||
const SlackCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
|
||||
@@ -29,3 +29,5 @@ export default function SlackCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default SlackCard
|
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
import { SpectrumIcon } from '../Icons'
|
||||
|
||||
export default function SpectrumCard({ query }) {
|
||||
const SpectrumCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const names = [query]
|
||||
|
||||
@@ -27,3 +27,5 @@ export default function SpectrumCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default SpectrumCard
|
@@ -5,7 +5,7 @@ import { FaTwitter } from 'react-icons/fa'
|
||||
import { capitalize } from '../../util/text'
|
||||
import { Card, Repeater, DedicatedAvailability } from '../Cards'
|
||||
|
||||
export default function TwitterCard({ query }) {
|
||||
const TwitterCard: React.FC<{ query: string }> = ({ query }) => {
|
||||
const { t } = useTranslation()
|
||||
const lowerCase = query.toLowerCase()
|
||||
const capitalCase = capitalize(query)
|
||||
@@ -38,3 +38,5 @@ export default function TwitterCard({ query }) {
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default TwitterCard
|
Reference in New Issue
Block a user