Always get permalink from state

Also: Fetch skin data from Algolia as a stop gap
This commit is contained in:
Jordan Eldredge 2019-05-20 07:58:08 -07:00
parent deb8d8c245
commit 79ede7e26c
7 changed files with 109 additions and 42 deletions

View file

@ -14,7 +14,8 @@ const Cell = React.memo(props => {
skin,
requestToken,
setSelectedSkin,
requestUnloadedSkin
requestUnloadedSkin,
permalinkUrl
} = props;
const { width, height } = data;
React.useEffect(() => {
@ -51,7 +52,8 @@ const Cell = React.memo(props => {
width={width}
selectSkin={setSelectedSkin}
color={color}
permalink={Utils.getPermalinkUrlFromHashAndFilename(hash, skin.fileName)}
// TODO: This is werid because there is an implicit assumption that this is always avaliable if we have the skin
permalink={permalinkUrl}
/>
);
});
@ -65,9 +67,13 @@ const mapStateToProps = (state, ownProps) => {
rowIndex,
columnIndex
});
const getPermalinkUrlFromHash = Selectors.getPermalinkUrlFromHashGetter(
state
);
return {
requestToken,
skin
skin,
permalinkUrl: skin == null ? null : getPermalinkUrlFromHash(skin.hash)
};
};

View file

@ -6,10 +6,11 @@ import DownloadLink from "./DownloadLink";
import * as Utils from "./utils";
import * as Selectors from "./redux/selectors";
import * as Actions from "./redux/actionCreators";
import { SCREENSHOT_HEIGHT, SCREENSHOT_WIDTH, SKIN_WIDTH } from "./constants";
import { SCREENSHOT_HEIGHT, SCREENSHOT_WIDTH } from "./constants";
import { delay } from "rxjs/operators";
import { Subject, combineLatest, timer, fromEvent } from "rxjs";
import { Subject, combineLatest, timer, fromEvent, from } from "rxjs";
import Disposable from "./Disposable";
import { search } from "./algolia";
class FocusedSkin extends React.Component {
constructor(props) {
@ -64,7 +65,40 @@ class FocusedSkin extends React.Component {
);
}
_fetchSkinData() {
if (this.props.skinData != null) {
return;
}
// OMG Giant hack. Kill this please. We should be able to get this data from our own server.
from(
search(this.props.hash, { hitsPerPage: 2, typoTolerance: 0 })
).subscribe(results => {
if (results.nbHits.length === 1) {
console.error(
"Failed to get skin data for hash",
this.props.hash,
results
);
return;
}
if (results.nbHits.length > 1) {
console.error(
"Failed to uniquely get skin data for hash",
this.props.hash
);
return;
}
const { fileName, color } = results.hits[0];
this.props.gotSkinData(this.props.hash, {
md5: this.props.hash,
fileName,
color
});
});
}
componentDidMount() {
this._fetchSkinData();
this._disposable.add(
fromEvent(window.document, "keydown").subscribe(e => {
if (e.key === "ArrowRight") {
@ -183,7 +217,7 @@ class FocusedSkin extends React.Component {
}}
onLoad={() => this.setState({ previewLoaded: true })}
src={Utils.screenshotUrlFromHash(this.props.hash)}
alt={this.props.fileName}
alt={this.props.skinData && this.props.skinData.fileName}
/>
)}
</div>
@ -204,7 +238,7 @@ class FocusedSkin extends React.Component {
e.target.setSelectionRange(0, e.target.value.length)
}
className="permalink-input"
value={Utils.getAbsolutePermalinkUrlFromHash(this.props.hash)}
value={this.props.absolutePermalink}
readOnly
autoFocus
/>
@ -220,7 +254,9 @@ class FocusedSkin extends React.Component {
</span>
</div>
)}
{this.props.fileName}
{this.props.skinData
? this.props.skinData.fileName
: "Filename loading..."}
{" ["}
<DownloadLink
href={Utils.skinUrlFromHash(this.props.hash)}
@ -240,7 +276,7 @@ class FocusedSkin extends React.Component {
</a>
{"] ["}
<a
href={Utils.getAbsolutePermalinkUrlFromHash(this.props.hash)}
href={this.props.absolutePermalink}
onClick={e => {
this.setState({ showLink: !this.state.showLink });
e.preventDefault();
@ -264,14 +300,22 @@ class FocusedSkin extends React.Component {
}
}
const mapStateToProps = (state, ownProps) => ({
hash: Selectors.getSelectedSkinHash(state),
initialPosition: Selectors.getSelectedSkinPosition(state),
fileExplorerOpen: Selectors.getFileExplorerOpen(state),
fileName: state.skins[ownProps.hash].fileName
});
const mapStateToProps = (state, ownProps) => {
return {
hash: Selectors.getSelectedSkinHash(state),
initialPosition: Selectors.getSelectedSkinPosition(state),
fileExplorerOpen: Selectors.getFileExplorerOpen(state),
skinData: state.skins[ownProps.hash] || null,
absolutePermalink: Selectors.getAbsolutePermalinkUrlFromHashGetter(state)(
ownProps.hash
)
};
};
const mapDispatchToProps = dispatch => ({
gotSkinData(hash, data) {
dispatch({ type: "GOT_SKIN_DATA", hash, data });
},
selectRelativeSkin(offset) {
dispatch(Actions.selectRelativeSkin(offset));
},

View file

@ -3,7 +3,7 @@ var client = algoliasearch("HQ9I5Z6IM5", "6466695ec3f624a5fccf46ec49680e51");
var index = client.initIndex("Skins");
export function search(query) {
export function search(query, options = {}) {
return new Promise((resolve, reject) => {
index.search(
{
@ -13,7 +13,8 @@ export function search(query) {
hitsPerPage: 1000,
// https://www.algolia.com/doc/api-reference/api-parameters/typoTolerance/
// min: Retrieve records with the smallest number of typos.
typoTolerance: "min"
typoTolerance: "min",
...options
},
(err, content) => {
if (err != null) {

View file

@ -3,14 +3,7 @@ import { of, from, empty } from "rxjs";
import * as Actions from "./actionCreators";
import * as Selectors from "./selectors";
import * as Utils from "../utils";
import {
filter,
switchMap,
map,
ignoreElements,
mergeMap,
tap
} from "rxjs/operators";
import { filter, switchMap, map, mergeMap } from "rxjs/operators";
import { search } from "../algolia";
const urlChangedEpic = actions =>

View file

@ -25,6 +25,12 @@ const defaultState = {
export default function reducer(state = defaultState, action) {
switch (action.type) {
case "GOT_SKIN_DATA": {
return {
...state,
skins: { ...state.skins, [action.hash]: action.data }
};
}
case "GOT_SKIN_CHUNK": {
const newSkins = { ...state.skins };
const newDefaultSkins = [...state.defaultSkins];

View file

@ -94,19 +94,50 @@ export function getRandomSkinHash(state) {
return skinHashes[randomIndex];
}
export const getPermalinkUrlFromHashGetter = createSelector(
getSkins,
skins => {
return hash => {
const skin = skins[hash];
if (skin == null) {
return `/skin/${hash}/`;
}
return `/skin/${hash}/${skin.fileName}/`;
};
}
);
export const getAbsolutePermalinkUrlFromHashGetter = createSelector(
getPermalinkUrlFromHashGetter,
getPermalinkUrlFromHash => {
return hash => {
return window.location.origin + getPermalinkUrlFromHash(hash);
};
}
);
export const getUrl = createSelector(
getActiveContentPage,
getSelectedSkinHash,
getSearchQuery,
getFileExplorerOpen,
getFocusedSkinFile,
(activeContentPage, hash, query, fileExplorerOpen, focusedSkinFile) => {
getSkins,
getPermalinkUrlFromHashGetter,
(
activeContentPage,
hash,
query,
fileExplorerOpen,
focusedSkinFile,
skins,
getPermalinkUrlFromHash
) => {
if (activeContentPage === ABOUT_PAGE) {
return "/about/";
}
if (hash) {
// TODO: Add a human readable version
const skinUrl = Utils.getPermalinkUrlFromHash(hash);
const skinUrl = getPermalinkUrlFromHash(hash);
if (fileExplorerOpen && focusedSkinFile) {
return `${skinUrl}files/${encodeURIComponent(
focusedSkinFile.fileName

View file

@ -6,20 +6,6 @@ export function skinUrlFromHash(hash) {
return `https://s3.amazonaws.com/webamp-uploaded-skins/skins/${hash}.wsz`;
}
export function getPermalinkUrlFromHash(hash) {
// TODO: Make this a selector
return `/skin/${hash}/${hash}/`;
}
export function getPermalinkUrlFromHashAndFilename(hash, fileName) {
// TODO: Make this a selector
return `/skin/${hash}/${fileName}/`;
}
export function getAbsolutePermalinkUrlFromHash(hash) {
return window.location.origin + getPermalinkUrlFromHash(hash);
}
export function getWindowSize() {
var w = window,
d = document,