Upgrade dependencies, etc. (#55)

* Run prettify

* Upgrade dependencies and switch out some libraries:

* timeago (freshly jquery dependent) -> ReactTimeAgo
* next-ga -> react-ga and custom plumbing

* Move static/ to public/static/

As per https://github.com/zeit/next.js/blob/master/errors/static-dir-deprecated.md

* Fix cons->icons typo

* Import only what's necessary from lodash (223 -> 180 kb)

* Asyncify MembershipInfoForm
This commit is contained in:
Aarni Koskela
2019-10-30 20:05:08 +02:00
committed by Riku Rouvila
parent db98341a51
commit dcf26deb0f
36 changed files with 3376 additions and 585 deletions

View File

@@ -1,9 +1,14 @@
import _ from "lodash"; import flatMap from "lodash/flatMap";
import sortBy from "lodash/sortBy";
import React from "react"; import React from "react";
import request from "axios"; import request from "axios";
import timeago from "timeago";
import api from "./api"; import api from "./api";
import transformers from "./feed-transformers"; import transformers from "./feed-transformers";
import ReactTimeAgo from "react-time-ago";
import JavascriptTimeAgo from "javascript-time-ago";
import timeagoFi from "javascript-time-ago/locale/fi";
JavascriptTimeAgo.locale(timeagoFi);
export default class Feed extends React.Component { export default class Feed extends React.Component {
state = { state = {
@@ -16,17 +21,13 @@ export default class Feed extends React.Component {
async updateFeed() { async updateFeed() {
const res = await request.get(api("feeds")); const res = await request.get(api("feeds"));
const messages = _(res.data) const messages = sortBy(
.map((messages, type) => transformers[type](messages)) flatMap(res.data, (messages, type) => transformers[type](messages)),
.flatten() "timestamp"
.value(); );
messages.reverse(); // In-place
this.setState({ this.setState({
messages: _(messages) messages: messages.slice(0, 40),
.sortBy("timestamp")
.reverse()
.value()
.slice(0, 40),
}); });
} }
@@ -36,7 +37,12 @@ export default class Feed extends React.Component {
if (message.imageLink) { if (message.imageLink) {
image = ( image = (
<a target="_blank" href={message.imageLink} rel="noopener noreferrer" tabIndex="-1"> <a
target="_blank"
href={message.imageLink}
rel="noopener noreferrer"
tabIndex="-1"
>
{image} {image}
</a> </a>
); );
@@ -44,7 +50,9 @@ export default class Feed extends React.Component {
return ( return (
<div className="message" key={i}> <div className="message" key={i}>
<div className="message__image" aria-hidden="true">{image}</div> <div className="message__image" aria-hidden="true">
{image}
</div>
<div className="message__content"> <div className="message__content">
<div className="message__user"> <div className="message__user">
<a href={message.userLink}>{message.user}</a> <a href={message.userLink}>{message.user}</a>
@@ -52,13 +60,13 @@ export default class Feed extends React.Component {
<div <div
className="message__body" className="message__body"
dangerouslySetInnerHTML={{ __html: message.body }} dangerouslySetInnerHTML={{ __html: message.body }}
></div> />
<div className="message__icon"> <div className="message__icon">
<i className={`fa fa-${message.type}`}></i> <i className={`fa fa-${message.type}`} aria-hidden="true" />
</div> </div>
<div className="message__details"> <div className="message__details">
<span className="message__timestamp"> <span className="message__timestamp">
{timeago(message.timestamp)} <ReactTimeAgo date={message.timestamp} locale="fi" />
</span> </span>
<span className="message__meta">{message.meta}</span> <span className="message__meta">{message.meta}</span>
</div> </div>

View File

@@ -24,19 +24,34 @@ export function Footer() {
</div> </div>
<div className="contacts"> <div className="contacts">
<div> <div>
<a href="https://koodiklinikka.slack.com" aria-label="Koodiklinikka Slackissä"> <a
href="https://koodiklinikka.slack.com"
aria-label="Koodiklinikka Slackissä"
>
<i className="fa fa-slack" aria-hidden="true" /> <i className="fa fa-slack" aria-hidden="true" />
</a> </a>
<a href="https://github.com/koodiklinikka/koodiklinikka.fi" aria-label="Koodiklinikka Githubissa"> <a
href="https://github.com/koodiklinikka/koodiklinikka.fi"
aria-label="Koodiklinikka Githubissa"
>
<i className="fa fa-github" aria-hidden="true" /> <i className="fa fa-github" aria-hidden="true" />
</a> </a>
<a href="https://twitter.com/koodiklinikka" aria-label="Koodiklinikka Twitterissä"> <a
href="https://twitter.com/koodiklinikka"
aria-label="Koodiklinikka Twitterissä"
>
<i className="fa fa-twitter" aria-hidden="true" /> <i className="fa fa-twitter" aria-hidden="true" />
</a> </a>
<a href="https://www.linkedin.com/groups/12025476" aria-label="Koodiklinikka Linkedinissä"> <a
href="https://www.linkedin.com/groups/12025476"
aria-label="Koodiklinikka Linkedinissä"
>
<i className="fa fa-linkedin" aria-hidden="true" /> <i className="fa fa-linkedin" aria-hidden="true" />
</a> </a>
<a href="https://www.facebook.com/koodiklinikka" aria-label="Koodiklinikka Facebookissa"> <a
href="https://www.facebook.com/koodiklinikka"
aria-label="Koodiklinikka Facebookissa"
>
<i className="fa fa-facebook" aria-hidden="true" /> <i className="fa fa-facebook" aria-hidden="true" />
</a> </a>
<div id="email"> <div id="email">

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import request from "axios"; import request from "axios";
import _ from "lodash"; import shuffle from "lodash/shuffle";
import api from "./api"; import api from "./api";
export default class Members extends React.Component { export default class Members extends React.Component {
@@ -15,14 +15,16 @@ export default class Members extends React.Component {
async refreshMembers() { async refreshMembers() {
const res = await request.get(api("members")); const res = await request.get(api("members"));
this.setState({ this.setState({
members: _.shuffle(res.data), members: shuffle(res.data),
}); });
} }
render() { render() {
const members = this.state.members.map(member => { const members = this.state.members.map(member => {
const src = `${member.avatar_url}&s=120`; const src = `${member.avatar_url}&s=120`;
return <img className="member" key={member.avatar_url} src={src} alt="" />; return (
<img className="member" key={member.avatar_url} src={src} alt="" />
);
}); });
return ( return (

View File

@@ -1,16 +1,23 @@
import _ from "lodash"; import lodashTemplate from "lodash/template";
import defaultTemplateSettings from "lodash/templateSettings";
import githubEvent from "parse-github-event"; import githubEvent from "parse-github-event";
import twitterText from "twitter-text"; import twitterText from "twitter-text";
const isVisibleGithubEvent = ({ type }) => const isVisibleGithubEvent = ({ type }) =>
type !== "PushEvent" && type !== "DeleteEvent"; type !== "PushEvent" && type !== "DeleteEvent";
const templateSettings = {
...defaultTemplateSettings,
interpolate: /{{([\s\S]+?)}}/g,
};
export default { export default {
github(items) { github(items) {
return items.filter(isVisibleGithubEvent).map(item => { return items.filter(isVisibleGithubEvent).map(item => {
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g; const template = lodashTemplate(
githubEvent.parse(item).text,
const template = _.template(githubEvent.parse(item).text); templateSettings
);
const repository = `https://github.com/${item.repo.name}`; const repository = `https://github.com/${item.repo.name}`;
let branch; let branch;

View File

@@ -1,4 +1,4 @@
import _ from "lodash"; import pick from "lodash/pick";
import request from "axios"; import request from "axios";
import React from "react"; import React from "react";
import classSet from "classnames"; import classSet from "classnames";
@@ -23,7 +23,7 @@ function validateEmail(email) {
const fieldNames = ["name", "email", "handle", "address", "postcode", "city"]; const fieldNames = ["name", "email", "handle", "address", "postcode", "city"];
function getUserInfo(state) { function getUserInfo(state) {
return _.pick(state, fieldNames); return pick(state, fieldNames);
} }
export default class MembershipInfoForm extends React.Component { export default class MembershipInfoForm extends React.Component {
@@ -38,23 +38,21 @@ export default class MembershipInfoForm extends React.Component {
pristineFields: fieldNames, pristineFields: fieldNames,
}; };
onSubmit = () => { onSubmit = async () => {
this.setState({ this.setState({
sending: true, sending: true,
error: null, error: null,
}); });
request try {
.post(api("membership"), { await request.post(api("membership"), {
userInfo: getUserInfo(this.state), userInfo: getUserInfo(this.state),
})
.then(() => {
this.setState({ sending: false });
this.props.onSignupSuccess();
})
.catch(err => {
this.setState({ error: err, sending: false });
}); });
this.setState({ sending: false });
this.props.onSignupSuccess();
} catch (err) {
this.setState({ error: err, sending: false });
}
}; };
onChange = e => { onChange = e => {
@@ -124,7 +122,7 @@ export default class MembershipInfoForm extends React.Component {
const inputFields = fieldNames.map(fieldName => { const inputFields = fieldNames.map(fieldName => {
const inputClasses = classSet({ const inputClasses = classSet({
input: true, input: true,
"has-error": _.includes(fieldsWithErrors, fieldName), "has-error": fieldsWithErrors.includes(fieldName),
half: fieldName === "city" || fieldName === "postcode", half: fieldName === "city" || fieldName === "postcode",
left: fieldName === "postcode", left: fieldName === "postcode",
}); });

3777
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,27 +17,27 @@
"@zeit/next-less": "^1.0.1", "@zeit/next-less": "^1.0.1",
"@zeit/next-stylus": "^1.0.1", "@zeit/next-stylus": "^1.0.1",
"axios": "^0.19.0", "axios": "^0.19.0",
"classnames": "^2.2.1", "classnames": "^2.2.6",
"font-awesome": "^4.4.0", "font-awesome": "^4.7.0",
"less": "^3.10.3", "less": "^3.10.3",
"lodash": "^3.9.1", "lodash": "^4.17.15",
"next": "^9.0.6", "next": "^9.1.2",
"next-fonts": "^0.18.0", "next-fonts": "^0.19.0",
"next-ga": "^2.3.4",
"parse-github-event": "^0.2.0", "parse-github-event": "^0.2.0",
"react": "^16.9.0", "react": "^16.11.0",
"react-dom": "^16.9.0", "react-dom": "^16.11.0",
"react-stripe-checkout": "^2.4.0", "react-ga": "^2.7.0",
"react-stripe-checkout": "^2.6.3",
"react-time-ago": "^5.0.7",
"stylus": "^0.54.7", "stylus": "^0.54.7",
"timeago": "^0.2.0", "twitter-text": "^3.0.0"
"twitter-text": "^1.11.0"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"eslint": "^6.4.0", "eslint": "^6.6.0",
"eslint-config-prettier": "^6.3.0", "eslint-config-prettier": "^6.5.0",
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.14.3", "eslint-plugin-react": "^7.16.0",
"prettier": "^1.18.2" "prettier": "^1.18.2"
}, },
"prettier": { "prettier": {

View File

@@ -1,13 +0,0 @@
import React from "react";
import App from "next/app";
import Router from "next/router";
import withGA from "next-ga";
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return <Component {...pageProps} />;
}
}
export default withGA("UA-58806132-1", Router)(MyApp);

View File

@@ -2,6 +2,19 @@ import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document"; import Document, { Html, Head, Main, NextScript } from "next/document";
import { Footer } from "../components/Footer"; import { Footer } from "../components/Footer";
import Fader from "../components/Fader"; import Fader from "../components/Fader";
import ReactGA from "react-ga";
function trackPageView() {
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
return;
}
if (!window.GA_INITIALIZED) {
ReactGA.initialize("UA-58806132-1");
window.GA_INITIALIZED = true;
}
ReactGA.set({ page: window.location.pathname });
ReactGA.pageview(window.location.pathname);
}
class MyDocument extends Document { class MyDocument extends Document {
static async getInitialProps(ctx) { static async getInitialProps(ctx) {
@@ -9,6 +22,10 @@ class MyDocument extends Document {
return { ...initialProps }; return { ...initialProps };
} }
componentDidMount() {
trackPageView();
}
render() { render() {
return ( return (
<Html lang="fi"> <Html lang="fi">
@@ -41,7 +58,7 @@ class MyDocument extends Document {
sizes="16x16" sizes="16x16"
href="/static/icons/favicon-16x16.png" href="/static/icons/favicon-16x16.png"
/> />
<link rel="manifest" href="/static/cons/site.webmanifest" /> <link rel="manifest" href="/static/icons/site.webmanifest" />
<meta property="og:image" content="images/logo.png" /> <meta property="og:image" content="images/logo.png" />
<script src="https://js.stripe.com/v3/" /> <script src="https://js.stripe.com/v3/" />
<script src="//use.typekit.net/scb5xny.js" /> <script src="//use.typekit.net/scb5xny.js" />

View File

@@ -100,7 +100,10 @@ const IndexContent = () => (
</div> </div>
<div className="column column1-2"> <div className="column column1-2">
<a href="/static/images/slack.png" target="_blank"> <a href="/static/images/slack.png" target="_blank">
<img src="/static/images/slack.png" alt="Slack app at Koodiklinikka" /> <img
src="/static/images/slack.png"
alt="Slack app at Koodiklinikka"
/>
</a> </a>
</div> </div>
</div> </div>
@@ -108,7 +111,10 @@ const IndexContent = () => (
<div className="row"> <div className="row">
<div className="bread"> <div className="bread">
<div className="column column2-5"> <div className="column column2-5">
<img src="/static/images/octocat.png" alt="Octocat, the mascot of GitHub" /> <img
src="/static/images/octocat.png"
alt="Octocat, the mascot of GitHub"
/>
</div> </div>
<div className="column column3-5"> <div className="column column3-5">
<h3>Avoin lähdekoodi</h3> <h3>Avoin lähdekoodi</h3>

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 182 B

After

Width:  |  Height:  |  Size: 182 B

View File

Before

Width:  |  Height:  |  Size: 340 B

After

Width:  |  Height:  |  Size: 340 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 866 B

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

View File

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 293 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB