diff --git a/src/App.css b/src/App.css index 20bba0e4..a674f063 100644 --- a/src/App.css +++ b/src/App.css @@ -2,6 +2,19 @@ body { margin: 0; } +#search { + width: 100%; + background: black; + padding: 5px; + text-align: center; +} + +#search input { + width: 100%; + border: none; + font-size: 40px; +} + body.overlay-open { overflow: hidden; } diff --git a/src/App.js b/src/App.js index 8b1f9d08..5e379cf8 100644 --- a/src/App.js +++ b/src/App.js @@ -9,8 +9,6 @@ import * as Utils from "./utils"; import * as Selectors from "./redux/selectors"; import { SKIN_WIDTH, SKIN_HEIGHT, SKIN_RATIO } from "./constants"; -const hashes = Object.keys(skins); - const OVERSCAN_ROWS_LEADING = 10; const OVERSCAN_ROWS_TRAILING = 4; @@ -70,6 +68,7 @@ class App extends React.Component { } render() { + const hashes = this.props.skinHashes; const columnCount = Math.floor(this.state.windowWidth / SKIN_WIDTH); const columnWidth = this.state.windowWidth / columnCount; // TODO: Consider flooring this to get things aligned to the pixel const rowHeight = columnWidth * SKIN_RATIO; @@ -128,6 +127,14 @@ class App extends React.Component { } return (
+
({ - selectedSkinHash: Selectors.getSelectedSkinHash(state) + selectedSkinHash: Selectors.getSelectedSkinHash(state), + searchQuery: Selectors.getSearchQuery(state), + skinHashes: Selectors.getMatchingSkinHashes(state) }); const mapDispatchToProps = dispatch => ({ // TODO: Extract to action creator setSelectedSkin(hash, position) { dispatch({ type: "SELECT_SKIN", hash, position }); + }, + setSearchQuery(query) { + dispatch({ type: "SET_SEARCH_QUERY", query }); } }); export default connect( diff --git a/src/redux/selectors.js b/src/redux/selectors.js index 31d20b2d..52bd288a 100644 --- a/src/redux/selectors.js +++ b/src/redux/selectors.js @@ -1,4 +1,5 @@ import * as Utils from "../utils"; +import skins from "../skins.json"; export function getSelectedSkinHash(state) { return state.selectedSkinHash; @@ -13,6 +14,23 @@ export function getSelectedSkinUrl(state) { return hash == null ? null : Utils.screenshotUrlFromHash(hash); } +export function getSearchQuery(state) { + return state.searchQuery; +} + +// TODO: Memoize this very expensive function +export function getMatchingSkinHashes(state) { + const hashes = Object.keys(skins); + const searchQuery = getSearchQuery(state); + if (searchQuery == null) { + return hashes; + } + return hashes.filter(hash => { + const { fileName } = skins[hash]; + return fileName.includes(searchQuery); + }); +} + export function getUrl(state) { if (state.selectedSkinHash) { // TODO: Add a human readable version diff --git a/src/redux/store.js b/src/redux/store.js index 4e77c6c4..4c10a132 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -2,6 +2,7 @@ import { createStore as createReduxStore } from "redux"; import * as Selectors from "./selectors"; const defaultState = { + searchQuery: null, selectedSkinHash: null, selectedSkinPosition: null }; @@ -31,6 +32,11 @@ function reducer(state = defaultState, action) { }; } return defaultState; + case "SET_SEARCH_QUERY": + return { + ...state, + searchQuery: action.query + }; default: return state; }