tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

LocationSearch.jsx (3910B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import React, { useEffect, useRef, useState } from "react";
      6 import { useDispatch, useSelector } from "react-redux";
      7 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
      8 
      9 function LocationSearch({ outerClassName }) {
     10  // should be the location object from suggestedLocations
     11  const [selectedLocation, setSelectedLocation] = useState("");
     12  const suggestedLocations = useSelector(
     13    state => state.Weather.suggestedLocations
     14  );
     15  const locationSearchString = useSelector(
     16    state => state.Weather.locationSearchString
     17  );
     18 
     19  const [userInput, setUserInput] = useState(locationSearchString || "");
     20  const inputRef = useRef(null);
     21 
     22  const dispatch = useDispatch();
     23 
     24  useEffect(() => {
     25    if (selectedLocation) {
     26      dispatch(
     27        ac.AlsoToMain({
     28          type: at.WEATHER_LOCATION_DATA_UPDATE,
     29          data: {
     30            city: selectedLocation.localized_name,
     31            adminName: selectedLocation.administrative_area,
     32            country: selectedLocation.country,
     33          },
     34        })
     35      );
     36      dispatch(ac.SetPref("weather.query", selectedLocation.key));
     37      dispatch(
     38        ac.BroadcastToContent({
     39          type: at.WEATHER_SEARCH_ACTIVE,
     40          data: false,
     41        })
     42      );
     43    }
     44  }, [selectedLocation, dispatch]);
     45 
     46  // when component mounts, set focus to input
     47  useEffect(() => {
     48    inputRef?.current?.focus();
     49  }, [inputRef]);
     50 
     51  function handleChange(event) {
     52    const { value } = event.target;
     53    setUserInput(value);
     54 
     55    // if the user input contains less than three characters and suggestedLocations is not an empty array,
     56    // reset suggestedLocations to [] so there aren't incorrect items in the datalist
     57    if (value.length < 3 && suggestedLocations.length) {
     58      dispatch(
     59        ac.AlsoToMain({
     60          type: at.WEATHER_LOCATION_SUGGESTIONS_UPDATE,
     61          data: [],
     62        })
     63      );
     64    }
     65    // find match in suggestedLocation array
     66    const match = suggestedLocations?.find(({ key }) => key === value);
     67    if (match) {
     68      setSelectedLocation(match);
     69      setUserInput(
     70        `${match.localized_name}, ${match.administrative_area.localized_name}`
     71      );
     72    } else if (value.length >= 3 && !match) {
     73      dispatch(
     74        ac.AlsoToMain({
     75          type: at.WEATHER_LOCATION_SEARCH_UPDATE,
     76          data: value,
     77        })
     78      );
     79    }
     80  }
     81 
     82  function handleCloseSearch() {
     83    dispatch(
     84      ac.BroadcastToContent({
     85        type: at.WEATHER_SEARCH_ACTIVE,
     86        data: false,
     87      })
     88    );
     89    setUserInput("");
     90  }
     91 
     92  function handleKeyDown(e) {
     93    if (e.key === "Escape") {
     94      handleCloseSearch();
     95    }
     96  }
     97 
     98  return (
     99    <div className={`${outerClassName} location-search`}>
    100      <div className="location-input-wrapper">
    101        <div className="search-icon" />
    102        <input
    103          ref={inputRef}
    104          list="merino-location-list"
    105          type="text"
    106          data-l10n-id="newtab-weather-change-location-search-input-placeholder"
    107          onChange={handleChange}
    108          value={userInput}
    109          onKeyDown={handleKeyDown}
    110        />
    111        <moz-button
    112          class="close-icon"
    113          type="icon ghost"
    114          size="small"
    115          iconSrc="chrome://global/skin/icons/close.svg"
    116          onClick={handleCloseSearch}
    117        />
    118        <datalist id="merino-location-list">
    119          {(suggestedLocations || []).map(merinoLocation => (
    120            <option value={merinoLocation.key} key={merinoLocation.key}>
    121              {merinoLocation.localized_name},{" "}
    122              {merinoLocation.administrative_area.localized_name}
    123            </option>
    124          ))}
    125        </datalist>
    126      </div>
    127    </div>
    128  );
    129 }
    130 
    131 export { LocationSearch };