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
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
28
package.json
@@ -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": {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 182 B After Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 340 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 866 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 293 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |