Improve skin inspector

This commit is contained in:
Jordan Eldredge 2018-12-19 20:27:27 -08:00
parent 4e0d362c96
commit 017a4f661d
5 changed files with 99 additions and 64 deletions

View file

@ -1,49 +1,19 @@
import React from "react";
import { connect } from "react-redux";
import { switchMap } from "rxjs/operators";
import { from } from "rxjs";
import Disposable from "./Disposable";
import * as Actions from "./redux/actionCreators";
class Readme extends React.Component {
constructor(props) {
super(props);
this._disposable = new Disposable();
this.state = { focusedFile: null };
}
componentDidMount() {}
componentDidUpdate(_, oldState) {}
componentWillUnmount() {
this._disposable.dispose();
}
async _focusFile(fileName) {
if (this.props.zip == null) {
return;
}
const file = await this.props.zip.file(fileName);
const type = fileName
.split(".")
.pop()
.toLowerCase();
const methodFromType = {
txt: "string",
bmp: "blob",
cur: "blob"
};
let content = await file.async(methodFromType[type]);
this.setState({ focusedFile: { fileName, type, content } });
}
_renderFocusedFile() {
if (this.state.focusedFile == null) {
if (this.props.focusedFile == null) {
return;
}
switch (this.state.focusedFile.type) {
const { ext, fileName, content } = this.props.focusedFile;
if (content == null) {
return;
}
switch (ext) {
case "txt":
return (
<textarea
@ -51,17 +21,18 @@ class Readme extends React.Component {
width: "100%",
height: "300px"
}}
>
{this.state.focusedFile.content}
</textarea>
defaultValue={content}
/>
);
case "bmp":
case "cur":
const mimeType = `image/cur`;
const content = URL.createObjectURL(
new Blob([this.state.focusedFile.content], { type: mimeType })
const mimeType = `image/${ext}`;
const url = URL.createObjectURL(
new Blob([content], { type: mimeType })
);
return <img src={content} />;
return <img src={url} alt={fileName} />;
default:
return null;
}
}
@ -77,24 +48,22 @@ class Readme extends React.Component {
overflow: "scroll"
}}
>
<ul style={{ float: "left" }}>
<select onChange={e => this.props.selectSkinFile(e.target.value)}>
{this.props.zip &&
Object.keys(this.props.zip.files).map(fileName => (
<li
<option
key={fileName}
style={{
fontWeight:
this.state.focusedFile &&
this.state.focusedFile.fileName === fileName
? "bold"
: "normal"
}}
value={fileName}
selected={
this.props.focusedFile &&
this.props.focusedFile.fileName === fileName
}
>
<a onClick={() => this._focusFile(fileName)}>{fileName}</a>
</li>
{fileName}
</option>
))}
</ul>
<div style={{ float: "left" }}>{this._renderFocusedFile()}</div>
</select>
<div>{this._renderFocusedFile()}</div>
</div>
);
}
@ -102,8 +71,19 @@ class Readme extends React.Component {
function mapStateToProps(state) {
return {
zip: state.skinZip
zip: state.skinZip,
focusedFile: state.focusedSkinFile
};
}
export default connect(mapStateToProps)(Readme);
function mapDispatchToProps(dispatch) {
return {
selectSkinFile(fileName) {
dispatch(Actions.selectSkinFile(fileName));
}
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Readme);

View file

@ -38,8 +38,8 @@ class SkinTable extends React.Component {
_handleResize() {
const { windowWidth, windowHeight } = Utils.getWindowSize();
if (
windowWidth != this.state.windowWidth ||
windowHeight != this.state.windowHeight
windowWidth !== this.state.windowWidth ||
windowHeight !== this.state.windowHeight
) {
this.setState({ windowWidth, windowHeight });
}

View file

@ -21,3 +21,16 @@ export function gotNewMatchingHashes(matchingHashes) {
export function loadedSkinZip(zip) {
return { type: "LOADED_SKIN_ZIP", zip };
}
export function selectSkinFile(fileName) {
const ext = fileName
.split(".")
.pop()
.toLowerCase();
return { type: "SELECTED_SKIN_FILE_TO_FOCUS", fileName, ext };
}
export function gotFocusedSkinFile(content) {
return { type: "GOT_FOCUSED_SKIN_FILE", content };
}

View file

@ -3,7 +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, tap } from "rxjs/operators";
import { filter, switchMap, map, tap } from "rxjs/operators";
import { search } from "../algolia";
const urlChangedEpic = actions =>
@ -36,6 +36,28 @@ const selectedSkinEpic = actions =>
})
);
const focusedSkinFileEpic = (actions, states) =>
actions.pipe(
filter(action => action.type === "SELECTED_SKIN_FILE_TO_FOCUS"),
switchMap(({ fileName, ext }) => {
// TODO: Ensure this is never called with the wrong zip. Should this live in the "got zip" closure?
const { skinZip } = states.value;
if (skinZip == null) {
// TODO: Should this throw?
return empty();
}
const methodFromExt = {
txt: "string",
bmp: "blob",
cur: "blob"
};
return from(skinZip.file(fileName).async(methodFromExt[ext])).pipe(
map(content => Actions.gotFocusedSkinFile(content))
);
})
);
const searchEpic = actions =>
actions.pipe(
filter(action => action.type === "SEARCH_QUERY_CHANGED"),
@ -64,5 +86,6 @@ export default combineEpics(
searchEpic,
urlChangedEpic,
selectedSkinEpic,
focusedSkinFileEpic,
randomSkinEpic
);

View file

@ -8,7 +8,8 @@ const defaultState = {
selectedSkinHash: null,
selectedSkinPosition: null,
matchingHashes: null,
skinZip: null
skinZip: null,
focusedSkinFile: null
};
function reducer(state = defaultState, action) {
@ -44,6 +45,24 @@ function reducer(state = defaultState, action) {
...state,
skinZip: action.zip
};
case "SELECTED_SKIN_FILE_TO_FOCUS": {
return {
...state,
focusedSkinFile: {
content: null,
ext: action.ext,
fileName: action.fileName
}
};
}
case "GOT_FOCUSED_SKIN_FILE":
return {
...state,
focusedSkinFile: {
...state.focusedSkinFile,
content: action.content
}
};
default:
return state;
}