Move extracting the zip to the epic/store

This commit is contained in:
Jordan Eldredge 2018-12-15 13:33:29 -08:00
parent 5ff53e39f2
commit 4e0d362c96
4 changed files with 126 additions and 50 deletions

View file

@ -1,66 +1,109 @@
import React from "react";
import JSZip from "jszip";
import { connect } from "react-redux";
import { switchMap } from "rxjs/operators";
import { from, empty } from "rxjs";
import { from } from "rxjs";
import Disposable from "./Disposable";
export default class Readme extends React.Component {
class Readme extends React.Component {
constructor(props) {
super(props);
this._disposable = new Disposable();
this.state = { readmeText: null };
this.state = { focusedFile: null };
}
componentDidMount() {
this._fetchReadme();
}
componentDidMount() {}
componentDidUpdate(_, oldState) {
if (this.state.skinUrl != oldState.skinUrl) {
this._fetchReadme();
}
}
componentDidUpdate(_, oldState) {}
componentWillUnmount() {
this._disposable.dispose();
}
_fetchReadme() {
this._disposable.add(
from(fetch(this.props.skinUrl))
.pipe(
switchMap(response => response.blob()),
switchMap(blob => JSZip.loadAsync(blob)),
switchMap(zip => {
const readme = zip.file("readme.txt");
if (readme == null) {
return empty();
}
return readme.async("string");
})
)
.subscribe(text => {
console.log(text);
// this.setState({ readmeText: text });
})
);
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) {
return;
}
switch (this.state.focusedFile.type) {
case "txt":
return (
<textarea
style={{
width: "100%",
height: "300px"
}}
>
{this.state.focusedFile.content}
</textarea>
);
case "bmp":
case "cur":
const mimeType = `image/cur`;
const content = URL.createObjectURL(
new Blob([this.state.focusedFile.content], { type: mimeType })
);
return <img src={content} />;
}
}
render() {
return (
this.state.readmeText && (
<pre
style={{
backgroundColor: "white",
color: "black",
position: "absolute",
width: "500px",
right: 0
}}
>
{this.state.readmeText}
</pre>
)
<div
style={{
position: "fixed",
width: 500,
left: 0,
height: "100%",
backgroundColor: "white",
overflow: "scroll"
}}
>
<ul style={{ float: "left" }}>
{this.props.zip &&
Object.keys(this.props.zip.files).map(fileName => (
<li
key={fileName}
style={{
fontWeight:
this.state.focusedFile &&
this.state.focusedFile.fileName === fileName
? "bold"
: "normal"
}}
>
<a onClick={() => this._focusFile(fileName)}>{fileName}</a>
</li>
))}
</ul>
<div style={{ float: "left" }}>{this._renderFocusedFile()}</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
zip: state.skinZip
};
}
export default connect(mapStateToProps)(Readme);

View file

@ -17,3 +17,7 @@ export function requestedRandomSkin() {
export function gotNewMatchingHashes(matchingHashes) {
return { type: "GOT_NEW_MATCHING_HASHES", matchingHashes };
}
export function loadedSkinZip(zip) {
return { type: "LOADED_SKIN_ZIP", zip };
}

View file

@ -1,8 +1,9 @@
import { combineEpics } from "redux-observable";
import { of, from } from "rxjs";
import { of, from, empty } from "rxjs";
import * as Actions from "./actionCreators";
import * as Selectors from "./selectors";
import { filter, switchMap, map } from "rxjs/operators";
import * as Utils from "../utils";
import { filter, switchMap, map, ignoreElements, tap } from "rxjs/operators";
import { search } from "../algolia";
const urlChangedEpic = actions =>
@ -20,6 +21,21 @@ const urlChangedEpic = actions =>
})
);
const selectedSkinEpic = actions =>
actions.pipe(
filter(action => action.type === "SELECTED_SKIN"),
switchMap(action => {
return from(fetch(Utils.skinUrlFromHash(action.hash))).pipe(
switchMap(response => response.blob()),
switchMap(async blob => {
const JSZip = await import("jszip");
return JSZip.loadAsync(blob);
}),
map(zip => Actions.loadedSkinZip(zip))
);
})
);
const searchEpic = actions =>
actions.pipe(
filter(action => action.type === "SEARCH_QUERY_CHANGED"),
@ -44,4 +60,9 @@ const randomSkinEpic = (actions, states) =>
return Actions.selectedSkin(Selectors.getRandomSkinHash(states.value));
})
);
export default combineEpics(searchEpic, urlChangedEpic, randomSkinEpic);
export default combineEpics(
searchEpic,
urlChangedEpic,
selectedSkinEpic,
randomSkinEpic
);

View file

@ -7,7 +7,8 @@ const defaultState = {
searchQuery: null,
selectedSkinHash: null,
selectedSkinPosition: null,
matchingHashes: null
matchingHashes: null,
skinZip: null
};
function reducer(state = defaultState, action) {
@ -16,13 +17,15 @@ function reducer(state = defaultState, action) {
return {
...state,
selectedSkinHash: action.hash,
selectedSkinPosition: action.position
selectedSkinPosition: action.position,
skinZip: null
};
case "CLOSE_MODAL":
return {
...state,
selectedSkinHash: null,
selectedSkinPosition: null
selectedSkinPosition: null,
skinZip: null
};
case "SEARCH_QUERY_CHANGED":
return {
@ -36,6 +39,11 @@ function reducer(state = defaultState, action) {
...state,
matchingHashes: action.matchingHashes
};
case "LOADED_SKIN_ZIP":
return {
...state,
skinZip: action.zip
};
default:
return state;
}