Skip to content

Commit

Permalink
Merge pull request #3 from Asing1001/master
Browse files Browse the repository at this point in the history
Release 2016/12/13
  • Loading branch information
Asing1001 authored Dec 13, 2016
2 parents 9d5b08e + 4f543f5 commit 61932ad
Show file tree
Hide file tree
Showing 19 changed files with 440 additions and 136 deletions.
94 changes: 43 additions & 51 deletions config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,47 @@ var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');

module.exports = {
entry: {
'main': './src/app/main.tsx',
'vendor': './src/app/vendor.ts'
},

resolve: {
extensions: ['', '.js', '.ts', '.tsx']
},

module: {
loaders: [
{ test: /\.tsx?$/, loader: "ts-loader" },
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=public/[name].[hash].[ext]'
}
],
preLoaders: [
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ test: /\.js$/, loader: "source-map-loader" }

resolve: {
extensions: ['', '.js', '.ts', '.tsx']
},

module: {
loaders: [
{ test: /\.tsx?$/, loaders: ["react-hot-loader/webpack", "ts-loader"] },
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=public/[name].[hash].[ext]'
}
],
preLoaders: [
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ test: /\.js$/, loader: "source-map-loader" }
]
},

plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['main', 'vendor']
}),

new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),

new CopyWebpackPlugin(
[{
from: './src/public',
to: 'public'
}]
),

new webpack.ProvidePlugin({}),

]
},

plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['main', 'vendor']
}),

new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),

new CopyWebpackPlugin(
[
{
from: './src/public',
to: 'public'
}
]
),

new webpack.ProvidePlugin({
'Promise': "bluebird"
}),

]
};
};
8 changes: 8 additions & 0 deletions config/webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ var helpers = require('./helpers');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-eval-source-map',

entry: {
// Add the react hot loader entry point - in reality, you only want this in your dev Webpack config
'hot' :'react-hot-loader/patch',
'hotserver' :'webpack/hot/only-dev-server',
'main': './src/app/main.tsx',
'vendor': './src/app/vendor.ts',
},

output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:3004/',
Expand Down
5 changes: 5 additions & 0 deletions config/webpack.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',

entry: {
'main': './src/app/main.tsx',
'vendor': './src/app/vendor.ts'
},

output: {
path: helpers.root('dist'),
publicPath: '/',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
},
"private": true,
"dependencies": {
"bluebird": "^3.4.6",
"body-parser": "^1.15.2",
"bootstrap": "^3.3.7",
"cheerio": "^0.22.0",
"core-js": "^2.4.1",
"express": "^4.14.0",
"express-graphql": "^0.5.3",
"graphql": "^0.7.0",
Expand Down Expand Up @@ -80,6 +80,7 @@
"mocha": "^3.0.2",
"node-inspector": "^0.12.8",
"nodemon": "^1.10.2",
"react-hot-loader": "^3.0.0-beta.6",
"rimraf": "^2.5.4",
"source-map-loader": "^0.1.5",
"style-loader": "^0.13.1",
Expand Down
52 changes: 52 additions & 0 deletions src/app/components/findResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from 'react';
import Movie from '../../models/movie';
import Paper from 'material-ui/Paper';
import Ratings from './ratings';

interface MovieDetailProps {
movie: Movie
showDetail: Function
}

const isSmallScreen = typeof window !== 'undefined' && window.matchMedia("only screen and (max-width: 760px)").matches;
class FindResult extends React.Component<MovieDetailProps, {}> {
constructor(props) {
super(props)
}

private getSmallPosterSrc(posterUrl: string) {
return isSmallScreen ? posterUrl.replace('mpost', 'mpost4') : posterUrl;
}

private showDetail(e) {
e.preventDefault();
this.props.showDetail(this.props.movie);
}

render() {
return (
<Paper zDepth={2} className="row no-margin" style={{ marginBottom: '.5em' }}>
<div className="col-xs-3 col-sm-2 no-padding">
<img className="pointer img-responsive" onClick={this.showDetail.bind(this)} src={this.getSmallPosterSrc(this.props.movie.posterUrl)} />
</div>
<div className="col-xs-9 col-sm-10" style={{ paddingBottom: '.5em' }}>
<div className="pointer" onClick={this.showDetail.bind(this)} style={{ paddingTop: '.5em', paddingBottom: '.5em' }}>
<b>{this.props.movie.chineseTitle}({this.props.movie.englishTitle})</b>
<div className="resultInfo">
<span>上映日:{this.props.movie.releaseDate}</span>
<span className="hidden-xs">類型:{this.props.movie.type}</span>
<span>片長:{this.props.movie.runTime}</span>
</div>
</div>
<Ratings className="resultRatings" movie={this.props.movie}></Ratings>
<div className="hidden-xs">
<div className="resultSummary" dangerouslySetInnerHTML={{ __html: this.props.movie.summary }}></div>
<a href="" className="pointer" onClick={this.showDetail.bind(this)}>繼續閱讀...</a>
</div>
</div>
</Paper>
);
};
}

export default FindResult;
66 changes: 44 additions & 22 deletions src/app/components/home.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import * as React from 'react';
import MovieDetailTabs from './movieDetailTabs';
import FindResult from './findResult';
import AutoComplete from 'material-ui/AutoComplete';
import Paper from 'material-ui/Paper';
import Movie from '../../models/movie';
import 'isomorphic-fetch';


class Home extends React.Component<any, any> {
allMoviesName: Array<Object> = [];
resultMovie: any = {};
constructor(props) {
super(props)
this.state = {
searchText: '',
dataSource: [],
resultMovie: new Movie()
resultMovies: []
};
}

componentWillMount() {
this.getDataSource();
if (this.props.params.id){
this.search([parseInt(this.props.params.id)]);
}else{

}
}

componentDidMount() {
this.getDataSource();
document.querySelector('input').focus();
}

Expand All @@ -33,17 +36,11 @@ class Home extends React.Component<any, any> {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: "{allMovies{chineseTitle,englishTitle,yahooId}}" }),
body: JSON.stringify({ query: "{allMoviesNames{value,text}}" }),
credentials: 'include',
}).then(res => res.json())
.then(json => {
json.data.allMovies.forEach(({chineseTitle, englishTitle, yahooId}: Movie) => {
this.allMoviesName.push({ value: yahooId, text: chineseTitle })
if (englishTitle && englishTitle !== chineseTitle) {
this.allMoviesName.push({ value: yahooId, text: englishTitle })
}
});
this.setState({ dataSource: this.allMoviesName })
this.setState({ dataSource: json.data.allMoviesNames })
});
}

Expand All @@ -54,7 +51,23 @@ class Home extends React.Component<any, any> {
document.querySelector('input').focus();
}

private search(selectItem, index) {
private onNewRequest(selectItem, index, filteredList) {
let yahooIds = [];
if (index === -1) {
let searchText = selectItem.toLowerCase();
if (!filteredList) {
yahooIds = this.state.dataSource.filter(({value, text}) => text.toLowerCase().indexOf(searchText) !== -1).map(({value}) => parseInt(value)).slice(0, 6);
} else {
yahooIds = filteredList.map(({value}) => parseInt(value.key)).slice(0, 6);
}
} else {
yahooIds.push(parseInt(selectItem.value));
}

this.search(yahooIds);
}

private search(yahooIds: Array<Number>) {
fetch('/graphql', {
method: 'POST',
headers: {
Expand All @@ -64,7 +77,7 @@ class Home extends React.Component<any, any> {
body: JSON.stringify({
query: `
{
movie(yahooId:${selectItem.value}){
movies(yahooIds:${JSON.stringify(yahooIds)}){
yahooId
posterUrl
chineseTitle
Expand All @@ -90,8 +103,7 @@ class Home extends React.Component<any, any> {
credentials: 'include',
}).then(res => res.json())
.then(json => {
this.setState({ resultMovie: this.classifyArticle(json.data.movie) });
document.querySelector('input').focus();
this.setState({ resultMovies: json.data.movies.map(movie => this.classifyArticle(movie)) });
});
}

Expand All @@ -117,27 +129,37 @@ class Home extends React.Component<any, any> {
return movie;
}

private showDetail(movie) {
this.setState({ resultMovies: [movie] });
}


render() {
return (
<div className="container">
<div style={{ position: 'relative' }}>
<div className="autoCompleteWrapper">
<AutoComplete
hintText="電影名稱(中英皆可)"
dataSource={this.state.dataSource}
floatingLabelText="找電影"
fullWidth={true}
filter={AutoComplete.caseInsensitiveFilter}
maxSearchResults={6}
onNewRequest={this.search.bind(this)}
onNewRequest={this.onNewRequest.bind(this)}
searchText={this.state.searchText}
onUpdateInput={this.handleUpdateInput.bind(this)}
/>
<button className={`clearButton ${this.state.searchText ? '' : 'displayNone'}`} onClick={this.clearSearchText.bind(this)}>X</button>
</div>
<Paper zDepth={2}>
<MovieDetailTabs movie={this.state.resultMovie}></MovieDetailTabs>
</Paper>
{
this.state.resultMovies.length === 1 ?
<Paper zDepth={2}>
<MovieDetailTabs movie={this.state.resultMovies[0]}></MovieDetailTabs>
</Paper> :
this.state.resultMovies.map((movie: Movie) => (
<FindResult key={movie.yahooId} movie={movie} showDetail={this.showDetail.bind(this)}></FindResult>
))
}
</div>
);
}
Expand Down
25 changes: 3 additions & 22 deletions src/app/components/movieDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table';
import Movie from '../../models/movie';
import Ratings from './ratings';

interface MovieDetailProps {
movie: Movie
Expand All @@ -14,28 +15,8 @@ class MovieDetail extends React.Component<MovieDetailProps, {}> {
render() {
return (
<div>
<div className="col-md-8 col-xs-12 pull-right">
<div className="ratings">
<div className="ratingWrapper"><img src="public/image/imdb.png" />
{this.props.movie.imdbID ? <a target="_blank" href={"http://www.imdb.com/title/" + this.props.movie.imdbID}> {this.props.movie.imdbRating ? this.props.movie.imdbRating : 'N/A'}</a>
: <span> {this.props.movie.imdbRating ? this.props.movie.imdbRating : 'N/A'}</span>
}
</div>
<div className="ratingWrapper"><img src="public/image/yahoo.png" />
<a target="_blank" href={"https://tw.movies.yahoo.com/movieinfo_main.html/id=" + this.props.movie.yahooId}> {this.props.movie.yahooRating ? this.props.movie.yahooRating : 'N/A'}</a>
</div>
<div className="ratingWrapper"><img src="public/image/rottentomatoes.png" />
{this.props.movie.tomatoURL && this.props.movie.tomatoURL !== 'N/A' ? <a target="_blank" href={this.props.movie.tomatoURL}> {this.props.movie.tomatoRating ? this.props.movie.tomatoRating : 'N/A'}</a>
: <span> {this.props.movie.tomatoRating ? this.props.movie.tomatoRating : 'N/A'}</span>
}
</div>
<div className="ratingWrapper"><span className="pttLogo">PTT</span>
<span className="hint--bottom" aria-label="(好雷/普雷/負雷)">
{this.props.movie.goodRateArticles.length}/{this.props.movie.normalRateArticles.length}/{this.props.movie.badRateArticles.length}
</span>
</div>
</div>

<div className="col-md-8 col-xs-12 pull-right">
<Ratings className="ratings" movie={this.props.movie}></Ratings>
<Table className="movieDetail"
selectable={false}
>
Expand Down
Loading

0 comments on commit 61932ad

Please sign in to comment.