diff --git a/nodes.md b/nodes.md new file mode 100644 index 00000000..a969dee3 --- /dev/null +++ b/nodes.md @@ -0,0 +1,49 @@ +# States + +1. ~Lazy loading initial results~ +2. Above the fold placeholders +3. Above the fold images +4. Scrolling existing placeholders +5. Scrolling unfetched placeholders + Show random colors +6. Clicked scrollbar and went to the bottom of the page +7. Cliked an image + +We should include the first ~100 results in the initial payload + +Loading a set of search results + +1. Include teh color and md5 in the results + +Each segment should have a state: + +- Loaded +- Loading +- Errored +- Not Requested + +When we want to show a placeholder we: + +- Check if we already have it +- If so, we show it +- If not, pick a random color, and then (asycn) we check which segment it would be in +- If that segement is loading, noop +- If the segment has not been requested, we request it and mark it as loading +- When a segment comes back, we update our state + +When a user clicks a skin + +- If the skin is loaded (we have the md5) we take the user to the permalink +- If the skin is not loaded, we check which segment it would be in +- If the segment has not yet been requested (unlikely, since it should have been requested when it came into view) we request it +- If the segment is loading, we await its completion. +- If 500ms passes before the load completes, we show a spinnner or something +- When the loading completes we take the user to the permalink + +## Functions we'll need + +- `getSegmentForIndex(i)` +- `getMd5ForIndex(i)` (syncronous, but may kick off an async action) +- `getColorForIndex(i)` (syncronus. May give a BS result if it does not have one right away, may kick off an async action) +- `getSegmentStatus(segmentId)` +- `awaitMd5ForIndex(i)` diff --git a/scripts/createSearchIndex.js b/scripts/createSearchIndex.js deleted file mode 100644 index 37e95e96..00000000 --- a/scripts/createSearchIndex.js +++ /dev/null @@ -1,71 +0,0 @@ -const path = require("path"); -const fs = require("fs"); -const algoliasearch = require("algoliasearch"); - -const client = algoliasearch("HQ9I5Z6IM5", "f5357f4070cdb6ed652d9c3feeede89f"); -const index = client.initIndex("Skins"); - -const CACHE_PATH = "/Volumes/Mobile Backup/skins/cache/"; -const info = JSON.parse( - fs.readFileSync(path.join(CACHE_PATH, "info.json"), "utf8") -); - -function tuncate(str, len) { - const overflow = str.length - len; - if (overflow < 0) { - return str; - } - - const half = Math.floor((len - 1) / 2); - - const start = str.slice(0, half); - const end = str.slice(-half); - return `${start} ########### ${end}`; -} - -async function buildSkinIndex(skin) { - const { md5, filePaths } = skin; - if (!filePaths || filePaths.length === 0) { - console.warn("no file name for ", md5); - return; - } - const fileName = path.basename(filePaths[0]); - let readmeText = null; - if (skin.readmePath) { - readmeText = tuncate(fs.readFileSync(skin.readmePath, "utf8"), 4800); - } - return { - objectID: skin.md5, - //md5, - //fileName, - // emails: skin.emails || null, - // readmeText - // color: skin.averageColor - // twitterLikes: Number(skin.twitterLikes || 0) - }; -} - -const indexesPromise = Promise.all( - Object.values(info) - .filter((skin) => skin.type === "CLASSIC") - .map((skin) => buildSkinIndex(skin)) -); - -async function go({ dry = true }) { - const indexes = await indexesPromise; - - if (dry) { - console.log("Index turned off. Turn it on if you really mean it"); - return; - } - console.log("Writing index"); - const results = await new Promise((resolve, reject) => { - index.partialUpdateObjects(indexes, function (err, content) { - if (err != null) reject(err); - resolve(content); - }); - }); - console.log("done!", results); -} - -go({ dry: false }); // .then(content => console.log("Updated index for:", content.length)); diff --git a/scripts/findSkins.js b/scripts/findSkins.js deleted file mode 100644 index 56c4e5a6..00000000 --- a/scripts/findSkins.js +++ /dev/null @@ -1,77 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const Bluebird = require("bluebird"); - -const info = JSON.parse( - fs.readFileSync("/Volumes/Mobile Backup/skins/cache/info.json", "utf8") -); - -const getFileData = async () => { - const fileData = await Bluebird.map( - Object.values(info).filter(skin => skin.type === "CLASSIC"), - async skin => { - const md5 = skin.md5; - const filePaths = skin.filePaths; - if (!filePaths || filePaths.length === 0) { - console.warn("no file name for ", md5); - return; - } - if (skin.twitterLikes) { - // console.log({ skin }); - } - const fileName = path.basename(filePaths[0]); - return { - color: skin.averageColor, - favorites: skin.twitterLikes || 0, - fileName, - md5 - }; - }, - { concurrency: 10 } - ); - const orderedFileData = fileData.sort((a, b) => { - return b.favorites - a.favorites; - }); - return orderedFileData.map(({ favorites, ...rest }) => rest); -}; - -const SKIN_DATA_ROOT = "public/skinData"; -const BOOTSTRAP_SKIN_DATA_ROOT = "src"; -const CHUNK_SIZE = 500; -getFileData().then(data => { - let start = 0; - const chunkFileNames = []; - let chunkNumber = 0; - while (start <= data.length) { - const end = start + CHUNK_SIZE; - - const chunk = data.slice(start, end); - const json = JSON.stringify(chunk); - const fileName = `skins-${chunkNumber}.json`; - fs.writeFileSync(path.join(SKIN_DATA_ROOT, fileName), json, "utf8"); - if (chunkNumber === 0) { - fs.writeFileSync( - path.join(BOOTSTRAP_SKIN_DATA_ROOT, fileName), - json, - "utf8" - ); - } - chunkFileNames.push(fileName); - start = end; - chunkNumber++; - } - const json = JSON.stringify( - { - chunkSize: CHUNK_SIZE, - numberOfSkins: data.length, - chunkFileNames - }, - null, - 2 - ); - fs.writeFileSync( - path.join(BOOTSTRAP_SKIN_DATA_ROOT, "skins.json"), - json, - "utf8" - ); -}); diff --git a/scripts/utils.js b/scripts/utils.js deleted file mode 100644 index 40a5ac02..00000000 --- a/scripts/utils.js +++ /dev/null @@ -1,63 +0,0 @@ -const path = require("path"); -const fs = require("fs"); -const md5File = require("md5-file"); -var { exec } = require("child_process"); - -const METADATA_ROOT = path.join(__dirname, "../metadata/"); - -function getSkinMetadataPath(md5, key) { - return path.join(METADATA_ROOT, md5, `${key}.json`); -} - -function getSkinMetadata(md5, key) { - const skinMetadataPath = getSkinMetadataPath(md5, key); - return new Promise((resolve, reject) => { - fs.readFile(skinMetadataPath, "utf8", (err, data) => { - if (err != null) reject(err); - resolve(JSON.parse(data)); - }); - }); -} - -function ensureDirectoryExistence(filePath) { - var dirname = path.dirname(filePath); - if (fs.existsSync(dirname)) { - return true; - } - ensureDirectoryExistence(dirname); - fs.mkdirSync(dirname); -} - -async function writeSkinMetadata(md5, key, data) { - const skinMetadataPath = getSkinMetadataPath(md5, key); - ensureDirectoryExistence(skinMetadataPath); - return new Promise((resolve, reject) => { - fs.writeFile( - skinMetadataPath, - JSON.stringify(data, null, 2), - "utf8", - err => { - if (err != null) { - reject(err); - } - resolve(skinMetadataPath); - } - ); - }); -} - -function getFileMd5(filePath) { - return new Promise((resolve, reject) => { - md5File(filePath, (err, hash) => { - if (err) reject(err); - - resolve(hash); - }); - }); -} - -module.exports = { - getSkinMetadata, - writeSkinMetadata, - getFileMd5 -}; diff --git a/src/App.js b/src/App.js index dd74fda1..24c7faf2 100644 --- a/src/App.js +++ b/src/App.js @@ -9,12 +9,12 @@ import FocusedSkin from "./FocusedSkin"; import * as Selectors from "./redux/selectors"; import { ABOUT_PAGE } from "./constants"; import * as Utils from "./utils"; -import { SKIN_WIDTH, SKIN_RATIO } from "./constants"; +import { SCREENSHOT_WIDTH, SKIN_RATIO } from "./constants"; // Render your table -const getTableDimensions = (windowWidth) => { - const columnCount = Math.floor(windowWidth / SKIN_WIDTH); +const getTableDimensions = (windowWidth, scale) => { + const columnCount = Math.floor(windowWidth / (SCREENSHOT_WIDTH * scale)); const columnWidth = windowWidth / columnCount; // TODO: Consider flooring this to get things aligned to the pixel const rowHeight = columnWidth * SKIN_RATIO; return { columnWidth, rowHeight, columnCount }; @@ -36,7 +36,8 @@ function useWindowSize() { function App(props) { const { windowWidth, windowHeight } = useWindowSize(); const { columnWidth, rowHeight, columnCount } = getTableDimensions( - windowWidth + windowWidth, + props.scale ); return (