Skip to content

Commit

Permalink
[Release 1.4.0]
Browse files Browse the repository at this point in the history
* remove jQuery / Blaze dependencies
* upgrade material-ui
* fix CommentList user avatar bug
* improve TopImages layout
  • Loading branch information
ShinyLeee committed Dec 18, 2017
1 parent 5be6d3a commit 8aa09c0
Show file tree
Hide file tree
Showing 26 changed files with 365 additions and 194 deletions.
3 changes: 0 additions & 3 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,10 @@ deanius:promise
didericis:callpromise-mixin

# production
nitrolabs:cdn # TODO remove jQuery / Blaze dependencies
percolate:migrations

# testing
dburles:factory
practicalmeteor:mocha # TODO remove jQuery / Blaze dependencies
practicalmeteor:chai
practicalmeteor:sinon
johanbrook:publication-collector
dispatch:mocha-phantomjs
Expand Down
16 changes: 6 additions & 10 deletions imports/startup/client/accounts-config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Accounts } from 'meteor/accounts-base';
import history from '/imports/utils/history';

Accounts.onLogout(() => {
history.replace('/login');
});
Accounts.onLogout(() => history.replace('/login'));

Accounts.onEmailVerificationLink((token) => {
Accounts.verifyEmail(token, (err) => {
if (err) {
console.log(err);
console.warn(err);
return history.replace({
pathname: `/${err.error || 500}`,
state: { message: `服务器内部错误 ${err.reason}` },
Expand All @@ -18,9 +16,7 @@ Accounts.onEmailVerificationLink((token) => {
});
});

Accounts.onResetPasswordLink((token) => {
history.replace({
pathname: '/accounts/resetPassword',
state: { token },
});
});
Accounts.onResetPasswordLink((token) => history.replace({
pathname: '/accounts/resetPassword',
state: { token },
}));
144 changes: 144 additions & 0 deletions imports/startup/server/cdn-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

import url from 'url';
import path from 'path';
import { Meteor } from 'meteor/meteor';
import { WebApp, WebAppInternals } from 'meteor/webapp';

// From https://github.com/Nitrolabs/meteor-cdn/blob/master/lib/server.js
// Minimal cdn config which remove blaze and jQuery dependencies

const FONTS = ['.ttf', '.eot', '.otf', '.svg', '.woff', '.woff2'];
const ALLOWED_PROTOCOLS = ['http:', 'https:'];

/* stripSlashes
*
* Strip the trailing slash from a url
*/
function stripSlashes(slashUrl) {
if (slashUrl) {
return slashUrl.replace(/\/$/, '');
}
return slashUrl;
}

function validateSettings(rootUrl, cdnUrl) {
// Return True if the ROOT_URL and CDN_URL settings are valid
// Return False if the settings are invalid, but the server can continue
// Throw an error if the settings are fatally incorrect
if (!rootUrl) {
console.warn('ROOT_URL is not set. Using default Meteor behaviour');
return false;
} else if (!cdnUrl) {
console.warn('CDN_URL is not set. Using default Meteor behaviour');
return false;
}
const cdn = url.parse(cdnUrl);
const root = url.parse(rootUrl);

if (root.hostname === 'localhost') {
return false;
}

// Make sure that the CDN_URL is different from the ROOT_URL
// If these are the same, we can't detect requests from the CDN
if (cdn.host === root.host) {
console.warn('CDN: CDN HOST === ROOT HOST. Using default Meteor behaviour');
return false;
}

// Ensure that the CDN_URL and ROOT_URL are correctly formed
if (ALLOWED_PROTOCOLS.indexOf(root.protocol) < 0) {
throw new Meteor.Error(`ROOT_URL must use http or https protocol, not ${root.protocol}`);
} else if (ALLOWED_PROTOCOLS.indexOf(cdn.protocol) < 0) {
throw new Meteor.Error(`CDN_URL must use http or https protocol, not ${cdn.protocol}`);
}

// Return true if the settings are valid
return true;
}


function setClientCdnUrl(cdnUrl) {
// Make the CDN_URL available on the client
// console.log("Setting BundledJsCssPrefix to "+cdnUrl);
const hasQuestionMark = new RegExp('[?]');
WebAppInternals.setBundledJsCssUrlRewriteHook((bundlePath) => {
// This code fixes an issue in Galaxy where you can end up getting served
// stale code after deployments
const galaxyVersionId = process.env.GALAXY_APP_VERSION_ID;
let rewrittenUrl = cdnUrl + bundlePath;
if (galaxyVersionId) {
const separator = hasQuestionMark.test(bundlePath) ? '&' : '?';
rewrittenUrl += `${separator}_g_app_v_=${galaxyVersionId}`;
}
return rewrittenUrl;
});
// WebAppInternals.setBundledJsCssPrefix(cdnUrl);
// eslint-disable-next-line no-undef
__meteor_runtime_config__.CDN_URL = cdnUrl;
}

function configureBrowserPolicy(cdnUrl) {
console.log('Attemping to configure BrowserPolicy');
if (Package['browser-policy']) { // eslint-disable-line no-undef
BrowserPolicy.content.allowOriginForAll(cdnUrl); // eslint-disable-line no-undef
console.log(`Configure BrowserPolicy allowOriginForAll(${cdnUrl})`);
}
}

function CdnController() {
const rootUrl = stripSlashes(process.env.ROOT_URL);
const cdnUrl = stripSlashes(process.env.CDN_URL);

const CORSconnectHandler = (req, res, next) => {
// Set CORS headers on webfonts to avoid issue with chrome and firefox
const ext = path.extname(url.parse(req.url).pathname);
if (FONTS.indexOf(ext) > -1) {
res.setHeader('Strict-Transport-Security', 'max-age=2592000; includeSubDomains'); // 2592000s / 30 days
res.setHeader('Access-Control-Allow-Origin', '*');
}
next();
};

const static404connectHandler = (req, res, next) => {
// Return 404 if a non-existent static file is requested
// If REQUEST_HOST === CDN_URL then a 404 is returned for all non-static files
const pathname = url.parse(req.url).pathname;
// const ext = path.extname(pathname);
const root = url.parse(rootUrl);
const cdn = url.parse(cdnUrl);

const isFromCDN = (req.headers.host === cdn.host && req.headers.host !== root.host);

// Cloudfront removes all headers by default
// We need the HOST header to determine where this request came from
if (!req.headers.host) {
console.warn('HOST header is not set');
console.warn('Unable to determine if this request came via the CDN');
} else if (isFromCDN && !(pathname in WebAppInternals.staticFiles)) {
console.warn(`Static resource not found: ${pathname}`);
res.writeHead(404);
res.write('Static File Not Found');
res.end();
return res;
} else if (isFromCDN) {
console.log(`Serving to CDN: ${pathname}`);
}
next();
};

// Initialize the CDN
if (validateSettings(rootUrl, cdnUrl)) {
setClientCdnUrl(cdnUrl);
configureBrowserPolicy(cdnUrl);
WebApp.rawConnectHandlers.use(static404connectHandler);
WebApp.rawConnectHandlers.use(CORSconnectHandler);
console.info(`Using CDN: ${cdnUrl}`);
}
}

Meteor.startup(() => {
if (process.env.NODE_ENV === 'production') {
CdnController();
}
});
2 changes: 2 additions & 0 deletions imports/startup/server/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import './cdn-config';

// This defines a starting set of data to be loaded if the app is loaded with an empty db.
import './fixtures';

Expand Down
16 changes: 7 additions & 9 deletions imports/ui/components/CollList/CollHolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ class CollHolder extends Component {
anchorEl: undefined,
}

_handleRequestClose = () => {
_handleClose = () => {
this.setState({ menuOpen: false });
}

_handleToggleLock = () => {
const { coll } = this.props;
this._handleRequestClose();
this._handleClose();
this.props.onToggleLock(coll);
}

_handleToggleRemove = () => {
const { coll } = this.props;
this._handleRequestClose();
this._handleClose();
this.props.onRemove(coll);
}

Expand All @@ -84,11 +84,9 @@ class CollHolder extends Component {
: `${coll.cover}?imageView2/2/w/${rWidth}`;
return (
<Wrapper>
<Cover>
<Link to={`/user/${coll.user}/collection/${coll.name}`}>
<img src={fastSrc} alt="" />
</Link>
</Cover>
<Link to={`/user/${coll.user}/collection/${coll.name}`}>
<Cover><img src={fastSrc} alt="" /></Cover>
</Link>
<Info>
<Avatar classes={{ root: classes.avatar }} src={get(owner, 'profile.avatar')} alt="" />
<CollName>{coll.name}</CollName>
Expand All @@ -111,7 +109,7 @@ class CollHolder extends Component {
key="actionMenu"
open={this.state.menuOpen}
anchorEl={this.state.anchorEl}
onRequestClose={this._handleRequestClose}
onClose={this._handleClose}
>
{/* <MenuItem
leftIcon={<InfoIcon />}
Expand Down
38 changes: 9 additions & 29 deletions imports/ui/components/CommentList/CommentList.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
import get from 'lodash/get';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import TimeAgo from 'react-timeago';
import CNStrings from 'react-timeago/lib/language-strings/zh-CN';
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter';
import React, { PureComponent } from 'react';
import Avatar from 'material-ui/Avatar';
import Button from 'material-ui/Button';
import Collapse from 'material-ui/transitions/Collapse';
import Popover from 'material-ui/Popover';
import Input from 'material-ui/Input';
import List, {
ListItem,
ListItemAvatar,
ListItemText,
} from 'material-ui/List';
import { insertComment, removeComment } from '/imports/api/comments/methods';
import settings from '/imports/utils/settings';
import CommentListItem from './CommentListItem';
import {
CommentsContent,
CommentsTime,
PublishSection,
PublishContent,
PublishFooter,
} from './CommentList.style';

const { sourceDomain } = settings;

const formatter = buildFormatter(CNStrings);

export default class CommentList extends Component {
export default class CommentList extends PureComponent {
static propTypes = {
open: PropTypes.bool.isRequired,
owner: PropTypes.object.isRequired,
discId: PropTypes.string.isRequired,
comments: PropTypes.array.isRequired,
User: PropTypes.object, // not required bc guest can visit it
Expand All @@ -51,7 +43,7 @@ export default class CommentList extends Component {
return User ? User.profile.avatar : defaultAvatar;
}

_handleCommentClick(e, comment) {
_handleCommentClick = (e, comment) => {
this.setState({ [comment._id]: true, popoverAnchor: e.currentTarget });
}

Expand Down Expand Up @@ -115,7 +107,6 @@ export default class CommentList extends Component {
render() {
const {
open,
owner,
comments,
User,
classes,
Expand All @@ -129,27 +120,16 @@ export default class CommentList extends Component {
{
comments.length > 0 && comments.map((comment) => (
<List key={comment._id} className={classes.list}>
<ListItem
onClick={(e) => this._handleCommentClick(e, comment)}
button
>
<ListItemAvatar>
<Avatar src={get(owner, 'profile.avatar')} />
</ListItemAvatar>
<CommentsContent>
<h3>{comment.user}</h3>
<div dangerouslySetInnerHTML={{ __html: comment.content }} />
</CommentsContent>
<CommentsTime>
<TimeAgo date={comment.createdAt} formatter={formatter} />
</CommentsTime>
</ListItem>
<CommentListItem
comment={comment}
onClick={this._handleCommentClick}
/>
<Popover
open={this.state[comment._id]}
anchorEl={this.state.popoverAnchor}
anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
transformOrigin={{ horizontal: 'right', vertical: 'center' }}
onRequestClose={() => this.setState({ [comment._id]: false })}
onClose={() => this.setState({ [comment._id]: false })}
>
<List>
<ListItem onClick={() => this._handleReplyComment(comment)}>
Expand Down
Loading

0 comments on commit 8aa09c0

Please sign in to comment.