diff --git a/.env.example b/.env.example index 7247381..ef9af60 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ -REACT_APP_PUBLIC_URL=https://www.stiletto.live -REACT_APP_RESOURCES_URL= URL API ADDRESS FOR ICONS AND MAPS -REACT_APP_API_URL= URL API -REACT_APP_DISCORD_CLIENT_ID= DISCORD CLIENT ID -REACT_APP_GA_ID= Google Analytics ID +REACT_APP_PUBLIC_URL=https://www.stiletto.live +REACT_APP_RESOURCES_URL= URL API ADDRESS FOR ICONS AND MAPS +REACT_APP_API_URL= URL API +REACT_APP_DISCORD_CLIENT_ID= DISCORD CLIENT ID +REACT_APP_PLAUSIBLE_URL=PLAUSIBLE URL REACT_APP_VERSION=$npm_package_version \ No newline at end of file diff --git a/README.md b/README.md index 55ce791..4925d57 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The website uses several json files to read the game data so that it is always u - REACT_APP_RESOURCES_URL= URL API ADDRESS FOR ICONS AND MAPS - REACT_APP_API_URL= URL API - REACT_APP_DISCORD_CLIENT_ID= DISCORD CLIENT ID -- REACT_APP_GA_ID= Google Analytics ID +- REACT_APP_PLAUSIBLE_URL=PLAUSIBLE URL #### Generate maps diff --git a/package.json b/package.json index 803dcc6..8199180 100644 --- a/package.json +++ b/package.json @@ -1,80 +1,79 @@ -{ - "name": "stiletto-web", - "version": "4.6.2", - "license": "UNLICENSED", - "author": "Dm94Dani", - "description": "Web with different utilities for the game Last Oasis", - "private": true, - "bugs": { - "url": "https://github.com/dm94/stiletto-web/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/dm94/stiletto-web" - }, - "dependencies": { - "@giscus/react": "^2.2.6", - "axios": ">=1.3.1", - "beautiful-skill-tree": "^1.7.1", - "bootstrap": "^4.5.3", - "i18next": "^22.4.9", - "i18next-browser-languagedetector": "^7.0.1", - "i18next-http-backend": "^2.1.1", - "jquery": "^3.6.3", - "leaflet": "^1.7.1", - "leaflet-rastercoords": "^1.0.5", - "query-string": "^8.1.0", - "react": "^16.14.0", - "react-dom": "^16.14.0", - "react-ga4": "^2.0.0", - "react-helmet": "^6.1.0", - "react-i18next": "^12.1.4", - "react-leaflet": "^2.8.0", - "react-router-dom": "^5.2.0", - "react-scripts": "^5.0.1", - "react-tiny-virtual-list": "^2.2.0", - "workbox": "0.0.0", - "workbox-core": "^6.5.4", - "workbox-expiration": "^6.5.4", - "workbox-precaching": "^6.5.4", - "workbox-routing": "^6.5.4", - "workbox-strategies": "^6.5.4" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", - "cypress": "cypress open", - "cypress:test": "cypress run", - "lint": "eslint --fix --ext .js,.jsx ." - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@babel/preset-react": "^7.18.6", - "babel-eslint": "^10.1.0", - "cypress": "^12.5.1", - "eslint": "^8.33.0", - "eslint-plugin-cypress": "^2.12.1", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.3" - }, - "engines": { - "node": "18.x" - }, - "lint": "eslint --ext .js,.jsx ." -} +{ + "name": "stiletto-web", + "version": "4.6.3", + "license": "UNLICENSED", + "author": "Dm94Dani", + "description": "Web with different utilities for the game Last Oasis", + "private": true, + "bugs": { + "url": "https://github.com/dm94/stiletto-web/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/dm94/stiletto-web" + }, + "dependencies": { + "@giscus/react": "^2.2.6", + "axios": ">=1.3.1", + "beautiful-skill-tree": "^1.7.1", + "bootstrap": "^4.5.3", + "i18next": "^22.4.9", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", + "jquery": "^3.6.3", + "leaflet": "^1.7.1", + "leaflet-rastercoords": "^1.0.5", + "query-string": "^8.1.0", + "react": "^16.14.0", + "react-dom": "^16.14.0", + "react-helmet": "^6.1.0", + "react-i18next": "^12.1.4", + "react-leaflet": "^2.8.0", + "react-router-dom": "^5.2.0", + "react-scripts": "^5.0.1", + "react-tiny-virtual-list": "^2.2.0", + "workbox": "0.0.0", + "workbox-core": "^6.5.4", + "workbox-expiration": "^6.5.4", + "workbox-precaching": "^6.5.4", + "workbox-routing": "^6.5.4", + "workbox-strategies": "^6.5.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject", + "cypress": "cypress open", + "cypress:test": "cypress run", + "lint": "eslint --fix --ext .js,.jsx ." + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/preset-react": "^7.18.6", + "babel-eslint": "^10.1.0", + "cypress": "^12.5.1", + "eslint": "^8.33.0", + "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-prettier": "^4.2.1", + "prettier": "^2.8.3" + }, + "engines": { + "node": "18.x" + }, + "lint": "eslint --ext .js,.jsx ." +} diff --git a/src/components/ChangeLanguageModal.jsx b/src/components/ChangeLanguageModal.jsx index 78a58cb..f932e84 100644 --- a/src/components/ChangeLanguageModal.jsx +++ b/src/components/ChangeLanguageModal.jsx @@ -1,69 +1,68 @@ -import React, { Component } from "react"; -import { withTranslation } from "react-i18next"; -import { getStoredItem } from "../services"; - -class ChangeLanguageModal extends Component { - getLanguajes = () => { - const supportedLanguages = [ - { key: "en", name: "English" }, - { key: "es", name: "Spanish" }, - { key: "ru", name: "Russian" }, - { key: "fr", name: "French" }, - { key: "de", name: "German" }, - { key: "it", name: "Italian" }, - { key: "ja", name: "Japanese" }, - { key: "pl", name: "Polish" }, - { key: "zh", name: "Chinese Simplified" }, - { key: "pt", name: "Portuguese, Brazilian" }, - { key: "uk", name: "Ukrainian" }, - ]; - - return supportedLanguages.map((languaje) => { - const { t } = this.props; - return ( -
- {`${languaje.name} this.props.switchLanguage(languaje.key)} - /> -

{t(languaje.name)}

-
- ); - }); - }; - - render() { - const { t } = this.props; - return ( -
-
-
-
{t("Change language")}
-
-
{this.getLanguajes()}
-
-
-

v4.6.2

- -
-
-
-
- ); - } -} - -export default withTranslation()(ChangeLanguageModal); +import React, { Component } from "react"; +import { withTranslation } from "react-i18next"; +import { getStoredItem } from "../services"; + +class ChangeLanguageModal extends Component { + getLanguajes = () => { + const supportedLanguages = [ + { key: "en", name: "English" }, + { key: "es", name: "Spanish" }, + { key: "ru", name: "Russian" }, + { key: "fr", name: "French" }, + { key: "de", name: "German" }, + { key: "it", name: "Italian" }, + { key: "ja", name: "Japanese" }, + { key: "pl", name: "Polish" }, + { key: "zh", name: "Chinese Simplified" }, + { key: "pt", name: "Portuguese, Brazilian" }, + { key: "uk", name: "Ukrainian" }, + ]; + + return supportedLanguages.map((languaje) => { + const { t } = this.props; + return ( +
+ {`${languaje.name} this.props.switchLanguage(languaje.key)} + /> +

{t(languaje.name)}

+
+ ); + }); + }; + + render() { + const { t } = this.props; + return ( +
+
+
+
{t("Change language")}
+
+
{this.getLanguajes()}
+
+
+ +
+
+
+
+ ); + } +} + +export default withTranslation()(ChangeLanguageModal); diff --git a/src/components/Crafter/TotalMaterials.jsx b/src/components/Crafter/TotalMaterials.jsx index 90a791b..e8de286 100644 --- a/src/components/Crafter/TotalMaterials.jsx +++ b/src/components/Crafter/TotalMaterials.jsx @@ -1,205 +1,208 @@ -import React, { Component } from "react"; -import { withTranslation } from "react-i18next"; -import Axios from "axios"; -import ListIngredients from "./ListIngredients"; -import Icon from "../Icon"; -import { sendEvent } from "../../page-tracking"; -import { sendNotification } from "../../functions/broadcast"; - -class TotalMaterials extends Component { - constructor(props) { - super(props); - this.state = { - recipeToken: "", - }; - } - - addRecipe = () => { - sendEvent({ - category: "Share", - action: "Generate a recipe code", - }); - const items = this.props.selectedItems.map((item) => { - return { name: item.name, count: item.count }; - }); - const options = { - method: "post", - url: process.env.REACT_APP_API_URL + "/recipes", - params: { - items: JSON.stringify(items), - }, - }; - - Axios.request(options) - .then((response) => { - if (response.status === 503) { - sendNotification("Error connecting to database", "Error"); - } else if (response.status === 201) { - sendNotification("Sharing code has been generated", "Information"); - this.setState({ recipeToken: response.data.token }); - } - }) - .catch(() => { - sendNotification("Error when connecting to the API", "Error"); - }); - }; - - footerPart(t) { - if (this.state.recipeToken.length > 0) { - const url = - window.location.protocol.concat("//").concat(window.location.hostname) + - (window.location.port ? ":" + window.location.port : "") + - "/crafter?recipe=" + - this.state.recipeToken; - return ( -
- -
- - {this.shareButton(t)} -
-
- ); - } else { - return this.shareButton(t); - } - } - - shareButton(t) { - return ( - - ); - } - - itemsList(t) { - const http = window.location.protocol; - const slashes = http.concat("//"); - const host = slashes.concat(window.location.hostname); - const url = - host + - (window.location.port ? ":" + window.location.port : "") + - "/item/"; - - return this.props.selectedItems.map((item) => ( -
  • - {item.count}x{" "} - - {t(item.name, { ns: "items" })} - {" "} - - -
  • - )); - } - - copyMaterials = (t) => { - sendEvent({ - category: "Share", - action: "Copy materials", - }); - - let text = t("To make") + ":\n\n"; - - this.props.selectedItems.forEach( - (item) => - (text += item.count + "x " + t(item.name, { ns: "items" }) + " - ") - ); - - text += "\n\n" + t("You need the following materials") + ":\n\n"; - - const totalIngredients = []; - this.props.selectedItems.forEach((item) => { - if (item.crafting != null && item.crafting[0].ingredients != null) { - const output = - item.crafting[0].output != null ? item.crafting[0].output : 1; - item.crafting[0].ingredients.forEach((ingredient) => { - if ( - totalIngredients.find((ingre) => ingre.name === ingredient.name) - ) { - totalIngredients.forEach((ingre) => { - if (ingre.name === ingredient.name) { - ingre.count += (ingredient.count / output) * item.count; - } - }); - } else { - totalIngredients.push({ - name: ingredient.name, - count: (ingredient.count / output) * item.count, - ingredients: ingredient.ingredients, - }); - } - }); - } - }); - totalIngredients.forEach( - (ingredient) => - (text += "\t" + ingredient.count + "x " + t(ingredient.name) + "\n") - ); - - text += - "\n" + - t("List of all necessary materials by") + - " " + - window.location.origin; - - navigator.clipboard.writeText(text); - sendNotification("Items copied to the clipboard", "Information"); - }; - - render() { - const { t } = this.props; - return ( -
    -
    - -
    {t("Total materials")}
    -
    -
    - -
    - -
  • - {t("List of all necessary materials by")}{" "} - {window.location.hostname + - (window.location.port ? ":" + window.location.port : "")} -
  • -
    -
    -
    {this.footerPart(t)}
    -
    - ); - } -} -export default withTranslation()(TotalMaterials); +import React, { Component } from "react"; +import { withTranslation } from "react-i18next"; +import Axios from "axios"; +import ListIngredients from "./ListIngredients"; +import Icon from "../Icon"; +import { sendEvent } from "../../page-tracking"; +import { sendNotification } from "../../functions/broadcast"; + +class TotalMaterials extends Component { + constructor(props) { + super(props); + this.state = { + recipeToken: "", + }; + } + + addRecipe = () => { + sendEvent("share", { + props: { + action: "addRecipe", + }, + }); + + const items = this.props.selectedItems.map((item) => { + return { name: item.name, count: item.count }; + }); + const options = { + method: "post", + url: process.env.REACT_APP_API_URL + "/recipes", + params: { + items: JSON.stringify(items), + }, + }; + + Axios.request(options) + .then((response) => { + if (response.status === 503) { + sendNotification("Error connecting to database", "Error"); + } else if (response.status === 201) { + sendNotification("Sharing code has been generated", "Information"); + this.setState({ recipeToken: response.data.token }); + } + }) + .catch(() => { + sendNotification("Error when connecting to the API", "Error"); + }); + }; + + footerPart(t) { + if (this.state.recipeToken.length > 0) { + const url = + window.location.protocol.concat("//").concat(window.location.hostname) + + (window.location.port ? ":" + window.location.port : "") + + "/crafter?recipe=" + + this.state.recipeToken; + return ( +
    + +
    + + {this.shareButton(t)} +
    +
    + ); + } else { + return this.shareButton(t); + } + } + + shareButton(t) { + return ( + + ); + } + + itemsList(t) { + const http = window.location.protocol; + const slashes = http.concat("//"); + const host = slashes.concat(window.location.hostname); + const url = + host + + (window.location.port ? ":" + window.location.port : "") + + "/item/"; + + return this.props.selectedItems.map((item) => ( +
  • + {item.count}x{" "} + + {t(item.name, { ns: "items" })} + {" "} + - +
  • + )); + } + + copyMaterials = (t) => { + sendEvent("share", { + props: { + action: "copyMaterials", + }, + }); + + let text = t("To make") + ":\n\n"; + + this.props.selectedItems.forEach( + (item) => + (text += item.count + "x " + t(item.name, { ns: "items" }) + " - ") + ); + + text += "\n\n" + t("You need the following materials") + ":\n\n"; + + const totalIngredients = []; + this.props.selectedItems.forEach((item) => { + if (item.crafting != null && item.crafting[0].ingredients != null) { + const output = + item.crafting[0].output != null ? item.crafting[0].output : 1; + item.crafting[0].ingredients.forEach((ingredient) => { + if ( + totalIngredients.find((ingre) => ingre.name === ingredient.name) + ) { + totalIngredients.forEach((ingre) => { + if (ingre.name === ingredient.name) { + ingre.count += (ingredient.count / output) * item.count; + } + }); + } else { + totalIngredients.push({ + name: ingredient.name, + count: (ingredient.count / output) * item.count, + ingredients: ingredient.ingredients, + }); + } + }); + } + }); + totalIngredients.forEach( + (ingredient) => + (text += "\t" + ingredient.count + "x " + t(ingredient.name) + "\n") + ); + + text += + "\n" + + t("List of all necessary materials by") + + " " + + window.location.origin; + + navigator.clipboard.writeText(text); + sendNotification("Items copied to the clipboard", "Information"); + }; + + render() { + const { t } = this.props; + return ( +
    +
    + +
    {t("Total materials")}
    +
    +
    + +
    + +
  • + {t("List of all necessary materials by")}{" "} + {window.location.hostname + + (window.location.port ? ":" + window.location.port : "")} +
  • +
    +
    +
    {this.footerPart(t)}
    +
    + ); + } +} +export default withTranslation()(TotalMaterials); diff --git a/src/components/ModalMessage.jsx b/src/components/ModalMessage.jsx index 61d1056..283bc05 100644 --- a/src/components/ModalMessage.jsx +++ b/src/components/ModalMessage.jsx @@ -1,76 +1,75 @@ -import React, { Component } from "react"; -import { Redirect } from "react-router-dom"; -import { withTranslation } from "react-i18next"; -import { sendEvent } from "../page-tracking"; - -class ModalMessage extends Component { - state = { redirect: false }; - - redirectButton() { - return ( - - ); - } - - onlyOkButton() { - return ( - - ); - } - - render() { - const { t } = this.props; - if (this.props.message.text === "Error when connecting to the API") { - localStorage.removeItem("allItems"); - sessionStorage.removeItem("allItems"); - caches.keys().then((names) => { - for (const name of names) { - caches.delete(name); - } - }); - } - if (this.state.redirect) { - return ; - } - - console.log(t(this.props.message.text)); - sendEvent({ - category: "Modal", - action: this.props.message.isError ? "Error" : "Information", - label: this.props.message.text, - nonInteraction: true, - }); - - return ( -
    -
    -
    -
    - -
    -
    {t(this.props.message.text)}
    -
    - {this.props.message.redirectPage == null - ? this.onlyOkButton() - : this.redirectButton()} -
    -
    -
    -
    - ); - } -} - -export default withTranslation()(ModalMessage); +import React, { Component } from "react"; +import { Redirect } from "react-router-dom"; +import { withTranslation } from "react-i18next"; +import { sendEvent } from "../page-tracking"; + +class ModalMessage extends Component { + state = { redirect: false }; + + redirectButton() { + return ( + + ); + } + + onlyOkButton() { + return ( + + ); + } + + render() { + const { t } = this.props; + if (this.props.message.text === "Error when connecting to the API") { + localStorage.removeItem("allItems"); + sessionStorage.removeItem("allItems"); + caches.keys().then((names) => { + for (const name of names) { + caches.delete(name); + } + }); + } + if (this.state.redirect) { + return ; + } + + sendEvent("modal", { + props: { + action: this.props?.message?.isError ? "Error" : "Information", + label: this.props?.message?.text, + }, + }); + + return ( +
    +
    +
    +
    + +
    +
    {t(this.props.message.text)}
    +
    + {this.props.message.redirectPage == null + ? this.onlyOkButton() + : this.redirectButton()} +
    +
    +
    +
    + ); + } +} + +export default withTranslation()(ModalMessage); diff --git a/src/page-tracking.js b/src/page-tracking.js index db1f883..afe5685 100644 --- a/src/page-tracking.js +++ b/src/page-tracking.js @@ -1,31 +1,45 @@ -import { useEffect, useState } from "react"; -import { useLocation } from "react-router-dom"; -import ReactGA from "react-ga4"; - -export const usePageTracking = () => { - const location = useLocation(); - const [initialized, setInitialized] = useState(false); - - useEffect(() => { - if (localStorage.getItem("acceptscookies") && process.env.REACT_APP_GA_ID) { - ReactGA.initialize(process.env.REACT_APP_GA_ID); - } - setInitialized(true); - }, []); - - useEffect(() => { - if ( - localStorage.getItem("acceptscookies") && - process.env.REACT_APP_GA_ID && - initialized - ) { - ReactGA.send({ hitType: "pageview", page: location.pathname }); - } - }, [initialized, location]); -}; - -export const sendEvent = (data) => { - if (localStorage.getItem("acceptscookies") && process.env.REACT_APP_GA_ID) { - ReactGA.event(data); - } -}; +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +export const initPlausible = () => { + const PLAUSIBLE_URL = process.env.REACT_APP_PLAUSIBLE_URL; + + if (!PLAUSIBLE_URL || PLAUSIBLE_URL.length <= 0) { + return; + } + + const script = document.querySelector(`script[src*="${PLAUSIBLE_URL}"]`); + if (script) { + return; + } + + const element = document.createElement("script"); + element.src = PLAUSIBLE_URL; + element.defer = true; + element.setAttribute("data-domain", document.location.hostname); + document.head.appendChild(element); +}; + +export const usePageTracking = () => { + const location = useLocation(); + + useEffect(() => { + initPlausible(); + }, []); + + useEffect(() => { + initPlausible(); + }, [location]); +}; + +export const sendEvent = (data) => { + initPlausible(); + + window.plausible = + window.plausible || + function() { + (window.plausible.q = window.plausible.q || []).push(arguments); + }; + + window.plausible(...data); +}; diff --git a/src/pages/Wiki.jsx b/src/pages/Wiki.jsx index a4d4026..42211eb 100644 --- a/src/pages/Wiki.jsx +++ b/src/pages/Wiki.jsx @@ -1,215 +1,217 @@ -import React, { Component } from "react"; -import { withTranslation } from "react-i18next"; -import { Helmet } from "react-helmet"; -import queryString from "query-string"; -import { getItems } from "../services"; -import { sendEvent } from "../page-tracking"; -import Ingredient from "../components/Ingredient"; - -class Wiki extends Component { - constructor(props) { - super(props); - this.state = { - items: [], - searchText: "", - textSearched: "", - filteredItems: [], - error: "", - categories: [], - categoryFilter: "All", - }; - } - - componentDidMount() { - this.updateRecipes(); - let parsed = null; - if (this.props.location != null && this.props.location.search != null) { - parsed = queryString.parse(this.props.location.search); - } - if (parsed && parsed.s != null) { - this.setState({ searchText: parsed.s }, () => this.searchItems()); - } - } - - updateRecipes = async () => { - const items = await getItems(); - if (items != null) { - const allCategories = []; - - items.forEach((item) => { - if (item.category && !allCategories.includes(item.category)) { - allCategories.push(item.category); - } - }); - allCategories.sort(); - - this.setState({ items: items, categories: allCategories }); - } - }; - - searchItems = async () => { - const search = this.state.searchText; - sendEvent({ - category: "User", - action: "Wiki search", - label: search, - }); - if (this.state.items == null || this.state.items.length <= 0) { - await this.updateRecipes(); - } - const { t } = this.props; - let filteredItems = this.state.items; - - if (this.state.categoryFilter && this.state.categoryFilter !== "All") { - filteredItems = filteredItems.filter((item) => { - return item.category && item.category === this.state.categoryFilter; - }); - } - - filteredItems = filteredItems.filter((it) => { - return search.split(" ").every((internalItem) => { - return ( - t(it.name).toLowerCase().indexOf(internalItem.toLowerCase()) !== -1 - ); - }); - }); - this.setState({ - filteredItems: filteredItems, - textSearched: search, - }); - }; - - showItems = (t) => { - if (this.state.filteredItems != null) { - if (this.state.filteredItems.length > 0) { - return this.state.filteredItems.map((item) => ( -
    - -
    - )); - } else if (this.state.textSearched.length > 0) { - return ( -
    -
    -
    - {t("Nothing found")} -
    -
    -
    - ); - } - } - }; - - showCategories = (t) => { - if (this.state.categories != null && this.state.categories.length > 0) { - return this.state.categories.map((category) => ( - - )); - } - }; - - render() { - const { t } = this.props; - - return ( -
    - - Last Oasis Wiki - Stiletto - - - - - - -
    -
    -
    -
    -
    - - this.setState({ searchText: e.currentTarget.value }) - } - onKeyPress={(e) => { - const keyPress = e.key || e.keyCode; - if (keyPress === 13 || keyPress === "Enter") { - this.searchItems(); - } - }} - value={this.state.searchText} - /> -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    {this.showItems(t)}
    -
    -
    - ); - } -} - -export default withTranslation()(Wiki); +import React, { Component } from "react"; +import { withTranslation } from "react-i18next"; +import { Helmet } from "react-helmet"; +import queryString from "query-string"; +import { getItems } from "../services"; +import { sendEvent } from "../page-tracking"; +import Ingredient from "../components/Ingredient"; + +class Wiki extends Component { + constructor(props) { + super(props); + this.state = { + items: [], + searchText: "", + textSearched: "", + filteredItems: [], + error: "", + categories: [], + categoryFilter: "All", + }; + } + + componentDidMount() { + this.updateRecipes(); + let parsed = null; + if (this.props.location != null && this.props.location.search != null) { + parsed = queryString.parse(this.props.location.search); + } + if (parsed && parsed.s != null) { + this.setState({ searchText: parsed.s }, () => this.searchItems()); + } + } + + updateRecipes = async () => { + const items = await getItems(); + if (items != null) { + const allCategories = []; + + items.forEach((item) => { + if (item.category && !allCategories.includes(item.category)) { + allCategories.push(item.category); + } + }); + allCategories.sort(); + + this.setState({ items: items, categories: allCategories }); + } + }; + + searchItems = async () => { + const search = this.state.searchText; + + sendEvent("search", { + props: { + term: search, + }, + }); + + if (this.state.items == null || this.state.items.length <= 0) { + await this.updateRecipes(); + } + const { t } = this.props; + let filteredItems = this.state.items; + + if (this.state.categoryFilter && this.state.categoryFilter !== "All") { + filteredItems = filteredItems.filter((item) => { + return item.category && item.category === this.state.categoryFilter; + }); + } + + filteredItems = filteredItems.filter((it) => { + return search.split(" ").every((internalItem) => { + return ( + t(it.name).toLowerCase().indexOf(internalItem.toLowerCase()) !== -1 + ); + }); + }); + this.setState({ + filteredItems: filteredItems, + textSearched: search, + }); + }; + + showItems = (t) => { + if (this.state.filteredItems != null) { + if (this.state.filteredItems.length > 0) { + return this.state.filteredItems.map((item) => ( +
    + +
    + )); + } else if (this.state.textSearched.length > 0) { + return ( +
    +
    +
    + {t("Nothing found")} +
    +
    +
    + ); + } + } + }; + + showCategories = (t) => { + if (this.state.categories != null && this.state.categories.length > 0) { + return this.state.categories.map((category) => ( + + )); + } + }; + + render() { + const { t } = this.props; + + return ( +
    + + Last Oasis Wiki - Stiletto + + + + + + +
    +
    +
    +
    +
    + + this.setState({ searchText: e.currentTarget.value }) + } + onKeyPress={(e) => { + const keyPress = e.key || e.keyCode; + if (keyPress === 13 || keyPress === "Enter") { + this.searchItems(); + } + }} + value={this.state.searchText} + /> +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    {this.showItems(t)}
    +
    +
    + ); + } +} + +export default withTranslation()(Wiki);