Skip to content

Commit

Permalink
feat(router): add RouterStore
Browse files Browse the repository at this point in the history
  • Loading branch information
azu committed Jun 14, 2016
1 parent ecff0c5 commit 81fc388
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 35 deletions.
17 changes: 17 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Design

## Navigation history

### At First time

1. If has not `?text={query}`, return
2. fill with `{query}` and analyzed

In other word,

1. Separate save data and change url.

### Typing

1. Analyze `{query}` and output result
1. Set `{query}` to navigation `?text={query}`
2 changes: 2 additions & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@
"almin-logger": "^1.1.3",
"bulma": "0.0.28",
"classname": "0.0.0",
"history": "^3.0.0",
"kuromojin": "^1.3.0",
"map-like": "^1.0.1",
"morpheme-match": "file:..",
"query-string": "^4.2.2",
"react": "^15.1.0",
"react-dom": "^15.1.0",
"react-github-corner": "^0.3.0"
Expand Down
4 changes: 4 additions & 0 deletions website/src/AppLocator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export class AppLocator {
* @type {Context}
*/
this.context = null;
/**
* @type {History}
*/
this.history = null;
}
}

Expand Down
18 changes: 14 additions & 4 deletions website/src/component/container/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const React = require("react");
import AppLocator from "../../../AppLocator";
import UpdateAnalyzedTextUseCase from "../../../js/use-case/analyzer/UpdateAnalyzedTextUseCase";
import UpdateTestTextUseCase from "../../../js/use-case/analyzer/UpdateTestTextUseCase";
import ChangeURLStateUseCase from "../../../js/use-case/routing/ChangeURLStateUseCase";
// component
import Title from "../../project/Title/Title";
import InputForm from "../../project/InputForm/InputForm";
Expand All @@ -17,6 +18,7 @@ export default class App extends React.Component {
constructor(...args) {
super(...args);
this.state = AppLocator.context.getState();

}

componentDidMount() {
Expand All @@ -35,16 +37,23 @@ export default class App extends React.Component {
* @type {AnalyzerState}
*/
const analyzer = this.state.analyzer;
/**
* @type {RoutingState}
*/
const routing = this.state.routing;
const matchResult = analyzer.testMatch;
const updateAnalyzedText = (text) => {
AppLocator.context.useCase(UpdateAnalyzedTextUseCase.create()).execute(text);
const context = AppLocator.context;
context.useCase(UpdateAnalyzedTextUseCase.create()).execute(text).then(() => {
return context.useCase(ChangeURLStateUseCase.create()).execute({text});
});
};
const updateTestText = (text) => {
AppLocator.context.useCase(UpdateTestTextUseCase.create()).execute(text);
};
const hashChangeComponent = analyzer.initialized ? <SideEffectLocationHash text={analyzer.currentText}/> : null;

return <div className="App">
{hashChangeComponent}
<SideEffectLocationHash text={routing.text}/>
<GithubCorner href="https://github.com/azu/morpheme-match"/>
<Title className="App-title"><a href="https://github.com/azu/morpheme-match">morpheme-match</a></Title>
<h2 class="subtitle">形態素解析したトークンを元に、文章にマッチするトークンが含まれているかをチェックするライブラリのデモ</h2>
Expand All @@ -54,7 +63,8 @@ export default class App extends React.Component {
</div>
<div className="App-Analyzed">
<AnalyzedTable label="解析結果" tokens={analyzer.tokens}/>
<AnalyzedJSONField permanentURL={analyzer.permanentURL} outputJSON={analyzer.outputJSON}/>
<AnalyzedJSONField permanentURL={analyzer.permanentURL}
outputJSON={analyzer.outputJSON}/>
</div>
<div className="App-Test">
<TestInputForm text={analyzer.testText} onSubmit={updateTestText}/>
Expand Down
11 changes: 8 additions & 3 deletions website/src/component/container/AppBootStrap.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
// LICENSE : MIT
"use strict";
const React = require("react");
const parse = require("query-string").parse;
import App from "./App/App"
import AppLocator from "../../AppLocator";
import LoadingContainer from "../ui-kit/LoadingContainer/LoadingContainer";
import InitializeUseCase from "../../js/use-case/InitializeUseCase";
import ChangeURLStateUseCase from "../../js/use-case/routing/ChangeURLStateUseCase";
const AppLoading = LoadingContainer(App);
export default class AppBootStrap extends React.Component {
render() {
const hashString = location.hash.slice(1);
const hash = hashString ? decodeURIComponent(hashString) : undefined;
const promise = AppLocator.context.useCase(InitializeUseCase.create()).execute({hash});
const location = AppLocator.history.getCurrentLocation();
const query = parse(location.search);
const context = AppLocator.context;
const promise = context.useCase(InitializeUseCase.create()).execute({text: query.text}).catch(error => {
console.error(error);
});
return <AppLoading promise={promise}/>;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
// LICENSE : MIT
"use strict";
const React = require("react");
import AppLocator from "../../../AppLocator";
export default class SideEffectLocationHash extends React.Component {

constructor() {
super();
}

shouldComponentUpdate(nextProps, nextState) {
return nextProps.text !== undefined && nextProps.text.length > 0;
}

render() {
this._updateHash(this.props.text);
if (this.props.text) {
AppLocator.history.push({
search: `?text=${this.props.text}`,
});
} else {
// empt
AppLocator.history.push({
search: null
});
}
return null;
}

Expand All @@ -20,6 +34,6 @@ export default class SideEffectLocationHash extends React.Component {
}
}
SideEffectLocationHash.propTypes = {
updateText: React.PropTypes.func,
text: React.PropTypes.string
text: React.PropTypes.string,
isFirstTime: React.PropTypes.bool
};
7 changes: 7 additions & 0 deletions website/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import AppStoreGroup from "./js/store/AppStore";
// context
import {Context, Dispatcher} from "almin";
import AlminLogger from "almin-logger";
import {createHistory} from 'history';
const history = createHistory();
// instances
const dispatcher = new Dispatcher();
const appStoreGroup = AppStoreGroup.create();
Expand All @@ -20,7 +22,12 @@ const appContext = new Context({
// start logger
const logger = new AlminLogger();
logger.startLogging(appContext);
appContext.onErrorDispatch(error => {
console.error(error);
});
// global
AppLocator.context = appContext;
AppLocator.history = history;

// entry point
ReactDOM.render(<AppBootStrap />, document.getElementById("js-app"));
4 changes: 3 additions & 1 deletion website/src/js/store/AppStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
"use strict";
import {StoreGroup} from "almin";
import AnalyzerStore from "./analyzer/AnalyzerStore"
import RoutingStore from "./routing/RoutingStore"
import analyzedRepository from "../infra/repository/AnalyzerRepository";
export default class AppStore {
static create() {
return new StoreGroup([
new AnalyzerStore({analyzedRepository})
new AnalyzerStore({analyzedRepository}),
new RoutingStore()
])
}
}
18 changes: 5 additions & 13 deletions website/src/js/store/analyzer/AnalyzerState.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"use strict";
import ReduceState from "../base/ReduceState";
const createTokenMatcher = require("morpheme-match");
import UpdateAnalyzedTextUseCase from "../../use-case/analyzer/UpdateAnalyzedTextUseCase";
import InitializeUseCase from "../../use-case/InitializeUseCase";
export default class AnalyzerState extends ReduceState {
/**
* @param {Analyzer} analyzer
* @param {Analyzer} [analyzer]
* @param {boolean} initialized
*/
constructor({analyzer = {}, initialized} = {}) {
super();
Expand All @@ -18,10 +18,6 @@ export default class AnalyzerState extends ReduceState {
this.testTokens = analyzer.testTokens || [];
}

get isChangedByUser() {
return this.initialized;
}

get testMatch() {
let matchedToken = [];
const matchedTokens = this.tokens.map(token => token.toJSON());
Expand All @@ -45,7 +41,8 @@ export default class AnalyzerState extends ReduceState {

// return "<value>"
get permanentURL() {
const origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
const origin = window.location.protocol + "//" + window.location.hostname + (window.location.port
? ":" + window.location.port : "");
const hash = this.currentText ? `#${this.currentText}` : "";
return `${origin}${window.location.pathname}${hash}`;
}
Expand All @@ -56,15 +53,10 @@ export default class AnalyzerState extends ReduceState {

reduce(payload) {
switch (payload.type) {
case UpdateAnalyzedTextUseCase.Events.initialize:
case InitializeUseCase.Events.didInitialized:
return new AnalyzerState(Object.assign({}, this, {
initialized: true
}));
// by initial event
case InitializeUseCase.Events.initialize:
return new AnalyzerState(Object.assign({}, this, {
initialized: false
}));
default:
return this;
}
Expand Down
22 changes: 22 additions & 0 deletions website/src/js/store/routing/RoutingState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// LICENSE : MIT
"use strict";
import ReduceState from "../base/ReduceState";
import ChangeURLStateUseCase from "../../use-case/routing/ChangeURLStateUseCase";
export default class RoutingState extends ReduceState {
constructor({query} = {}) {
super();
const queryObject = query || {};
this.text = queryObject.text;
}

reduce(payload) {
switch (payload.type) {
case ChangeURLStateUseCase.Events.change:
return new RoutingState(Object.assign({}, this, {
query: payload.query
}));
default:
return this;
}
}
}
19 changes: 19 additions & 0 deletions website/src/js/store/routing/RoutingStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// LICENSE : MIT
"use strict";
import ReduceStore from "../base/ReduceStore";
import RoutingState from "./RoutingState";
export default class RoutingStore extends ReduceStore {
constructor() {
super();
this.state = new RoutingState();
this.onDispatch(payload => {
this.setState(this.state.reduce(payload));
});
}

getState() {
return {
routing: this.state
};
}
}
8 changes: 4 additions & 4 deletions website/src/js/use-case/InitializeUseCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ export default class InitializeUseCase extends UseCase {
this.analyzedRepository = analyzedRepository;
}

execute({hash}) {
const defaultText = hash || "日本語の文章を解析します。";
execute({text} = {}) {
const defaultText = text || "日本語の文章を解析します。";
return AnalyzerFactory.create().then((analyzer) => {
this.analyzedRepository.save(analyzer);
const initializeAnalyzedText = new UpdateAnalyzedTextUseCase({analyzedRepository: this.analyzedRepository});
return this.context.useCase(initializeAnalyzedText).execute(defaultText);
}).then(() => {
this.dispatch({type: InitializeUseCase.Events.initialize})
this.dispatch({type: InitializeUseCase.Events.didInitialized})
});
}
}
InitializeUseCase.Events = {
initialize: "update-by-internal"
didInitialized: "didInitialized"
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ export default class UpdateAnalyzedTextUseCase extends UseCase {
execute(text) {
const analyzer = this.analyzedRepository.lastUsed();
analyzer.analyze(text);
this.dispatch({
type: UpdateAnalyzedTextUseCase.Events.initialize
});
this.analyzedRepository.save(analyzer);
}
}
UpdateAnalyzedTextUseCase.Events = {
initialize: "update-by-user"
};
}
22 changes: 22 additions & 0 deletions website/src/js/use-case/routing/ChangeURLStateUseCase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// LICENSE : MIT
"use strict";
import {UseCase} from 'almin';
/**
* Change URL Query for preserve state
*/
export default class ChangeURLStateUseCase extends UseCase {
static create() {
return new this();
}

execute(query) {
this.dispatch({
type: ChangeURLStateUseCase.Events.change,
query: query
});
}

}
ChangeURLStateUseCase.Events = {
change: "ChangeURLStateUseCase"
};

0 comments on commit 81fc388

Please sign in to comment.