From 3982fb5cc4823cffd0ebd7f4b037765e0b9113e0 Mon Sep 17 00:00:00 2001 From: Yasuaki Uechi Date: Sat, 3 Aug 2019 21:51:12 +0900 Subject: [PATCH] feat: smart suggestion --- web/package.json | 1 + web/public/locales/en/translation.json | 2 +- web/src/App.js | 3 +- web/src/components/Suggestion.js | 110 +++++++++++++++++++------ web/src/components/cards/GithubCard.js | 3 +- yarn.lock | 2 +- 6 files changed, 91 insertions(+), 30 deletions(-) diff --git a/web/package.json b/web/package.json index 89235ac..fc699f1 100644 --- a/web/package.json +++ b/web/package.json @@ -15,6 +15,7 @@ "i18next-browser-languagedetector": "^3.0.1", "i18next-xhr-backend": "^3.0.0", "isomorphic-unfetch": "^3.0.0", + "prop-types": "^15.7.2", "react": "^16.8.6", "react-dom": "^16.8.6", "react-ga": "^2.6.0", diff --git a/web/public/locales/en/translation.json b/web/public/locales/en/translation.json index 7ed6ad5..f5df2ac 100644 --- a/web/public/locales/en/translation.json +++ b/web/public/locales/en/translation.json @@ -15,5 +15,5 @@ "twitter": "Twitter", "slack": "Slack" }, - "try": "suggestion" + "try": "How about" } diff --git a/web/src/App.js b/web/src/App.js index d5e4950..a22758c 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -15,6 +15,7 @@ import PypiCard from './components/cards/PypiCard' import S3Card from './components/cards/S3Card' import CratesioCard from './components/cards/CratesioCard' import RubyGemsCard from './components/cards/RubyGemsCard' + import { EventReporter } from './components/Analytics' import Welcome from './components/Welcome' import Footer from './components/Footer' @@ -34,7 +35,7 @@ export default function App() { const queryGiven = query && query.length > 0 useEffect(() => { - const modifiedValue = inputValue.replace(/[\s@\+!#$%^&*()\[\]]/g, '') + const modifiedValue = inputValue.replace(/[\s@+!#$%^&*()[\]]/g, '') setQuery(modifiedValue) }, [inputValue, setQuery]) diff --git a/web/src/components/Suggestion.js b/web/src/components/Suggestion.js index cd0eaed..9973ece 100644 --- a/web/src/components/Suggestion.js +++ b/web/src/components/Suggestion.js @@ -1,26 +1,85 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import styled from 'styled-components' import { useTranslation } from 'react-i18next' +import fetch from 'isomorphic-unfetch' import { capitalize } from '../util/text' +function modifyWord(word) { + const modifiers = [ + (word) => `${capitalize(word)}ify`, + (word) => `lib${lower(word)}`, + (word) => `Omni${capitalize(word)}`, + (word) => `${capitalize(word)}Lab`, + (word) => `${capitalize(word)}Kit`, + (word) => `Open${capitalize(word)}`, + (word) => `${capitalize(word)}box`, + (word) => `Insta${lower(word)}`, + (word) => `${capitalize(word)}Hub`, + (word) => `Semantic ${capitalize(word)}`, + (word) => `Cloud${capitalize(word)}`, + (word) => `Deep${capitalize(word)}`, + (word) => `${capitalize(word)}gram`, + (word) => `${capitalize(word)}base`, + (word) => `${capitalize(word)}API`, + (word) => `${capitalize(word)}note`, + (word) => `In${capitalize(word)}`, + (word) => `Under${lower(word)}`, + (word) => `Uni${lower(word)}`, + (word) => `${capitalize(word)}mind`, + ] + return modifiers[Math.floor(Math.random() * modifiers.length)](word) +} + +function lower(word) { + return word.toLowerCase() +} + +async function findSynonyms(word, maximum = 10) { + 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 = json.synsets.reduce( + (sum, synset) => + (sum = [...sum, ...synset.entry.map((e) => e.synonym[0])]), + [] + ) + let bestWords = [ + ...new Set( + synonyms + .filter((word) => !word.match(/[\s-]/)) + .sort(() => Math.random() - 0.5) + .slice(0, maximum) + ), + ] + const deficit = maximum - bestWords.length + if (deficit > 0) { + bestWords = [...bestWords, ...Array(deficit).fill(word)] + } + return bestWords + } catch (err) { + return Array(maximum).fill(word) + } +} + export default function Suggestion({ query, onSubmit }) { const { t } = useTranslation() - const capital = capitalize(query) - const lower = query.toLowerCase() + const [synonyms, setSynonyms] = useState([]) - const suggestions = [ - `${lower}ify`, - `insta${lower}`, - `lib${lower}`, - `omni${lower}`, - `${capital}Lab`, - `${capital}Kit`, - `Open${capital}`, - `${capital}box`, - `${lower}hub`, - ] - .sort(() => Math.random() - 0.5) - .slice(0, 3) + useEffect(() => { + const fn = async () => { + if (query && query.length > 0) { + const synonyms = (await findSynonyms(query, 3)).map((synonym) => + modifyWord(synonym) + ) + setSynonyms(synonyms) + } + } + fn() + }, [query]) function applyQuery(name) { onSubmit(name) @@ -30,11 +89,12 @@ export default function Suggestion({ query, onSubmit }) { {t('try')} - {suggestions.map((name) => ( - applyQuery(name)}> - {name} - - ))} + {synonyms && + synonyms.map((name) => ( + applyQuery(name)}> + {name} + + ))} ) @@ -52,9 +112,6 @@ const Container = styled.div` const Title = styled.div` margin-top: 15px; - border: 1px solid black; - padding: 4px 12px; - border-radius: 20px; font-size: 0.6em; ` @@ -63,10 +120,13 @@ const Items = styled.div` margin-left: 8px; display: flex; flex-direction: row; + flex-wrap: wrap; + justify-content: center; ` const Item = styled.div` - margin-right: 8px; + margin-right: 10px; cursor: pointer; font-weight: bold; + font-family: monospace; ` diff --git a/web/src/components/cards/GithubCard.js b/web/src/components/cards/GithubCard.js index 893e40d..7dedda7 100644 --- a/web/src/components/cards/GithubCard.js +++ b/web/src/components/cards/GithubCard.js @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { FaGithub } from 'react-icons/fa' import { Card } from '../Cards' import { DedicatedAvailability } from '../Availability' -import { capitalize } from '../../util/text' export default function GithubCard({ name }) { const { t } = useTranslation() @@ -17,7 +16,7 @@ export default function GithubCard({ name }) { `${lowerCase}hq`, `${lowerCase}-team`, `${lowerCase}-org`, - `${capitalize(name)}-js`, + `${lowerCase}-js`, ]}> {(name) => (