diff --git a/.circleci/config.yml b/.circleci/config.yml index fa5d8dad8d3..87bf7e2b52a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.0 jobs: acceptance: docker: - - image: circleci/node:8-stretch-browsers + - image: circleci/node:10-stretch-browsers parallelism: 1 steps: - add_ssh_keys @@ -44,7 +44,7 @@ jobs: hokusai registry push --tag $CIRCLE_SHA1 --force --overwrite publish_staging_assets: docker: - - image: circleci/node:8-stretch-browsers + - image: circleci/node:10-stretch-browsers steps: - add_ssh_keys - checkout @@ -58,7 +58,7 @@ jobs: - manifest.json publish_release_assets: docker: - - image: circleci/node:8-stretch-browsers + - image: circleci/node:10-stretch-browsers steps: - add_ssh_keys - checkout diff --git a/.nvmrc b/.nvmrc index ef59ed7f567..4e31022509a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.12 +10.13 diff --git a/.prettierignore b/.prettierignore index 764f8b23876..7d4e8e8f54d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ __generated__ +src/desktop/components/react/stitch_components/index.tsx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 71af3f0732a..914f8fa0ab9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,11 +18,11 @@ git remote add upstream https://github.com/artsy/force.git ## Run Force -Install [NVM](https://github.com/creationix/nvm) and Node 8. +Install [NVM](https://github.com/creationix/nvm) and Node 10. ```sh -nvm install 8 -nvm alias default 8 +nvm install 10 +nvm alias default 10 ``` Install node modules with Yarn. diff --git a/Dockerfile b/Dockerfile index 724a28448e7..d363e078502 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:8.12.0 +FROM node:10.14.1 ARG commit_hash RUN test -n "$commit_hash" diff --git a/app.json b/app.json index 5e6a0d13bfe..a0999268e4f 100644 --- a/app.json +++ b/app.json @@ -179,9 +179,6 @@ "METAPHYSICS_ENDPOINT": { "required": true }, - "MIXPANEL_ID": { - "required": true - }, "MOBILE_MARKETING_SIGNUP_MODALS": { "required": true }, diff --git a/docs/images/echo_manifest.png b/docs/images/echo_manifest.png new file mode 100644 index 00000000000..94090e15b33 Binary files /dev/null and b/docs/images/echo_manifest.png differ diff --git a/docs/images/hokusai_images.png b/docs/images/hokusai_images.png new file mode 100644 index 00000000000..85c36cc453d Binary files /dev/null and b/docs/images/hokusai_images.png differ diff --git a/docs/images/single_image.png b/docs/images/single_image.png new file mode 100644 index 00000000000..f16b60171c3 Binary files /dev/null and b/docs/images/single_image.png differ diff --git a/docs/rolling_back.md b/docs/rolling_back.md new file mode 100644 index 00000000000..58fea62d5cc --- /dev/null +++ b/docs/rolling_back.md @@ -0,0 +1,23 @@ +## How to Rollback (on Kubernetes, deployed w/ Hokusai) + +So, you're reading this doc, in which case either nothing is on fire and you're simply educating yourself (yay!), or, there's a problem and you need to rollback Force. Take a deep breath, and read on. + +1. Run `hokusai registry images`. You'll see output like the below. + +![list of images](images/hokusai_images.png "Hokusai Images") + +2. Locate the name of a tag you want to deploy. In this case, since we're rolling back, it'll be the previous tag of the form `production-YYYY-mm-dd..` + +![specific image](images/single_image.png "Last Prod Image") + +3. Locate the Circle CI deploy process that corresponded to the deploy you are rolling back to. You may need to scroll back in the build history until you find it. One such example is: https://circleci.com/workflow-run/9fec809a-d096-4fc0-88b7-82419ada519d + +4. Open up the final step, `deploy_hokusai_production`, and expand the 'Echo Manifest' step. This is a config variable that changes with each deploy, and needs to be set manually during the rollback. + +![echo manifest](images/echo_manifest.png "Echo Manifest") + +5. Copy the long value into a text editor, and add single quotes around the entire thing. Then copy it to your clipboard. + +6. Run the following command to set the config in the environment. `hokusai production env set ASSET_MANIFEST=...clipboard contents...` + +7. When that completes, run the following command to deploy the desired image that you've located above. `hokusai production deploy production--2018-11-21--20-05-37` diff --git a/package.json b/package.json index a393db278c9..23ddce2bda1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "engines": { - "node": "8.12.x", + "node": "10", "yarn": "1.x.x" }, "scripts": { @@ -50,11 +50,12 @@ "react-relay": "https://github.com/alloy/relay/releases/download/v1.5.0-artsy.5/react-relay-1.5.0-artsy.5.tgz" }, "dependencies": { - "@artsy/express-reloadable": "^1.3.1", - "@artsy/palette": "^2.18.1", - "@artsy/passport": "^1.1.0", - "@artsy/reaction": "^6.1.8", - "@artsy/stitch": "^2.0.0", + "@airbnb/node-memwatch": "^1.0.2", + "@artsy/express-reloadable": "1.4.0", + "@artsy/palette": "2.23.2", + "@artsy/passport": "1.1.0", + "@artsy/reaction": "9.1.12", + "@artsy/stitch": "3.1.0", "@babel/core": "^7.0.0", "@babel/node": "^7.0.0", "@babel/plugin-proposal-class-properties": "^7.0.0", @@ -98,7 +99,7 @@ "bem-cn-lite": "^3.0.0", "bluebird": "^3.4.6", "bluebird-q": "^2.1.1", - "blueimp-file-upload": "9.9.0", + "blueimp-file-upload": "9.22.1", "body-parser": "^1.14.1", "bucket-assets": "^1.0.2", "chalk": "^2.4.1", @@ -158,7 +159,6 @@ "lodash.isinteger": "^4.0.4", "lodash.merge": "^4.6.0", "mailcheck": "^1.1.1", - "memwatch-ng": "^1.2.0", "moment": "~2.16.0", "moment-timezone": "^0.5.5", "moment-twitter": "^0.2.0", @@ -238,6 +238,7 @@ "@types/react-dom": "^16.0.7", "@types/styled-system": "^3.0.4", "@types/webpack": "^4.4.11", + "@types/webpack-env": "^1.13.6", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^23.6.0", "benv": "^3.3.0", @@ -245,7 +246,7 @@ "coffee-loader": "^0.8.0", "core-js": "^2.5.7", "danger": "^6.0.2", - "electron": "1.7.8", + "electron": "1.7.16", "enzyme": "^3.4.4", "enzyme-adapter-react-16": "^1.5.0", "eslint": "^5.6.1", @@ -265,7 +266,7 @@ "husky": "^0.14.3", "imagesloaded": "^4.1.1", "inquirer": "^1.2.3", - "jest": "^23.5.2", + "jest": "^23.6.0", "jest-coffee-preprocessor": "^1.0.0", "jsdom": "^11.0.0", "jsdom-global": "^3.0.2", diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000000..a786489dd2b --- /dev/null +++ b/renovate.json @@ -0,0 +1,10 @@ +{ + "extends": [ + "@artsy" + ], + "assignees": [ + "zephraph", + "damassi", + "sweir27" + ] +} diff --git a/src/desktop/analytics/README.md b/src/desktop/analytics/README.md index 6bc6d55fc43..137ac7e80d8 100644 --- a/src/desktop/analytics/README.md +++ b/src/desktop/analytics/README.md @@ -139,7 +139,7 @@ Open Chrome dev tools `cmd + opt + i`, click on the "Network" tab, click the "Fi ![](https://s3.amazonaws.com/f.cl.ly/items/2Z2K0X1E3d0O141D1d1n/Image%202015-04-14%20at%201.09.48%20PM.png) -Filter the calls according the service you need to debug by typing in the "Filter" input on the left, e.g. "mixpanel". Then click on a network call you think is reponsible for the even you want to track. This will bring up a panel that looks like this: +Filter the calls according the service you need to debug by typing in the "Filter" input on the left, e.g. "segment". Then click on a network call you think is reponsible for the even you want to track. This will bring up a panel that looks like this: ![](https://s3.amazonaws.com/f.cl.ly/items/1p3M3N3o3O3z0s0P2C28/Image%202015-04-14%20at%201.20.24%20PM.png) diff --git a/src/desktop/analytics/criteo.js b/src/desktop/analytics/criteo.js index 5015406bc71..9153b392996 100644 --- a/src/desktop/analytics/criteo.js +++ b/src/desktop/analytics/criteo.js @@ -63,7 +63,7 @@ if (pathSplit[1] === "auctions") { ) } else { window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "d" }, { event: "setEmail", email: userEmail }, { event: "viewItem", item: sd.COMMERCIAL.artwork._id } @@ -72,7 +72,7 @@ if (pathSplit[1] === "auctions") { // ARTWORKS viewBasket analyticsHooks.on("inquiry_questionnaire:modal:opened", function(data) { window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "d" }, { event: "setEmail", email: userEmail }, { @@ -90,7 +90,7 @@ if (pathSplit[1] === "auctions") { // ARTWORKS trackTransaction analyticsHooks.on("inquiry_questionnaire:inquiry:sync", function(data) { window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "d" }, { event: "setEmail", email: userEmail }, { @@ -111,7 +111,7 @@ if (pathSplit[1] === "auctions") { // https://www.artsy.net/collect - (ARTWORKS viewHome) // 0 1 window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "d" }, { event: "setEmail", email: userEmail }, { event: "viewHome" } diff --git a/src/desktop/apps/article/__tests__/routes.test.js b/src/desktop/apps/article/__tests__/routes.test.js index a942fd23568..ab06ad8222b 100644 --- a/src/desktop/apps/article/__tests__/routes.test.js +++ b/src/desktop/apps/article/__tests__/routes.test.js @@ -6,7 +6,14 @@ import Channel from "desktop/models/channel.coffee" import { getCurrentUnixTimestamp } from "reaction/Components/Publishing/Constants" const rewire = require("rewire")("../routes") -const { amp, classic, editorialSignup, index, subscribedToEditorial } = rewire +const { + amp, + classic, + editorialSignup, + index, + isCustomEditorial, + subscribedToEditorial, +} = rewire describe("Article Routes", () => { let req @@ -39,6 +46,7 @@ describe("Article Routes", () => { rewire.__set__("sd", { ARTSY_EDITORIAL_CHANNEL: "123", APP_URL: "https://artsy.net", + EOY_2018_ARTISTS: "5bf30690d8b9430baaf6c6de", }), rewire.__set__("sailthru", { apiPost: sailthruApiPost, @@ -388,6 +396,39 @@ describe("Article Routes", () => { }) }) }) + + describe("Custom editorial", () => { + it("#isCustomEditorial returns key if article.id matches custom editorial list", () => { + isCustomEditorial("5bf30690d8b9430baaf6c6de").should.containEql( + "EOY_2018_ARTISTS" + ) + }) + + it("Adds custom editorial var and no-header class to stitch args", done => { + const data = { + article: _.extend({}, fixtures.article, { + slug: "foobar", + channel_id: "123", + layout: "feature", + id: "5bf30690d8b9430baaf6c6de", + }), + } + rewire.__set__( + "positronql", + sinon.stub().returns(Promise.resolve(data)) + ) + const stitch = sinon.stub() + rewire.__set__("stitch", stitch) + req.path = "/article/foobar" + index(req, res, next).then(() => { + stitch.args[0][0].locals.bodyClass.should.containEql("body-no-header") + stitch.args[0][0].data.customEditorial.should.containEql( + "EOY_2018_ARTISTS" + ) + done() + }) + }) + }) }) describe("#classic", () => { diff --git a/src/desktop/apps/article/components/App.js b/src/desktop/apps/article/components/App.js index ba11b460c74..45e59ddee9e 100644 --- a/src/desktop/apps/article/components/App.js +++ b/src/desktop/apps/article/components/App.js @@ -16,6 +16,7 @@ export default hot(module)( class App extends React.Component { static propTypes = { article: PropTypes.object, + customEditorial: PropTypes.string, } getArticleLayout = () => { diff --git a/src/desktop/apps/article/components/layouts/Article.tsx b/src/desktop/apps/article/components/layouts/Article.tsx index b2f0298304f..df34cea4036 100644 --- a/src/desktop/apps/article/components/layouts/Article.tsx +++ b/src/desktop/apps/article/components/layouts/Article.tsx @@ -1,8 +1,9 @@ import React from "react" import { once } from "lodash" - -import { Article } from "reaction/Components/Publishing" -import { ArticleProps } from "reaction/Components/Publishing/Article" +import { + Article, + ArticleProps, +} from "@artsy/reaction/dist/Components/Publishing/Article" import { ModalOptions, ModalType, @@ -108,6 +109,7 @@ export default class ArticleLayout extends React.Component< render() { const { article, + customEditorial, isSuper, isLoggedIn, isMobile, @@ -116,7 +118,7 @@ export default class ArticleLayout extends React.Component< templates: { SuperArticleFooter, SuperArticleHeader } = {} as any, } = this.props - const isStatic = isSuper || article.seriesArticle + const isStatic = isSuper || article.seriesArticle || customEditorial return (
@@ -131,6 +133,7 @@ export default class ArticleLayout extends React.Component< {isStatic ? (
{ const isSuper = article.is_super_article || article.is_super_sub_article const isFullscreen = article.hero_section && article.hero_section.type === "fullscreen" - if (isSuper && isFullscreen) { + if ((isSuper && isFullscreen) || isCustomEditorial(article.id)) { bodyClass = bodyClass + " body-no-header" } return bodyClass @@ -318,3 +325,13 @@ export const editorialSignup = (req, res, next) => { export const redirectPost = (req, res, next) => res.redirect(301, req.url.replace("post", "article")) + +export const isCustomEditorial = id => { + const customIds = [sd.EOY_2018_ARTISTS, sd.EOY_2018_CULTURE] + + if (customIds.includes(id)) { + return findKey(sd, val => { + return val === id + }) + } +} diff --git a/src/desktop/apps/article/templates/meta.jade b/src/desktop/apps/article/templates/meta.jade index c82044ad250..690cd24eb99 100644 --- a/src/desktop/apps/article/templates/meta.jade +++ b/src/desktop/apps/article/templates/meta.jade @@ -82,3 +82,7 @@ link( rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/l //- Set Viewport scale meta( name="viewport", content="width=device-width, initial-scale=1.0" ) + + +style + =sd.RESPONSIVE_CSS diff --git a/src/desktop/apps/artist/client.js b/src/desktop/apps/artist/client.tsx similarity index 96% rename from src/desktop/apps/artist/client.js rename to src/desktop/apps/artist/client.tsx index 35a1c0e12a0..9324d543e10 100644 --- a/src/desktop/apps/artist/client.js +++ b/src/desktop/apps/artist/client.tsx @@ -1,20 +1,13 @@ import { buildClientApp } from "reaction/Artsy/Router/client" import { data as sd } from "sharify" import { routes } from "reaction/Apps/Artist/routes" -import mediator from "desktop/lib/mediator.coffee" import React from "react" import ReactDOM from "react-dom" import styled from "styled-components" import qs from "querystring" import { clone, isArray } from "underscore" -mediator.on("artist:filter:changed", filters => { - onFilterChange(filters) -}) - -mediator.on("artist:tabclick", ({ to }) => { - window.analytics.page({ path: to }, { integrations: { Marketo: false } }) -}) +const mediator = require("desktop/lib/mediator.coffee") buildClientApp({ routes, @@ -40,6 +33,14 @@ if (module.hot) { module.hot.accept() } +mediator.on("artist:filter:changed", filters => { + onFilterChange(filters) +}) + +mediator.on("artist:tabclick", ({ to }) => { + window.analytics.page({ path: to }, { integrations: { Marketo: false } }) +}) + // Update URL with current filters and sort. const onFilterChange = filters => { const params = clone(filters) diff --git a/src/desktop/apps/artist/server.js b/src/desktop/apps/artist/server.js deleted file mode 100644 index d74a35f5c36..00000000000 --- a/src/desktop/apps/artist/server.js +++ /dev/null @@ -1,81 +0,0 @@ -import { buildServerApp } from "reaction/Artsy/Router/server" -import { Meta, query, toJSONLD } from "./components/Meta" -import { stitch } from "@artsy/stitch" -import { routes } from "reaction/Apps/Artist/routes" -import express from "express" -import metaphysics from "lib/metaphysics.coffee" -import React from "react" -import styled from "styled-components" -import { buildServerAppContext } from "desktop/lib/buildServerAppContext" - -const app = (module.exports = express()) - -app.get("/artist/:artistID*", async (req, res, next) => { - try { - const user = req.user && req.user.toJSON() - - const { ServerApp, redirect, status } = await buildServerApp({ - routes, - url: req.url, - context: buildServerAppContext(req, res), - }) - - if (redirect) { - res.redirect(302, redirect.url) - return - } - - // FIXME: Move this to Reaction - const Container = styled.div` - width: 100%; - max-width: 1192px; - margin: auto; - ` - - const send = { - method: "post", - query, - variables: { artistID: req.params.artistID }, - } - - const { artist } = await metaphysics(send).then(data => data) - const { APP_URL, IS_MOBILE, REFERRER } = res.locals.sd - const isExternalReferer = !(REFERRER && REFERRER.includes(APP_URL)) - const jsonLD = toJSONLD(artist, APP_URL) - - res.locals.sd.ARTIST_PAGE_CTA_ENABLED = - !user && isExternalReferer && !IS_MOBILE - res.locals.sd.ARTIST_PAGE_CTA_ARTIST_ID = req.params.artistID - - // Render layout - const layout = await stitch({ - basePath: __dirname, - layout: "../../components/main_layout/templates/react_redesign.jade", - config: { - styledComponents: true, - }, - blocks: { - head: () => , - body: () => ( - - - - ), - }, - locals: { - ...res.locals, - assetPackage: "artist", - }, - data: { - jsonLD, - }, - }) - - res.status(status).send(layout) - } catch (error) { - console.log(error) - next(error) - } -}) - -export default app diff --git a/src/desktop/apps/artist/server.tsx b/src/desktop/apps/artist/server.tsx new file mode 100644 index 00000000000..6dde288f7b6 --- /dev/null +++ b/src/desktop/apps/artist/server.tsx @@ -0,0 +1,96 @@ +import { buildServerApp } from "reaction/Artsy/Router/server" +import { Meta, query, toJSONLD } from "./components/Meta" +import { stitch } from "@artsy/stitch" +import { routes } from "reaction/Apps/Artist/routes" +import React from "react" +import styled from "styled-components" +import { buildServerAppContext } from "desktop/lib/buildServerAppContext" +import express, { Request, Response, NextFunction } from "express" + +const metaphysics = require("lib/metaphysics.coffee") + +export const app = express() + +app.get( + "/artist/:artistID*", + async (req: Request, res: Response, next: NextFunction) => { + try { + const user = req.user && req.user.toJSON() + + const { + ServerApp, + redirect, + status, + headTags, + scripts, + } = await buildServerApp({ + routes, + url: req.url, + userAgent: req.header("User-Agent"), + context: buildServerAppContext(req, res), + }) + + if (redirect) { + res.redirect(302, redirect.url) + return + } + + // FIXME: Move this to Reaction + const Container = styled.div` + width: 100%; + max-width: 1192px; + margin: auto; + ` + + const send = { + method: "post", + query, + variables: { artistID: req.params.artistID }, + } + + const { artist } = await metaphysics(send).then(data => data) + const { APP_URL, IS_MOBILE, REFERRER } = res.locals.sd + const isExternalReferer = !(REFERRER && REFERRER.includes(APP_URL)) + const jsonLD = toJSONLD(artist, APP_URL) + + res.locals.sd.ARTIST_PAGE_CTA_ENABLED = + !user && isExternalReferer && !IS_MOBILE + res.locals.sd.ARTIST_PAGE_CTA_ARTIST_ID = req.params.artistID + + // Render layout + const layout = await stitch({ + basePath: __dirname, + layout: "../../components/main_layout/templates/react_redesign.jade", + config: { + styledComponents: true, + }, + blocks: { + head: () => ( + <> + {headTags} + + + ), + body: () => ( + + + + ), + }, + locals: { + ...res.locals, + assetPackage: "artist", + scripts, + }, + data: { + jsonLD, + }, + }) + + res.status(status).send(layout) + } catch (error) { + console.log(error) + next(error) + } + } +) diff --git a/src/desktop/apps/artsy_in_miami/components/MiamiFairWeekPage.js b/src/desktop/apps/artsy_in_miami/components/MiamiFairWeekPage.js index 186d37150b8..1a557b8c87e 100644 --- a/src/desktop/apps/artsy_in_miami/components/MiamiFairWeekPage.js +++ b/src/desktop/apps/artsy_in_miami/components/MiamiFairWeekPage.js @@ -86,7 +86,7 @@ export default ({
diff --git a/src/desktop/apps/artsy_in_miami/fixture.json b/src/desktop/apps/artsy_in_miami/fixture.json index 10d932a998b..39f3e6d0ccd 100644 --- a/src/desktop/apps/artsy_in_miami/fixture.json +++ b/src/desktop/apps/artsy_in_miami/fixture.json @@ -1,68 +1,81 @@ { + "meta": { + "title": "Art Basel and more on Artsy | Miami Week 2018", + "description": "Artsy is your guide to Miami Week 2018. Explore the fairs, editorial coverage, Artsy events, and more—all in one place. December 4-9." + }, "introduction": { - "title": "Miami Week
Dec 4-10, 2017", - "description": "For one week a year, Miami becomes a global destination for art, design, music and all things visual culture. Each fair brings together the most influential collectors, gallerists, designers, curators and critics from around the world in celebration of design culture and commerce." + "title": "Miami Art Week
Dec 4–9, 2018", + "description": "For one week a year, Miami becomes a global destination for art, design, music, and all things visual culture. Celebrate the year in art as collectors, art lovers, and curators come together for the most talked-about art fairs, openings, and events.", + "image": "https://d3vpvtm3t56z1n.cloudfront.net/images/hero.jpg" }, "fair_coverage": { - "title": "Visit the fairs", + "title": "Fair coverage launches November 28", "fairs": [ { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/artb.jpg", - "site_url": "https://www.google.com" + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/1VDIT3dxaBp8gPPti_ms9g/Group%2019%402x.png", + "site_url": "https://www.artsy.net/art-basel-in-miami-beach-2018" + }, + { + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/ZCfE2UxeGuBpXGZyzbMiFA/INK%20Miami.png", + "site_url": "https://www.artsy.net/ink-miami-2018" + }, + { + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/smTFa2LbpHyYltL5BnKpDA/Art%20Miami.png", + "site_url": "https://www.artsy.net/art-miami-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/designmiami.jpg", - "site_url": "" + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/NAsbMWiP_RGE9sxHDxUSxw/CONTEXT%20Miami.png", + "site_url": "https://www.artsy.net/context-art-miami-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/ink.jpg", - "site_url": "" + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/_MmDstUwULPLvMm4U8DiaQ/Aqua%20Miami.png", + "site_url": "https://www.artsy.net/aqua-art-miami-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/artmiami.jpg", - "site_url": "" + "logo_url": "https://artsy-media-uploads.s3.amazonaws.com/wzRMCWRdWY-grjY8dRARmw/PULSE%20Miami%20Beach.png", + "site_url": "https://www.artsy.net/pulse-miami-beach-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/context.jpg", - "site_url": "" + "logo_url": "http://files.artsy.net/images/untitled.png", + "site_url": "https://www.artsy.net/untitled-art-miami-beach-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/aqua.jpg", - "site_url": "" + "logo_url": "http://files.artsy.net/images/nadamiami.png", + "site_url": "https://www.artsy.net/nada-miami-2018" }, { - "logo_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/pulse.jpg", - "site_url": "" + "logo_url": "http://files.artsy.net/images/scopemiami.png", + "site_url": "https://www.artsy.net/scope-miami-beach-2018" } ] }, "artsy_in_miami": { - "title": "Artsy in Miami", - "banner_image_url": "https://d3vpvtm3t56z1n.cloudfront.net/images/artsyinmiami.jpg", - "description": "Collective Structures explores the relationship between individual artists and their mental landscape through a series of spatial installations. It will unfold in multiple chapters, across distinct spaces of the Bath Club, drawing on the historic building. The physical and sensory experiences strive to place viewers at a crossroads between current reality and imagined narrative.", - "public_viewing_date": "Public Viewing
December 7 12:00pm–6:00pm
The Bath Club
5937 Collins Ave, Miami Beach" + "title": null, + "banner_image_url": null, + "description": null, + "public_viewing_date": null }, "prepare_for_fairs": { - "title": "Stories from Miami", + "title": "Preparing for the fairs", "articles": [ { - "image_url": "https://d7hftxdivxxvm.cloudfront.net/?resize_to=width&src=https%3A%2F%2Fartsy-media-uploads.s3.amazonaws.com%2FlhQti2pXrsXDLlS8jJEIDg%252F_AR_3443.jpg&width=1100&quality=80", - "title": "The 20 Best Booths at Art Basel in Miami Beach", - "author": "ANNA LOUIE SUSSMAN", - "article_url": "https://www.artsy.net/article/artsy-editorial-the-20-best-booths-at-art-basel-in-miami-beach" + "article_url": "https://www.artsy.net/article/artsy-specialist-3-reasons-buy-art-fairs", + "author": "ARTSY SPECIALIST", + "image_url": "https://artsy-media-uploads.s3.amazonaws.com/6CJOf-XW90R0ML3xfUfwRQ/3reasons.jpg", + "title": "3 Reasons to Buy Art at Art Fairs" }, { - "image_url": "https://d7hftxdivxxvm.cloudfront.net/?resize_to=width&src=https%3A%2F%2Fartsy-media-uploads.s3.amazonaws.com%2FvClMRePyeu9nCashzAgEeA%252F_AR_3326.jpg&width=1100&quality=80", - "title": "50 Must-See Artworks at Miami Art Week’s Satellite Fairs", - "author": "ARTSY EDITORIAL", - "article_url": "https://www.artsy.net/article/artsy-editorial-50-must-see-artworks-at-miami-art-week-s-satellite-fairs" + "article_url": "https://www.artsy.net/article/artsy-specialist-best-deal-art-fair", + "author": "ARTSY SPECIALIST", + "image_url": "https://artsy-media-uploads.s3.amazonaws.com/4EiO7x1TI7adea0PsfYKvw/bestdeal.jpg", + "title": " How to Get the Best Deal at an Art Fair" }, { - "image_url": "https://d7hftxdivxxvm.cloudfront.net/?resize_to=width&src=https%3A%2F%2Fartsy-media-uploads.s3.amazonaws.com%2FOX8QZ5TCXVt8szczr7oWZQ%252Fammann.jpg&width=1100&quality=80", - "title": "The 10 Best Booths at Design Miami/", - "author": "ARTSY EDITORIAL", - "article_url": "https://www.artsy.net/article/artsy-editorial-the-10-best-booths-at-design-miami" + "article_url": "https://www.artsy.net/article/artsy-specialist-prepare-first-art-fair", + "author": "ARTSY SPECIALIST", + "image_url": "https://artsy-media-uploads.s3.amazonaws.com/UN_o-HYwLY0Y4M7zlgdoVQ/prepareforfairs.jpg", + "title": "How to Prepare for Your First Art Fair" } ] } -} +} \ No newline at end of file diff --git a/src/desktop/apps/artsy_in_miami/templates/meta.jade b/src/desktop/apps/artsy_in_miami/templates/meta.jade index 536e9fc48bb..1448bc7ae77 100644 --- a/src/desktop/apps/artsy_in_miami/templates/meta.jade +++ b/src/desktop/apps/artsy_in_miami/templates/meta.jade @@ -1,8 +1,5 @@ -- var title = 'Art Basel and more on Artsy | Miami Week 2017' -- var description = 'Artsy is your guide to Miami Week 2017. Explore the fairs, editorial coverage, Artsy events, and more—all in one place. December 4-10.' - -title= title -meta( property="og:title", content=title ) -meta( name="description", content=description ) -meta( name="og:description", content=description ) -meta( property="twitter:description", content=description ) \ No newline at end of file +title= data.meta.title +meta( property="og:title", content=data.meta.title ) +meta( name="description", content=data.meta.description ) +meta( name="og:description", content=data.meta.description ) +meta( property="twitter:description", content=data.meta.description ) \ No newline at end of file diff --git a/src/desktop/apps/artwork/client/bootstrap.coffee b/src/desktop/apps/artwork/client/bootstrap.coffee index 1b1cb5fe096..d82fa789362 100644 --- a/src/desktop/apps/artwork/client/bootstrap.coffee +++ b/src/desktop/apps/artwork/client/bootstrap.coffee @@ -5,3 +5,6 @@ module.exports = (sd, { artwork }) -> context: artwork.context href: artwork.href artists: artwork.artists + pageviews: artwork.pageviews + is_acquireable: artwork.is_acquireable + is_offerable: artwork.is_offerable \ No newline at end of file diff --git a/src/desktop/apps/artwork/client/index.coffee b/src/desktop/apps/artwork/client/index.coffee index 97f9e92a1fe..c25746e2c87 100644 --- a/src/desktop/apps/artwork/client/index.coffee +++ b/src/desktop/apps/artwork/client/index.coffee @@ -1,5 +1,5 @@ { extend, map, compact } = require 'underscore' -{ AUCTION, CLIENT } = require('sharify').data +{ AUCTION, CLIENT, INTERCOM_BUYER_APP_ID, INTERCOM_BUYER_ENABLED, INTERCOM_BUYER_HASH } = require('sharify').data { setCookie } = require '../../../components/recently_viewed_artworks/index.coffee' { recordArtworkView } = require '../../../../lib/components/record_artwork_view' metaphysics = require '../../../../lib/metaphysics.coffee' @@ -7,6 +7,8 @@ CurrentUser = require '../../../models/current_user.coffee' exec = require '../lib/exec.coffee' fold = -> require('./fold.jade') arguments... footer = -> require('./footer.jade') arguments... +splitTest = require '../../../components/split_test/index.coffee' +{ enableIntercom } = require '../../../../lib/intercom' helpers = extend [ {} @@ -159,9 +161,13 @@ module.exports = user: CurrentUser.orNull() init: -> + currentUser = CurrentUser.orNull() setCookie(CLIENT._id) - recordArtworkView(CLIENT._id, CurrentUser.orNull()) + recordArtworkView(CLIENT._id, currentUser) + splitTest('artwork_sidebar_pageviews').view() if CLIENT.pageviews? exec sharedInit + enableIntercom(CLIENT) + context = CLIENT.context or {} { query, init, variables } = setup(context) diff --git a/src/desktop/apps/artwork/components/auction/templates/index.jade b/src/desktop/apps/artwork/components/auction/templates/index.jade index 29788e3af61..4ef5b276b3e 100644 --- a/src/desktop/apps/artwork/components/auction/templates/index.jade +++ b/src/desktop/apps/artwork/components/auction/templates/index.jade @@ -1,7 +1,5 @@ - var auction = artwork.sale - var sale_artwork = artwork.sale_artwork -- var labFeatures = sd && sd.CURRENT_USER && sd.CURRENT_USER.lab_features - if auction.is_auction_promo //- Unimplemented @@ -11,8 +9,9 @@ else if auction.is_preview || auction.is_open || auction.is_live_open include ./live include ./bid include ./buy_now - if labFeatures && labFeatures.indexOf("Pageviews in Sidebar") > -1 - != stitch.components.ArtworkSidebarPageviews({artworkID: artwork.id}) + if sd && sd.stitch.components && sd.ARTWORK_SIDEBAR_PAGEVIEWS === 'experiment' + #stitch-pageviews-mountpoint + != sd.stitch.components.ArtworkSidebarPageviews({artworkID: artwork.id, mountId: 'stitch-pageviews-mountpoint'}) include ./buyers_premium include ./help diff --git a/src/desktop/apps/artwork/components/commercial/templates/index.jade b/src/desktop/apps/artwork/components/commercial/templates/index.jade index 60518f8e5e8..52f97ddb328 100644 --- a/src/desktop/apps/artwork/components/commercial/templates/index.jade +++ b/src/desktop/apps/artwork/components/commercial/templates/index.jade @@ -1,7 +1,6 @@ - var auctionPartner = artwork.partner.type === 'Auction' || artwork.partner.type === 'Auction House' -- var labFeatures = sd.CURRENT_USER && sd.CURRENT_USER.lab_features - var isCommercial = artwork.is_acquireable || artwork.is_offerable || artwork.is_inquireable -- var hasLabFeature = labFeatures && labFeatures.indexOf("Pageviews in Sidebar") > -1 +- var hasPageviewFeature = sd.ARTWORK_SIDEBAR_PAGEVIEWS === 'experiment' .artwork-commercial( class='js-artwork-commercial' ) form.artwork-commercial__form @@ -13,7 +12,7 @@ else if artwork.is_inquireable include inquire - if isCommercial && hasLabFeature + if isCommercial && hasPageviewFeature != stitch.components.ArtworkSidebarPageviews({artworkID: artwork.id}) include partner diff --git a/src/desktop/apps/artwork/components/commercial/templates/price.jade b/src/desktop/apps/artwork/components/commercial/templates/price.jade index 3ba2adb8c8c..4a080f1541e 100644 --- a/src/desktop/apps/artwork/components/commercial/templates/price.jade +++ b/src/desktop/apps/artwork/components/commercial/templates/price.jade @@ -10,11 +10,12 @@ if artwork.sale_message || isPermanentCollection if artwork.is_price_range .tooltip-question-container != stitch.components.TooltipQuestion({horizontalAlign: 'left', message: 'The range is an approximate indication of the work’s price point, and the exact price is quoted upon request.'}) - .artwork-commercial__shipping-info - | #{artwork.shippingInfo} - if artwork.shippingOrigin - br - | Ships from #{artwork.shippingOrigin} + if artwork.is_acquireable || artwork.is_offerable + .artwork-commercial__shipping-info + | #{artwork.shippingInfo} + if artwork.shippingOrigin + br + | Ships from #{artwork.shippingOrigin} else if isPermanentCollection | Permanent collection else diff --git a/src/desktop/apps/artwork/components/commercial/test/template.coffee b/src/desktop/apps/artwork/components/commercial/test/template.coffee index be936629339..b43ce1f3167 100644 --- a/src/desktop/apps/artwork/components/commercial/test/template.coffee +++ b/src/desktop/apps/artwork/components/commercial/test/template.coffee @@ -108,3 +108,23 @@ describe 'Commercial template', -> is_for_sale: true $ = cheerio.load html $('.js-artwork-bnmo-ask-specialist').length.should.eql 0 + + it 'shows shipping information for ecommerce artworks', -> + html = renderArtwork + artwork: acquireableArtwork + artworkOptions: + price: '$5,000' + shippingInfo: "Shipping: $50 continental US, $100 rest of world" + shippingOrigin: "New York, New York, US" + $ = cheerio.load html + $('.artwork-commercial__shipping-info').length.should.eql 1 + + it 'does not show shipping information when unenrolled from ecommerce programs', -> + html = renderArtwork + artwork: inquireableArtwork + artworkOptions: + price: '$5,000' + shippingInfo: "Shipping: $50 continental US, $100 rest of world" + shippingOrigin: "New York, New York, US" + $ = cheerio.load html + $('.artwork-commercial__shipping-info').length.should.eql 0 diff --git a/src/desktop/apps/artwork/components/metadata/query.coffee b/src/desktop/apps/artwork/components/metadata/query.coffee index f2f8101934c..ea4cc797dd4 100644 --- a/src/desktop/apps/artwork/components/metadata/query.coffee +++ b/src/desktop/apps/artwork/components/metadata/query.coffee @@ -29,5 +29,6 @@ module.exports = """ sale_artwork { lot_label } + pageviews } """ diff --git a/src/desktop/apps/artwork2/client.js b/src/desktop/apps/artwork2/client.js deleted file mode 100644 index edef302bc78..00000000000 --- a/src/desktop/apps/artwork2/client.js +++ /dev/null @@ -1,16 +0,0 @@ -import { buildClientApp } from "reaction/Artsy/Router/client" -import { routes } from "reaction/Apps/Artwork/routes" -import React from "react" -import ReactDOM from "react-dom" - -buildClientApp({ routes }) - .then(({ ClientApp }) => { - ReactDOM.hydrate(, document.getElementById("react-root")) - }) - .catch(error => { - console.error(error) - }) - -if (module.hot) { - module.hot.accept() -} diff --git a/src/desktop/apps/artwork2/client.tsx b/src/desktop/apps/artwork2/client.tsx new file mode 100644 index 00000000000..773178013a9 --- /dev/null +++ b/src/desktop/apps/artwork2/client.tsx @@ -0,0 +1,90 @@ +import { buildClientApp } from "reaction/Artsy/Router/client" +import { routes } from "reaction/Apps/Artwork/routes" +import { data as sd } from "sharify" +import React from "react" +import ReactDOM from "react-dom" +import styled from "styled-components" + +const mediator = require("desktop/lib/mediator.coffee") +const User = require("desktop/models/user.coffee") +const Artwork = require("desktop/models/artwork.coffee") +const ArtworkInquiry = require("desktop/models/artwork_inquiry.coffee") +const openInquiryQuestionnaireFor = require("desktop/components/inquiry_questionnaire/index.coffee") +const openAuctionBuyerPremium = require("desktop/apps/artwork/components/auction/components/buyers_premium/index.coffee") + +// FIXME: Move this to Reaction +const Container = styled.div` + width: 100%; + max-width: 1192px; + margin: auto; +` + +buildClientApp({ + routes, + context: { + user: sd.CURRENT_USER, + mediator, + }, +}) + .then(({ ClientApp }) => { + ReactDOM.hydrate( + + + , + document.getElementById("react-root") + ) + }) + .catch(error => { + console.error(error) + }) + +if (module.hot) { + module.hot.accept() +} + +const openInquireableModal = (artworkId: string, { ask_specialist }) => { + if (!artworkId) return + const user = User.instantiate() + const inquiry = new ArtworkInquiry({ notification_delay: 600 }) + const artwork = new Artwork({ id: artworkId }) + + artwork.fetch().then(() => { + openInquiryQuestionnaireFor({ + user, + artwork, + inquiry, + ask_specialist, + }) + }) +} + +mediator.on("launchInquiryFlow", options => { + openInquireableModal(options.artworkId, { ask_specialist: false }) +}) + +mediator.on("openBuyNowAskSpecialistModal", options => { + openInquireableModal(options.artworkId, { ask_specialist: true }) +}) + +mediator.on("openAuctionAskSpecialistModal", options => { + const artworkId = options.artworkId + if (artworkId) { + const user = User.instantiate() + const inquiry = new ArtworkInquiry({ notification_delay: 600 }) + const artwork = new Artwork({ id: artworkId }) + + artwork.fetch().then(() => { + artwork.set("is_in_auction", true) + openInquiryQuestionnaireFor({ + user, + artwork, + inquiry, + ask_specialist: true, + }) + }) + } +}) + +mediator.on("openAuctionBuyerPremium", options => { + openAuctionBuyerPremium(options.auctionId) +}) diff --git a/src/desktop/apps/artwork2/server.js b/src/desktop/apps/artwork2/server.js deleted file mode 100644 index 74c5e6ef0e5..00000000000 --- a/src/desktop/apps/artwork2/server.js +++ /dev/null @@ -1,48 +0,0 @@ -import express from "express" -import adminOnly from "desktop/lib/admin_only" -import { buildServerApp } from "reaction/Artsy/Router/server" -import { routes } from "reaction/Apps/Artwork/routes" -import { stitch } from "@artsy/stitch" -import { Meta } from "./components/Meta" -import { buildServerAppContext } from "desktop/lib/buildServerAppContext" - -const app = (module.exports = express()) - -app.get("/artwork2/:artworkID*", adminOnly, async (req, res, next) => { - try { - const { ServerApp, redirect, status } = await buildServerApp({ - routes, - url: req.url, - context: buildServerAppContext(req, res), - }) - - if (redirect) { - res.redirect(302, redirect.url) - return - } - - const layout = await stitch({ - basePath: __dirname, - layout: "../../components/main_layout/templates/react_redesign.jade", - config: { - styledComponents: true, - }, - blocks: { - head: Meta, - body: ServerApp, - }, - locals: { - ...res.locals, - assetPackage: "artwork2", - styledComponents: true, - }, - }) - - res.status(status).send(layout) - } catch (error) { - console.log(error) - next(error) - } -}) - -export default app diff --git a/src/desktop/apps/artwork2/server.tsx b/src/desktop/apps/artwork2/server.tsx new file mode 100644 index 00000000000..8d2f04ffcf7 --- /dev/null +++ b/src/desktop/apps/artwork2/server.tsx @@ -0,0 +1,75 @@ +import React from "react" +import adminOnly from "desktop/lib/admin_only" +import { buildServerApp } from "reaction/Artsy/Router/server" +import { routes } from "reaction/Apps/Artwork/routes" +import { stitch } from "@artsy/stitch" +import { Meta } from "./components/Meta" +import { buildServerAppContext } from "desktop/lib/buildServerAppContext" +import styled from "styled-components" +import express, { Request, Response, NextFunction } from "express" + +export const app = express() + +app.get( + "/artwork2/:artworkID*", + adminOnly, + async (req: Request, res: Response, next: NextFunction) => { + try { + const { + ServerApp, + redirect, + status, + headTags, + scripts, + } = await buildServerApp({ + routes, + url: req.url, + userAgent: req.header("User-Agent"), + context: buildServerAppContext(req, res), + }) + + if (redirect) { + res.redirect(302, redirect.url) + return + } + + // FIXME: Move this to Reaction + const Container = styled.div` + width: 100%; + max-width: 1192px; + margin: auto; + ` + + const layout = await stitch({ + basePath: __dirname, + layout: "../../components/main_layout/templates/react_redesign.jade", + config: { + styledComponents: true, + }, + blocks: { + head: () => ( + + {headTags} + + + ), + body: () => ( + + + + ), + }, + locals: { + ...res.locals, + assetPackage: "artwork2", + scripts, + }, + }) + + res.status(status).send(layout) + } catch (error) { + console.log(error) + next(error) + } + } +) diff --git a/src/desktop/apps/auction_support/client/registration_form.coffee b/src/desktop/apps/auction_support/client/registration_form.coffee index 8e699c4138a..c620f721937 100644 --- a/src/desktop/apps/auction_support/client/registration_form.coffee +++ b/src/desktop/apps/auction_support/client/registration_form.coffee @@ -27,6 +27,7 @@ module.exports = class RegistrationForm extends ErrorHandlingForm @$conditionsCheckbox = @$('.artsy-checkbox') @$submit = @$('.registration-form-content .avant-garde-button-black') @setUpFields() + @setUpStripe() showBiddingDialog: (e) -> e.preventDefault() @@ -37,27 +38,36 @@ module.exports = class RegistrationForm extends ErrorHandlingForm setUpFields: -> @fields = 'name on card': { el: @$('input[name=card_name]'), validator: @isPresent } - 'card number': { el: @$('input[name=card_number]'), validator: @isCardNumber } - 'security code': { el: @$('input[name=card_security_code]'), validator: @isPresent } telephone: { el: @$('input.telephone'), validator: @isPresent } - month: { el: @$('.card-expiration .month select'), validator: @isPresent } - year: { el: @$('.card-expiration .year select'), validator: @isPresent } street: { el: @$('input.street'), validator: @isPresent, label: 'address' } city: { el: @$('input.city'), validator: @isPresent, label: 'city' } state: { el: @$('input.region'), validator: @isState, label: 'state' } zip: { el: @$('input.postal-code'), validator: @isZip } @internationalizeFields() + setUpStripe: -> + @stripe = Stripe(STRIPE_PUBLISHABLE_KEY) + elements = @stripe.elements() + @card = elements.create('card', { + style: { + base: { + fontFamily: '"Adobe Garamond W08", Georgia, Serif', + fontSize: '16px', + '::placeholder': { + color: '#cccccc', + }, + } + } + }) + @card.update({ hidePostalCode: true }) + @card.mount('#card-element') + disableForm: -> @$('.auction-registration-form input, .auction-registration-form select').attr('disabled', true) @undelegateEvents() cardData: -> name: @fields['name on card'].el.val() - number: @fields['card number'].el.val() - exp_month: @fields.month.el.first().val() - exp_year: @fields.year.el.last().val() - cvc: @fields['security code'].el.val() address_line1: @fields.street.el.val() address_city: @fields.city.el.val() address_state: @fields.state.el.val() @@ -67,12 +77,12 @@ module.exports = class RegistrationForm extends ErrorHandlingForm tokenizeCard: -> Q.Promise (resolve, reject) => # Attempt to tokenize the credit card through Stripe - Stripe.setPublishableKey STRIPE_PUBLISHABLE_KEY - Stripe.card.createToken @cardData(), (status, data) -> - if status is 200 - resolve data + @stripe.createToken(@card, @cardData()).then (result) -> + if result.token + resolve result else - reject data.error.message + reject result.error.message + .then (data) => analyticsHooks.trigger 'registration:validated' @@ -80,7 +90,7 @@ module.exports = class RegistrationForm extends ErrorHandlingForm Q.Promise (resolve, reject) => card = new Backbone.Model card.url = "#{API_URL}/api/v1/me/credit_cards" - card.save { token: data.id, provider: 'stripe' }, + card.save { token: data.token.id, provider: 'stripe' }, success: (creditCard) => if creditCard.get("address_zip_check") == "fail" reject @errors.badZip @@ -88,7 +98,8 @@ module.exports = class RegistrationForm extends ErrorHandlingForm reject @errors.badSecurityCode else resolve(creditCard) - error: (m, xhr) -> reject(xhr.responseJSON?.message) + error: (m, xhr) -> + reject(xhr.responseJSON?.message) .then => # Create the "bidder" model for the user in this sale Q.Promise (resolve, reject) => diff --git a/src/desktop/apps/auction_support/templates/bid-form.jade b/src/desktop/apps/auction_support/templates/bid-form.jade index b1848bea7fa..ae315757acd 100644 --- a/src/desktop/apps/auction_support/templates/bid-form.jade +++ b/src/desktop/apps/auction_support/templates/bid-form.jade @@ -2,7 +2,7 @@ extends ../../../components/main_layout/templates/minimal_header append locals - assetPackage = 'auctions' - - options = {stripe: true} + - options = {stripev3: true} block body #auction-registration-page @@ -46,7 +46,7 @@ block body else include ./conditions-of-sale-checkbox.jade .auction-bid-form-confirm.avant-garde-button-black.is-block.is-disabled Confirm Bid - + else .bid-section-header= sd.BIDDER_H1_COPY p.note= sd.BIDDER_H2_COPY diff --git a/src/desktop/apps/auction_support/templates/registration.jade b/src/desktop/apps/auction_support/templates/registration.jade index b07f9e20d88..c84800edbff 100644 --- a/src/desktop/apps/auction_support/templates/registration.jade +++ b/src/desktop/apps/auction_support/templates/registration.jade @@ -2,7 +2,7 @@ extends ../../../components/main_layout/templates/minimal_header append locals - assetPackage = 'auctions' - - options = {stripe: true} + - options = {stripev3: true} block body #auction-registration-page diff --git a/src/desktop/apps/auction_support/test/client/registration_form.coffee b/src/desktop/apps/auction_support/test/client/registration_form.coffee index c2f92cda8f5..4723ad20749 100644 --- a/src/desktop/apps/auction_support/test/client/registration_form.coffee +++ b/src/desktop/apps/auction_support/test/client/registration_form.coffee @@ -10,15 +10,20 @@ RegistrationForm = require '../../client/registration_form' DateHelpers = require '../../../../components/util/date_helpers.coffee' describe 'RegistrationForm', -> - before (done) -> benv.setup => benv.expose $: benv.require('jquery'), Stripe: @Stripe = - setPublishableKey: sinon.stub() - card: - createToken: sinon.stub().yields(200, {}) + sinon.stub().returns({ + createToken: sinon.stub().returns(Promise.resolve({ token: { id: '123' } })) + elements: sinon.stub().returns({ + create: sinon.stub().returns({ + update: sinon.stub() + mount: sinon.stub() + }) + }) + }) Backbone.$ = $ done() @@ -53,10 +58,6 @@ describe 'RegistrationForm', -> @acceptConditions = => @view.$acceptConditions.prop('checked', true) @submitValidForm = => @view.$('input[name="card_name"]').val 'Foo Bar' - @view.$('select[name="card_expiration_month"]').val '1' - @view.$('select[name="card_expiration_year"]').val '2024' - @view.$('input[name="card_number"]').val '4111111111111111' - @view.$('input[name="card_security_code"]').val '123' @view.$('input[name="address[street]"]').val '456 Foo Bar Ln.' @view.$('input[name="address[city]"]').val 'Foobarrington' @view.$('input[name="address[region]"]').val 'FB' @@ -79,14 +80,14 @@ describe 'RegistrationForm', -> done() it 'validates the form and displays errors', -> + @acceptConditions() + @view.$submit.length.should.be.ok() @view.$submit.click() @view.once 'submitted', => html = @view.$el.html() html.should.containEql 'Invalid name on card' - html.should.containEql 'Invalid card number' - html.should.containEql 'Invalid security code' html.should.containEql 'Invalid city' html.should.containEql 'Invalid state' html.should.containEql 'Invalid zip' @@ -95,33 +96,29 @@ describe 'RegistrationForm', -> @view.$submit.hasClass('is-loading').should.be.false() it 'lets the user resubmit a corrected form', -> - # Submit a bad form + @acceptConditions() + # Submit a bad form @view.$submit.length.should.be.ok() @view.$submit.click() + @view.once "submitted", => html = @view.$el.html() html.should.containEql 'Please review the error(s) above and try again.' - # Now submit a good one - - # Successfully create a stripe token - @Stripe.card.createToken.callsArgWith(1, 200, {}) - # Successfully save phone number - Backbone.sync.onFirstCall().yieldsTo('success') - # Successfully save credit card - Backbone.sync.onSecondCall().yieldsTo('success') - # Successfully create the bidder - Backbone.sync.onThirdCall().yieldsTo('success') - + Backbone.sync + .yieldsTo 'success', {} # savePhoneNumber success + .onCall 1 + .yieldsTo 'success', { get: () -> 'pass' } # credit card save passes + .onCall 2 + .yieldsTo 'success', {} - @acceptConditions() + # Now submit a good one @submitValidForm() - @view.once "submitted", => - @Stripe.card.createToken.args[0][1](200, {}) + @view.once "submitted", => # Saves the phone number - Backbone.sync.args[0][1].changed.phone.should.equal '555-555-5555' + Backbone.sync.args[0][2].attrs.phone.should.equal '555-555-5555' # Saves the credit card Backbone.sync.args[1][1].url.should.containEql '/api/v1/me/credit_cards' diff --git a/src/desktop/apps/collect2/meta.tsx b/src/desktop/apps/collect2/meta.tsx index 0640dd6e737..0a4caec03af 100644 --- a/src/desktop/apps/collect2/meta.tsx +++ b/src/desktop/apps/collect2/meta.tsx @@ -2,7 +2,7 @@ import React, { Fragment } from "react" interface Props { appUrl: string - headTags: JSX.Element + headTags: JSX.Element[] } export const Meta = (props: Props) => { diff --git a/src/desktop/apps/collect2/server.js b/src/desktop/apps/collect2/server.tsx similarity index 87% rename from src/desktop/apps/collect2/server.js rename to src/desktop/apps/collect2/server.tsx index ece2d44ac2b..201c0fea9e5 100644 --- a/src/desktop/apps/collect2/server.js +++ b/src/desktop/apps/collect2/server.tsx @@ -6,16 +6,16 @@ import React from "react" import { Meta } from "./meta" import { buildServerAppContext } from "desktop/lib/buildServerAppContext" -const app = (module.exports = express()) +export const app = express() const index = async (req, res, next) => { try { - const user = req.user && req.user.toJSON() const { APP_URL, IS_MOBILE } = res.locals.sd - const { headTags, ServerApp, redirect } = await buildServerApp({ + const { headTags, ServerApp, redirect, scripts } = await buildServerApp({ routes, url: req.url, + userAgent: req.header("User-Agent"), context: buildServerAppContext(req, res), }) @@ -39,6 +39,7 @@ const index = async (req, res, next) => { ...res.locals, assetPackage: "collect2", bodyClass: IS_MOBILE ? "body-header-fixed body-no-margins" : null, + scripts, }, }) @@ -52,5 +53,3 @@ const index = async (req, res, next) => { app.get("/collect", index) app.get("/collect/:medium?", index) app.get("/collection/:slug", index) - -export default app diff --git a/src/desktop/apps/collections/server.js b/src/desktop/apps/collections/server.tsx similarity index 78% rename from src/desktop/apps/collections/server.js rename to src/desktop/apps/collections/server.tsx index 747212eec8d..16fa3497920 100644 --- a/src/desktop/apps/collections/server.js +++ b/src/desktop/apps/collections/server.tsx @@ -1,19 +1,20 @@ +import React from "react" import { buildServerApp } from "reaction/Artsy/Router/server" import { stitch } from "@artsy/stitch" import { routes } from "reaction/Apps/Collections/routes" import express from "express" import { buildServerAppContext } from "desktop/lib/buildServerAppContext" -import adminOnly from "desktop/lib/admin_only" -const app = (module.exports = express()) +export const app = express() -app.get("/collections", adminOnly, async (req, res, next) => { +app.get("/collections", async (req, res, next) => { try { const { IS_MOBILE } = res.locals.sd - const { ServerApp, redirect } = await buildServerApp({ + const { ServerApp, headTags, redirect, scripts } = await buildServerApp({ routes, url: req.url, + userAgent: req.header("User-Agent"), context: buildServerAppContext(req, res), }) @@ -30,13 +31,14 @@ app.get("/collections", adminOnly, async (req, res, next) => { styledComponents: true, }, blocks: { - head: () => null, + head: () => <>{headTags}, body: ServerApp, }, locals: { ...res.locals, assetPackage: "collections", bodyClass: IS_MOBILE ? "body-header-fixed body-no-margins" : null, + scripts, }, }) @@ -46,5 +48,3 @@ app.get("/collections", adminOnly, async (req, res, next) => { next(error) } }) - -export default app diff --git a/src/desktop/apps/fairs/helpers/view_helpers.coffee b/src/desktop/apps/fairs/helpers/view_helpers.coffee index eebc1897447..f12b10f9c35 100644 --- a/src/desktop/apps/fairs/helpers/view_helpers.coffee +++ b/src/desktop/apps/fairs/helpers/view_helpers.coffee @@ -1,5 +1,6 @@ DateHelpers = require '../../../components/util/date_helpers.coffee' _ = require 'underscore' +moment = require 'moment' module.exports = @@ -52,13 +53,15 @@ module.exports = fair.is_published and fair.profile?.is_published and fair.profile?.icon?.url isNotOver: (fair) -> - Date.parse(fair.end_at) > new Date + not @isOver(fair) isPast: (fair) -> @isEligible(fair) and @isOver(fair) isOver: (fair) -> - Date.parse(fair.end_at) < new Date + endOfFair = moment.utc(fair.end_at).endOf("day") + now = moment() + now.isAfter(endOfFair) isUpcoming: (fair) -> @isEventuallyEligible(fair) and @isNotOver(fair) diff --git a/src/desktop/apps/fairs/test/routes.coffee b/src/desktop/apps/fairs/test/routes.coffee index 9598d99d051..bb77350efcf 100644 --- a/src/desktop/apps/fairs/test/routes.coffee +++ b/src/desktop/apps/fairs/test/routes.coffee @@ -12,15 +12,12 @@ describe 'Fairs routes', -> image = { url: "https://www.example.com/cat.jpg" } profile = { is_published: true, icon: { url: "https://www.example.com/cat.jpg" } } @currentFairs = _.times 2, -> - fabricate('fair', image: image, profile: profile, id: _.uniqueId(), is_published: true, has_full_feature: true, has_listing: true, organizer: fabricate('fair_organizer'), end_at: moment().add(10, 'days').format(), banner_size: 'x-large') + fabricate('fair', image: image, profile: profile, id: _.uniqueId(), is_published: true, has_full_feature: true, has_listing: true, organizer: fabricate('fair_organizer'), start_at: moment().subtract(1, 'days'), end_at: moment().add(11, 'days').format(), banner_size: 'x-large') @pastFairs = _.times 4, -> - fabricate('fair', image: image, profile: profile, id: _.uniqueId('past'), is_published: true, has_full_feature: true, has_listing: true, organizer: fabricate('fair_organizer'), end_at: moment().subtract(10, 'days').format()) + fabricate('fair', image: image, profile: profile, id: _.uniqueId('past'), is_published: true, has_full_feature: true, has_listing: true, organizer: fabricate('fair_organizer'), end_at: moment().subtract(11, 'days').format()) @upcomingFairs = _.times 3, -> - fabricate('fair', id: _.uniqueId('upcoming'), is_published: true, has_full_feature: true, has_listing: true, organizer: null, end_at: moment().add(10, 'days')) - @invalidFairs = [ - fabricate('fair', id: _.uniqueId('invalid'), is_published: false) - fabricate('fair', id: _.uniqueId('invalid'), is_published: true, has_full_feature: false, has_listing: false) - ] + fabricate('fair', id: _.uniqueId('upcoming'), is_published: true, has_full_feature: true, has_listing: true, organizer: null, end_at: moment().add(11, 'days'), start_at: moment().add(1, 'days')) + @rows = ViewHelpers.fillRows @currentFairs @@ -37,7 +34,6 @@ describe 'Fairs routes', -> @currentFairs @pastFairs @upcomingFairs - @invalidFairs ] routes.__set__ 'metaphysics', => Q.resolve { featured_fairs: [ fairs: {} ], fairs: @fairs } @@ -54,7 +50,6 @@ describe 'Fairs routes', -> @fairs = _.flatten [ @pastFairs @upcomingFairs - @invalidFairs ] routes.__set__ 'metaphysics', => Q.resolve { featured_fairs: [ fairs: {} ], fairs: @fairs } diff --git a/src/desktop/apps/gallery_partnerships/client/index.coffee b/src/desktop/apps/gallery_partnerships/client/index.coffee index 59d50870f9a..fb53da766b7 100644 --- a/src/desktop/apps/gallery_partnerships/client/index.coffee +++ b/src/desktop/apps/gallery_partnerships/client/index.coffee @@ -1,5 +1,5 @@ initCarousel = require '../../../components/merry_go_round/bottom_nav_mgr.coffee' -sd = require('sharify').data +{ CURRENT_USER, INTERCOM_SELLER_APP_ID, INTERCOM_SELLER_ENABLED } = require('sharify').data module.exports.init = -> if sd.USER_AGENT.match('iphone') @@ -8,10 +8,11 @@ module.exports.init = -> $('.gallery-partnerships2__chat__video').hide() $('.gallery-partnerships2__chat__fallback_image').show() - isUserAdmin = sd?.CURRENT_USER?.type == 'Admin' + isUserAdmin = CURRENT_USER?.type == 'Admin' - if sd.INTERCOM_ENABLED && sd.INTERCOM_APP_ID && !isUserAdmin - intercom = require('./intercom') + if INTERCOM_SELLER_ENABLED && INTERCOM_SELLER_APP_ID && !isUserAdmin + { intercom } = require('../../../../lib/components/intercom/index') + intercom(INTERCOM_SELLER_APP_ID) initCarousel $('.js-partner-stats-slideshow'), { autoPlay: 2500, wrapAround: true, draggable: false } initCarousel $('.js-partner-testimonials-slideshow'), { autoPlay: 8000, wrapAround: true, prevNextButtons: false } diff --git a/src/desktop/apps/isomorphic-relay-example/server.js b/src/desktop/apps/isomorphic-relay-example/server.tsx similarity index 71% rename from src/desktop/apps/isomorphic-relay-example/server.js rename to src/desktop/apps/isomorphic-relay-example/server.tsx index b480737aef7..760fb7e8144 100644 --- a/src/desktop/apps/isomorphic-relay-example/server.js +++ b/src/desktop/apps/isomorphic-relay-example/server.tsx @@ -1,3 +1,4 @@ +import React from "react" import express from "express" import adminOnly from "desktop/lib/admin_only" import { buildServerApp } from "reaction/Artsy/Router/server" @@ -6,13 +7,20 @@ import { stitch } from "@artsy/stitch" import { Meta } from "./components/Meta" import { buildServerAppContext } from "desktop/lib/buildServerAppContext" -const app = (module.exports = express()) +export const app = express() app.get("/isomorphic-relay-example*", adminOnly, async (req, res, next) => { try { - const { ServerApp, redirect, status } = await buildServerApp({ - routes, + const { + ServerApp, + headTags, + redirect, + status, + scripts, + } = await buildServerApp({ + routes: routes as any, url: req.url, + userAgent: req.header("User-Agent"), context: buildServerAppContext(req, res), }) @@ -28,13 +36,18 @@ app.get("/isomorphic-relay-example*", adminOnly, async (req, res, next) => { styledComponents: true, }, blocks: { - head: Meta, + head: () => ( + + {headTags} + + + ), body: ServerApp, }, locals: { ...res.locals, assetPackage: "relay", - styledComponents: true, + scripts, }, }) diff --git a/src/desktop/apps/order2/client.js b/src/desktop/apps/order2/client.js index eb52c3733b1..c5bfd4dba3d 100644 --- a/src/desktop/apps/order2/client.js +++ b/src/desktop/apps/order2/client.js @@ -44,11 +44,12 @@ orderCheckoutFlowEvents.map(eventName => { ) // Reset timers that track time on page since we're tracking each order // checkout view as a separate page. - window.desktopPageTimeTrackers.forEach(tracker => { - // No need to reset the tracker if we're on the same page. - if (window.location.pathname !== tracker.path) - tracker.reset(window.location.pathname) - }) + typeof window.desktopPageTimeTrackers !== "undefined" && + window.desktopPageTimeTrackers.forEach(tracker => { + // No need to reset the tracker if we're on the same page. + if (window.location.pathname !== tracker.path) + tracker.reset(window.location.pathname) + }) }) }) diff --git a/src/desktop/apps/order2/routes.js b/src/desktop/apps/order2/routes.tsx similarity index 90% rename from src/desktop/apps/order2/routes.js rename to src/desktop/apps/order2/routes.tsx index 7e1abeee56f..ce7b25423fa 100644 --- a/src/desktop/apps/order2/routes.js +++ b/src/desktop/apps/order2/routes.tsx @@ -4,7 +4,7 @@ import { buildServerApp } from "reaction/Artsy/Router/server" import { buildServerAppContext } from "desktop/lib/buildServerAppContext" import { routes } from "reaction/Apps/Order/routes" import { stitch } from "@artsy/stitch" -import metaphysics from "lib/metaphysics.coffee" +const metaphysics = require("lib/metaphysics.coffee") export const checkoutFlow = async (req, res, next) => { if (!res.locals.sd.CURRENT_USER) { @@ -13,9 +13,16 @@ export const checkoutFlow = async (req, res, next) => { ) } try { - const { ServerApp, redirect, status, headTags } = await buildServerApp({ + const { + ServerApp, + redirect, + status, + headTags, + scripts, + } = await buildServerApp({ routes, url: req.url, + userAgent: req.header("User-Agent"), context: buildServerAppContext(req, res), }) @@ -58,6 +65,7 @@ export const checkoutFlow = async (req, res, next) => { options: { stripev3: true, }, + scripts, }, }) diff --git a/src/desktop/assets/artwork2.styl b/src/desktop/assets/artwork2.styl new file mode 100644 index 00000000000..5f0b0cf4a0c --- /dev/null +++ b/src/desktop/assets/artwork2.styl @@ -0,0 +1,4 @@ +@require '../components/buyers_premium' + +.modalize-body + padding 30px \ No newline at end of file diff --git a/src/desktop/components/credit_card/client/error_handling_form.coffee b/src/desktop/components/credit_card/client/error_handling_form.coffee index 27f43519c40..1f05850f435 100644 --- a/src/desktop/components/credit_card/client/error_handling_form.coffee +++ b/src/desktop/components/credit_card/client/error_handling_form.coffee @@ -15,7 +15,7 @@ module.exports = class ErrorHandlingForm extends Backbone.View paymentError: "Your payment could not be processed. Please try again or contact support." badZip: "The ZIP code provided did not match your card number. Please check it again, try another card, or contact support." badSecurityCode: "The security code provided did not match your card number. Please check it again, try another card, or contact support." - other: "There was a problem processing your order. Please try another card or contact support." + other: "There was a problem processing your request. Please try another card or contact support." timeout: "Processing your payment took too long. Please try again or contact support." connection: "Please check your network connectivity and try again." @@ -35,7 +35,7 @@ module.exports = class ErrorHandlingForm extends Backbone.View @filterHtml() @clearErrors() for own key, val of @fields - continue unless val.el.is(':visible') && !val.validator(val.el) + continue unless val.el && !val.validator(val.el) errors[key] = val.message || "Invalid #{val.label || key}" val.el.addClass 'has-error' val.el.last().after "
#{errors[key]}
" diff --git a/src/desktop/components/credit_card/stylesheets/index.styl b/src/desktop/components/credit_card/stylesheets/index.styl index 0a2af75d9cc..d293f52ac47 100644 --- a/src/desktop/components/credit_card/stylesheets/index.styl +++ b/src/desktop/components/credit_card/stylesheets/index.styl @@ -22,15 +22,12 @@ width 100% .order-input-section margin-bottom 12px - &.region, &.city, &.postal-code, &.card-name, &.card-expiration, &.card-number, &.card-security-code + &.region, &.city, &.postal-code, &.card-number display inline-block vertical-align top - &.city, &.card-name, &.card-number, &.card-security-code + &.city, &.card-number width: 49% margin-right: 3% - &.card-security-code - width: 48% - margin-right: 0% &.region width: 23% margin-right: 2% @@ -38,21 +35,6 @@ width: 23% &.email margin-bottom: 30px - &.card-expiration - width: 48% - .month, .year - position relative - height 51px - display inline-block - vertical-align top - width 47% - .month - margin-right: 6% - .error - position absolute - top 4px - bottom 0px - left 0px .order-form-button width: 100% .order-form-checkbox @@ -61,6 +43,18 @@ margin-top: 4px vertical-align: top +#card-element + border 2px solid gray-lighter-color + box-sizing border-box + width 100% + padding 10px + transition border-color 0.25s + height 44px + +#card-element.StripeElement--focus + border-color purple-color + outline none + .not-usa .order-form .order-input-section &.region diff --git a/src/desktop/components/credit_card/templates/credit_card.jade b/src/desktop/components/credit_card/templates/credit_card.jade index d1ee5cf90fc..e9806071b21 100644 --- a/src/desktop/components/credit_card/templates/credit_card.jade +++ b/src/desktop/components/credit_card/templates/credit_card.jade @@ -2,23 +2,6 @@ label( for='card_name' ) Name on Card input.bordered-input( type='text', name='card_name', value=name, autocomplete='cc-name' ) -.card-expiration.order-input-section - label( for='card_expiration' ) Expiration - .month - select( name='card_expiration_month', autocomplete="cc-exp-month" ) - option( value='' ) Month - each month in monthRange - option( value=month )!= month - .year - select( name='card_expiration_year', autocomplete="cc-exp-year" ) - option( value='' ) Year - each year in yearRange - option( value=year )!= year - -.card-number.order-input-section - label( for='card_number' ) Credit Card Number - input.bordered-input( type='text', name='card_number', value=number, autocomplete="cc-number", pattern="\d*" ) - -.card-security-code.order-input-section - label( for='card_security_code' ) Security Code - input.bordered-input( type='text', name='card_security_code', value=security_code, autocomplete="cc-csc", pattern="\d*" ) +label Credit Card Number +.order-input-section + #card-element diff --git a/src/desktop/components/credit_card_2/icons.json b/src/desktop/components/credit_card_2/icons.json deleted file mode 100644 index 37fbd519b05..00000000000 --- a/src/desktop/components/credit_card_2/icons.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "American Express": "/cc_American-Express.png", - "Diners Club": "/cc_Diners-Club.png", - "Discover": "/cc_Discover.png", - "JCB": "/cc_JCB.png", - "MasterCard": "/cc_MasterCard.png", - "Visa": "/cc_Visa.png", - "Unknown": "/cc_Unknown.png" -} diff --git a/src/desktop/components/credit_card_2/index.jade b/src/desktop/components/credit_card_2/index.jade deleted file mode 100644 index 146b99dc01e..00000000000 --- a/src/desktop/components/credit_card_2/index.jade +++ /dev/null @@ -1,13 +0,0 @@ -form.credit-card-form__form.stacked-form - .form-errors( class='js-form-errors' ) - //- Rendered client-side - - include ./templates/credit_card - - h3.credit-card-form__form__subheadline - | Billing Address - - include ./templates/billing_address - - button.avant-garde-button-white - | Add Payment diff --git a/src/desktop/components/credit_card_2/index.styl b/src/desktop/components/credit_card_2/index.styl deleted file mode 100644 index 33fb4eac8e2..00000000000 --- a/src/desktop/components/credit_card_2/index.styl +++ /dev/null @@ -1,47 +0,0 @@ -@require '../grid' -@require './modal' - -.credit-card-bordered-input - position relative - display inline-block - - &.is-block - &, & > .bordered-input - display block - width 100% - - > .bordered-input - padding-right 30px - - > span - position absolute - display block - top 50% - right 10px - width 100px - height 30px - background-size contain - background-position center right - background-repeat no-repeat - transform translateY(-50%) - pointer-events none - -.credit-card-form - margin 0 auto - - &__form - // - &__subheadline - margin gutter 0 - padding 10px 0 - border-top 1px solid gray-lighter-color - avant-garde-size('s-headline') - -icons = json('./icons.json', { hash: true }) - -.credit-card-icon - background-size contain - background-repeat no-repeat - for provider, src in icons - &[data-provider=\'{provider}\'] - background-image url(src) diff --git a/src/desktop/components/credit_card_2/modal/index.coffee b/src/desktop/components/credit_card_2/modal/index.coffee deleted file mode 100644 index 61f31936498..00000000000 --- a/src/desktop/components/credit_card_2/modal/index.coffee +++ /dev/null @@ -1,14 +0,0 @@ -modalize = require '../../modalize/index.coffee' -CreditCardView = require './view.coffee' - -module.exports = ({ collection } = {}) -> - view = new CreditCardView collection: collection - - modal = modalize view, - dimensions: width: '800px', height: '800px' - - view.once 'abort done', -> - modal.close() - - modal.open() - modal diff --git a/src/desktop/components/credit_card_2/modal/index.jade b/src/desktop/components/credit_card_2/modal/index.jade deleted file mode 100644 index 4a7dc4a5f69..00000000000 --- a/src/desktop/components/credit_card_2/modal/index.jade +++ /dev/null @@ -1,20 +0,0 @@ -header.credit-card-modal__header - h1 Add Credit Card - -form.credit-card-modal__form - .credit-card-modal__form__fields.stacked-form - .form-errors( class='js-form-errors' ) - //- Rendered client-side - - include ../templates/credit_card - - h3.credit-card-modal__form__fields__subheadline - | Billing Address - - include ../templates/billing_address - - .credit-card-modal__form__buttons - a.avant-garde-button-white( class='js-cancel' ) - | Cancel - button.avant-garde-button-black - | Add diff --git a/src/desktop/components/credit_card_2/modal/index.styl b/src/desktop/components/credit_card_2/modal/index.styl deleted file mode 100644 index d3e65d28299..00000000000 --- a/src/desktop/components/credit_card_2/modal/index.styl +++ /dev/null @@ -1,60 +0,0 @@ -credit-card-modal = { - header: { - height: 70px - } -} - -.credit-card-modal - position relative - height 100% - - &__header - garamond-size('headline') - position absolute - top 0 - right 0 - left 0 - height: credit-card-modal.header.height - line-height @height - padding 0 gutter - border-bottom 1px solid gray-lighter-color - - &__form - position absolute - top: credit-card-modal.header.height - right 0 - bottom 0 - left 0 - - &__fields - position absolute - margin: 0 0 credit-card-modal.header.height 0 - padding gutter 25% - top 0 - right 0 - bottom 0 - left 0 - overflow-y auto - -webkit-overflow-scrolling touch - - &__subheadline - avant-garde-size('s-headline') - margin gutter 0 - padding 10px 0 - border-top 1px solid gray-lighter-color - - &__buttons - position absolute - display flex - justify-content flex-end - align-items center - height: credit-card-modal.header.height - right 0 - bottom 0 - left 0 - padding 0 gutter - border-top 1px solid gray-lighter-color - - > a - > button - inline-block-margins(gutter / 4) diff --git a/src/desktop/components/credit_card_2/modal/view.coffee b/src/desktop/components/credit_card_2/modal/view.coffee deleted file mode 100644 index 3d53b3b700b..00000000000 --- a/src/desktop/components/credit_card_2/modal/view.coffee +++ /dev/null @@ -1,39 +0,0 @@ -{ invoke } = require 'underscore' -Backbone = require 'backbone' -CreditCardView = require '../view.coffee' -template = -> require('./index.jade') arguments... - -module.exports = class CreditCardModalView extends Backbone.View - className: 'credit-card-modal' - - events: - 'click .js-cancel': 'cancel' - - initialize: -> - @listenTo @collection, 'add', @done - - done: (model) -> - model.once 'sync', => - @trigger 'done' - - cancel: (e) -> - e.preventDefault() - @trigger 'abort' - - postRender: -> - creditCardView = new CreditCardView - el: @$el - collection: @collection - - @subViews = [ - creditCardView - ] - - render: -> - @$el.html template() - @postRender() - this - - remove: -> - invoke @subViews, 'remove' - super diff --git a/src/desktop/components/credit_card_2/public/cc_American-Express.png b/src/desktop/components/credit_card_2/public/cc_American-Express.png deleted file mode 100755 index 27258a4247b..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_American-Express.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_Diners-Club.png b/src/desktop/components/credit_card_2/public/cc_Diners-Club.png deleted file mode 100755 index d45b27cdaf4..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_Diners-Club.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_Discover.png b/src/desktop/components/credit_card_2/public/cc_Discover.png deleted file mode 100755 index 800dcffe9d0..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_Discover.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_JCB.png b/src/desktop/components/credit_card_2/public/cc_JCB.png deleted file mode 100755 index 6c0885e3143..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_JCB.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_MasterCard.png b/src/desktop/components/credit_card_2/public/cc_MasterCard.png deleted file mode 100755 index f30ea66be96..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_MasterCard.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_Unknown.png b/src/desktop/components/credit_card_2/public/cc_Unknown.png deleted file mode 100644 index 1664218eea4..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_Unknown.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/public/cc_Visa.png b/src/desktop/components/credit_card_2/public/cc_Visa.png deleted file mode 100755 index 7f4df2ce9a9..00000000000 Binary files a/src/desktop/components/credit_card_2/public/cc_Visa.png and /dev/null differ diff --git a/src/desktop/components/credit_card_2/templates/billing_address.jade b/src/desktop/components/credit_card_2/templates/billing_address.jade deleted file mode 100644 index 6f45cb535e5..00000000000 --- a/src/desktop/components/credit_card_2/templates/billing_address.jade +++ /dev/null @@ -1,44 +0,0 @@ -//- Stripe fields (not Gravity) - -input.bordered-input( - id='address_line1' - name='address_line1' - placeholder='Address' -) - -input.bordered-input( - name='address_line2' - placeholder='Address (cont.)' -) - -if countries && countries.length - label.bordered-select - select( id='address_country', name='address_country' ) - for country in countries - option( value= country ) - = country -else - input.bordered-input( - id='address_country' - name='address_country' - placeholder='Country' - ) - -input.bordered-input( - id='address_city' - name='address_city' - placeholder='City' -) - -input.bordered-input( - id='address_state' - name='address_state' - placeholder='State' -) - -.grid-2-up - .grid-item: input.bordered-input( - id='address_zip' - name='address_zip' - placeholder='Zip' - ) diff --git a/src/desktop/components/credit_card_2/templates/credit_card.jade b/src/desktop/components/credit_card_2/templates/credit_card.jade deleted file mode 100644 index 25a4be0e237..00000000000 --- a/src/desktop/components/credit_card_2/templates/credit_card.jade +++ /dev/null @@ -1,40 +0,0 @@ -//- Credit card-related fields intentionally lack a name attribute -//- to prevent sensitive data from being inadvertently -//- submitted to our servers. - -.stacked-form-cell - .credit-card-bordered-input.is-block - input.bordered-input( - id='cc-number' - class='js-cc-number' - data-stripe='number' - placeholder='Credit Card Number' - autocomplete='cc-number' - required - ) - span.credit-card-bordered-input__type.credit-card-icon( - class='js-cc-type' - ) - -.grid-3-up - .grid-item - input.bordered-input.is-block( - id='exp' - data-stripe='exp' - placeholder='MM/YYYY' - required - ) - .grid-item - input.bordered-input.is-block( - id='cvc' - data-stripe='cvc' - placeholder='CVC' - required - ) - -input.bordered-input( - id='name' - name='name' - placeholder='Name on Credit Card' - required -) diff --git a/src/desktop/components/credit_card_2/test/view.coffee b/src/desktop/components/credit_card_2/test/view.coffee deleted file mode 100644 index d11e0bca505..00000000000 --- a/src/desktop/components/credit_card_2/test/view.coffee +++ /dev/null @@ -1,85 +0,0 @@ -benv = require 'benv' -sinon = require 'sinon' -Backbone = require 'backbone' -CurrentUser = require '../../../models/current_user' -CreditCardView = benv.requireWithJadeify require.resolve('../view.coffee'), ['template'] -stripe = CreditCardView.__get__ 'stripe' -CreditCardView.__set__ 'jQueryPayment', sinon.stub() - -describe 'CreditCardView', -> - before (done) -> - benv.setup -> - benv.expose $: benv.require('jquery'), jQuery: benv.require('jquery') - Backbone.$ = $ - done() - - after -> - benv.teardown() - - beforeEach -> - sinon.stub stripe, 'initialize' - sinon.stub CreditCardView::, 'postRender' - - @user = new CurrentUser id: 'foobar' - @view = new CreditCardView collection: @user.related().creditCards - - afterEach -> - stripe.initialize.restore() - @view.postRender.restore() - - describe '#render', -> - it 'renders the template', -> - html = @view.render().$el.html() - html.should.containEql 'Credit Card Number' - html.should.containEql 'Name on Credit Card' - html.should.containEql 'Add Payment' - - describe '#submit', -> - beforeEach -> - sinon.stub Backbone, 'sync' - - @view.render() - - sinon.stub @view, 'validate' - - afterEach -> - Backbone.sync.restore() - stripe.tokenize.restore() - - describe 'successful', -> - beforeEach -> - sinon.stub stripe, 'tokenize' - .returns Promise.resolve id: 'a_token' - - it 'submits the form to the server when submitted/clicked', -> - @view.$('input[data-stripe="number"]') - .val '666666666666666666' - - @view.$('button').click() - - @view.__submit__.then => - stripe.tokenize.args[0][0].number - .should.equal '666666666666666666' - - Backbone.sync.called - .should.be.true() - - Backbone.sync.args[0][0] - .should.equal 'create' - - Backbone.sync.args[0][1].toJSON() - .should.eql - token: 'a_token' - provider: 'stripe' - - describe 'error state', -> - beforeEach -> - sinon.stub stripe, 'tokenize' - .returns Promise.reject 'Stripe error string' - - it 'renders any errors', -> - @view.$('button').click() - - @view.__submit__.then => - @view.$('.js-form-errors').text() - .should.equal 'Stripe error string' diff --git a/src/desktop/components/credit_card_2/view.coffee b/src/desktop/components/credit_card_2/view.coffee deleted file mode 100644 index c1461751811..00000000000 --- a/src/desktop/components/credit_card_2/view.coffee +++ /dev/null @@ -1,80 +0,0 @@ -{ extend } = require 'underscore' -Backbone = require 'backbone' -{ Countries } = require 'places' -Form = require '../form/index.coffee' -stripe = require '../stripe/index.coffee' -jQueryPayment = -> require 'jquery.payment' -template = -> require('./index.jade') arguments... - -module.exports = class CreditCardView extends Backbone.View - className: 'credit-card-form' - - events: - 'input .js-cc-number': 'type' - 'click button': 'submit' - 'input input': 'change' - - initialize: -> - stripe.initialize() - jQueryPayment() - - type: (e) -> - number = $(e.currentTarget).val() - provider = stripe.cardType number - (@$type ?= @$('.js-cc-type')) - .attr 'data-provider', provider - - change: -> - return if @__changed__ - - @__changed__ = yes - - @$('button') - .removeClass 'avant-garde-button-white' - .addClass 'avant-garde-button-black' - - validate: (sensitive, validator) -> - stripe.validate(sensitive).map ({ name, value }) -> - $input = @$("[data-stripe='#{name}']") - if value - validator.clearValidity $input - else - validator.setValidity $input, stripe.error(name) - - submit: (e) -> - e.preventDefault() - - form = new Form $form: $form = @$('form') - - sensitive = stripe.serialize $form - @validate sensitive, form.validator - - return unless form.isReady() - - form.state 'loading' - - data = extend {}, sensitive, form.data() - - @__submit__ = stripe.tokenize data - .then ({ id }) => - @collection.create { token: id, provider: 'stripe' }, - success: -> form.state 'default' # `create` returns model, not Promise - - .catch (err) -> - form.error err - - postRender: -> - @$('[data-stripe="number"]') - .payment 'formatCardNumber' - - @$('[data-stripe="exp"]') - .payment 'formatCardExpiry' - - @$('[data-stripe="cvc"]') - .payment 'formatCardCVC' - - render: -> - @$el.html template - countries: Countries - @postRender() - this diff --git a/src/desktop/components/json_page/client/editor.coffee b/src/desktop/components/json_page/client/editor.coffee index 7915305f539..94486bad321 100644 --- a/src/desktop/components/json_page/client/editor.coffee +++ b/src/desktop/components/json_page/client/editor.coffee @@ -1,7 +1,12 @@ _ = require 'underscore' require 'hulk-editor' require 'jquery-ui' -require 'blueimp-file-upload' + +# FIXME: When running `test/client.coffee` jQuery dep does not resolve. Seems to +# be issue related to UMD and benv. This dep isn't necessary for test, however. +if process.env.NODE_ENV != 'test' + require 'blueimp-file-upload' + require 'jquery.iframe-transport' GeminiForm = require '../../../components/gemini_form/view.coffee' diff --git a/src/desktop/components/main_layout/templates/blank.jade b/src/desktop/components/main_layout/templates/blank.jade index ce1a6598a24..e2ed480e908 100644 --- a/src/desktop/components/main_layout/templates/blank.jade +++ b/src/desktop/components/main_layout/templates/blank.jade @@ -1,6 +1,6 @@ block locals - bodyClass = helpers ? helpers.buildBodyClass(sd) : '' - - defaultOptions = {modal: true, flash: true, stripe: false, stripev3: false, sailthru: true, marketo: true, quantcast: true} + - defaultOptions = {modal: true, flash: true, stripev3: false, sailthru: true, marketo: true, quantcast: false} - options = options ? Object.assign({}, defaultOptions, options) : defaultOptions doctype html diff --git a/src/desktop/components/main_layout/templates/index.jade b/src/desktop/components/main_layout/templates/index.jade index cdce738e1ec..1fe6ef1270b 100644 --- a/src/desktop/components/main_layout/templates/index.jade +++ b/src/desktop/components/main_layout/templates/index.jade @@ -1,7 +1,7 @@ //- Override any locals with `append locals` block locals - bodyClass = helpers ? helpers.buildBodyClass(sd, 'body-header-fixed') : ''; - - defaultOptions = {modal: true, flash: true, stripe: false, stripev3: false, sailthru: true, marketo: true, quantcast: true} + - defaultOptions = {modal: true, flash: true, stripev3: false, sailthru: true, marketo: true, quantcast: false} - options = options ? Object.assign({}, defaultOptions, options) : defaultOptions doctype html diff --git a/src/desktop/components/main_layout/templates/minimal_header.jade b/src/desktop/components/main_layout/templates/minimal_header.jade index d82ecb86ed2..06fdf5133f4 100644 --- a/src/desktop/components/main_layout/templates/minimal_header.jade +++ b/src/desktop/components/main_layout/templates/minimal_header.jade @@ -1,7 +1,7 @@ //- Override any locals with `append locals` block locals - bodyClass = helpers ? helpers.buildBodyClass(sd, 'body-header-fixed minimal-header') : '' - - defaultOptions = {modal: true, flash: true, stripe: false, stripev3: false, sailthru: true, marketo: true, quantcast: true} + - defaultOptions = {modal: true, flash: true, stripev3: false, sailthru: true, marketo: true, quantcast: false} - options = options ? Object.assign({}, defaultOptions, options) : defaultOptions doctype html @@ -22,8 +22,11 @@ html( data-useragent= userAgent lang="en") block body #scripts - include scripts - script( src= asset('/assets/main_layout.js') ) - if assetPackage - script( src=asset('/assets/#{assetPackage}.js') ) + //- Don't replace this block, rather prepend or append! block scripts + include scripts + + script( src= asset('/assets/main_layout.js') ) + + if assetPackage + script( src=asset('/assets/#{assetPackage}.js') ) diff --git a/src/desktop/components/main_layout/templates/react_minimal_header.jade b/src/desktop/components/main_layout/templates/react_minimal_header.jade index 937b5cf0df8..4fbded8e32e 100644 --- a/src/desktop/components/main_layout/templates/react_minimal_header.jade +++ b/src/desktop/components/main_layout/templates/react_minimal_header.jade @@ -9,12 +9,15 @@ block head != css block body - script. - var __BOOTSTRAP__ = !{JSON.stringify(data)} - #react-root != body #react-portal if jsonLD include ./json_ld + +//- All hydration data should be loaded before React et al +block prepend scripts + script. + var __BOOTSTRAP__ = !{JSON.stringify(data)} + != scripts \ No newline at end of file diff --git a/src/desktop/components/main_layout/templates/react_redesign.jade b/src/desktop/components/main_layout/templates/react_redesign.jade index fb58fbbe2a7..aaee7f83367 100644 --- a/src/desktop/components/main_layout/templates/react_redesign.jade +++ b/src/desktop/components/main_layout/templates/react_redesign.jade @@ -9,12 +9,15 @@ block head != css block body - script. - var __BOOTSTRAP__ = !{JSON.stringify(data)} - #react-root != body #react-portal if jsonLD include ./json_ld + +//- All hydration data should be loaded before React et al +block prepend scripts + script. + var __BOOTSTRAP__ = !{JSON.stringify(data)} + != scripts \ No newline at end of file diff --git a/src/desktop/components/main_layout/templates/redesign.jade b/src/desktop/components/main_layout/templates/redesign.jade index 07568c97a8c..7aa510cf3a9 100644 --- a/src/desktop/components/main_layout/templates/redesign.jade +++ b/src/desktop/components/main_layout/templates/redesign.jade @@ -1,7 +1,7 @@ //- Override any locals with `append locals` block locals - bodyClass = helpers ? helpers.buildBodyClass(sd, 'body-header-fixed') : ''; - - defaultOptions = {modal: true, flash: true, stripe: false, stripev3: false, sailthru: true, marketo: true, quantcast: true} + - defaultOptions = {modal: true, flash: true, stripev3: false, sailthru: true, marketo: true, quantcast: false} - options = options ? Object.assign({}, defaultOptions, options) : defaultOptions doctype html @@ -38,10 +38,12 @@ html( //- Javascripts #scripts - include scripts - script( src= asset('/assets/main_layout.js') ) + //- Don't replace this block, rather prepend or append! + block scripts - if assetPackage - script( src=asset('/assets/#{assetPackage}.js') ) + include scripts + script( src= asset('/assets/main_layout.js') ) + + if assetPackage + script( src=asset('/assets/#{assetPackage}.js') ) - block scripts diff --git a/src/desktop/components/main_layout/templates/scripts.jade b/src/desktop/components/main_layout/templates/scripts.jade index 7ad9256c1be..99af914c072 100644 --- a/src/desktop/components/main_layout/templates/scripts.jade +++ b/src/desktop/components/main_layout/templates/scripts.jade @@ -1,4 +1,4 @@ -- defaultOptions = {stripe: false, stripev3: false, sailthru: true, marketo: true, quantcast: true} +- defaultOptions = {stripev3: false, sailthru: true, marketo: true, quantcast: false} - options = options ? Object.assign({}, defaultOptions, options) : defaultOptions //- Common bundle @@ -118,11 +118,8 @@ if sharify != sharify.script() //- Stripe -if options.stripe - script( type="text/javascript", src="https://js.stripe.com/v2/" ) - if options.stripev3 - script(async, id="stripe-js", src="https://js.stripe.com/v3/") + script(id="stripe-js", src="https://js.stripe.com/v3/") //- Analytics & common asset package if sd.BROWSER && sd.BROWSER.family != 'Other' diff --git a/src/desktop/components/react/stitch_components/ReactionArtwork.tsx b/src/desktop/components/react/stitch_components/ReactionArtwork.tsx deleted file mode 100644 index 1b0f86f6296..00000000000 --- a/src/desktop/components/react/stitch_components/ReactionArtwork.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react" -import { ContextProvider } from "reaction/Artsy" -import { data as sd } from "sharify" -import { Artwork } from "reaction/Components/Artwork" - -export const ReactionArtwork = props => { - return ( - - - - ) -} diff --git a/src/desktop/components/react/stitch_components/ReactionArtworkArtistInfo.tsx b/src/desktop/components/react/stitch_components/ReactionArtworkArtistInfo.tsx index cb86ab132f5..7a4110fae72 100644 --- a/src/desktop/components/react/stitch_components/ReactionArtworkArtistInfo.tsx +++ b/src/desktop/components/react/stitch_components/ReactionArtworkArtistInfo.tsx @@ -1,19 +1,11 @@ import React from "react" -// @ts-ignore -import mediator from "desktop/lib/mediator.coffee" -import { Theme, Box } from "@artsy/palette" -import { ContextProvider } from "reaction/Artsy" +import { Box } from "@artsy/palette" import { ArtistInfoQueryRenderer } from "reaction/Apps/Artwork/Components/ArtistInfo" -import { data as sd } from "sharify" export const ReactionArtworkArtistInfo = props => { return ( - - - - - - - + + + ) } diff --git a/src/desktop/components/react/stitch_components/ReactionArtworkDetails.tsx b/src/desktop/components/react/stitch_components/ReactionArtworkDetails.tsx deleted file mode 100644 index e3679befb13..00000000000 --- a/src/desktop/components/react/stitch_components/ReactionArtworkDetails.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react" -import { Theme } from "@artsy/palette" -import { ContextProvider } from "reaction/Artsy" -import { ArtworkDetailsQueryRenderer } from "reaction/Apps/Artwork/Components/ArtworkDetails" -import { data as sd } from "sharify" - -export const ReactionArtworkDetails = props => { - return ( - - - - - - ) -} diff --git a/src/desktop/components/react/stitch_components/ReactionArtworkGrid.tsx b/src/desktop/components/react/stitch_components/ReactionArtworkGrid.tsx index 789bca8d573..45354d6b1ce 100644 --- a/src/desktop/components/react/stitch_components/ReactionArtworkGrid.tsx +++ b/src/desktop/components/react/stitch_components/ReactionArtworkGrid.tsx @@ -1,7 +1,5 @@ import React, { Component } from "react" import PropTypes from "prop-types" -import { ContextProvider } from "reaction/Artsy" -import { data as sd } from "sharify" import { mapToRelayConnection } from "./utils/mapToRelayConnection" import { ArtworkGrid } from "reaction/Components/ArtworkGrid" @@ -45,10 +43,6 @@ export class ReactionArtworkGrid extends Component { render() { const artworks = mapToRelayConnection(this.state.artworks) - return ( - - - - ) + return } } diff --git a/src/desktop/components/react/stitch_components/ReactionArtworkSidebarPageviews.tsx b/src/desktop/components/react/stitch_components/ReactionArtworkSidebarPageviews.tsx index 9445352e6ef..fde12d251f5 100644 --- a/src/desktop/components/react/stitch_components/ReactionArtworkSidebarPageviews.tsx +++ b/src/desktop/components/react/stitch_components/ReactionArtworkSidebarPageviews.tsx @@ -1,17 +1,14 @@ import React from "react" -import { Theme, Box, Separator } from "@artsy/palette" -import { ContextProvider } from "reaction/Artsy" +import { Box, Separator } from "@artsy/palette" import { ArtworkSidebarPageviewsQueryRenderer } from "reaction/Apps/Artwork/Components/ArtworkSidebar/ArtworkSidebarPageviews" export const ReactionArtworkSidebarPageviews = props => { return ( - - - - - - - - + <> + + + + + ) } diff --git a/src/desktop/components/react/stitch_components/ReactionFillwidth.tsx b/src/desktop/components/react/stitch_components/ReactionFillwidth.tsx index 986f343f135..c81dd134b1c 100644 --- a/src/desktop/components/react/stitch_components/ReactionFillwidth.tsx +++ b/src/desktop/components/react/stitch_components/ReactionFillwidth.tsx @@ -1,6 +1,4 @@ import React from "react" -import { ContextProvider } from "reaction/Artsy" -import { data as sd } from "sharify" import { mapToRelayConnection } from "./utils/mapToRelayConnection" export const ReactionFillwidth = props => { @@ -11,11 +9,7 @@ export const ReactionFillwidth = props => { const artworks = mapToRelayConnection(props.artworks) // eslint-disable-line - return ( - - - - ) + return } else { return "" } diff --git a/src/desktop/components/react/stitch_components/ReactionUserSettingsPayments.tsx b/src/desktop/components/react/stitch_components/ReactionUserSettingsPayments.tsx deleted file mode 100644 index 2c148a69ce7..00000000000 --- a/src/desktop/components/react/stitch_components/ReactionUserSettingsPayments.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react" -import { ContextProvider } from "reaction/Artsy" -import { UserSettingsPaymentsQueryRenderer as UserSettingsPayments } from "reaction/Components/Payment/UserSettingsPayments" -import { data as sd } from "sharify" - -export const ReactionUserSettingsPayments = () => { - return ( - - - - ) -} diff --git a/src/desktop/components/react/stitch_components/StitchWrapper.tsx b/src/desktop/components/react/stitch_components/StitchWrapper.tsx new file mode 100644 index 00000000000..c6bdf451147 --- /dev/null +++ b/src/desktop/components/react/stitch_components/StitchWrapper.tsx @@ -0,0 +1,17 @@ +import React from "react" +import { data as sd } from "sharify" +import { ContextProvider } from "@artsy/reaction/dist/Artsy" +import { MediaContextProvider } from "reaction/Utils/Responsive" +import { Theme } from "@artsy/palette" + +const mediator = require("desktop/lib/mediator.coffee") + +export const StitchWrapper = props => { + return ( + + + {props.children} + + + ) +} diff --git a/src/desktop/components/react/stitch_components/index.tsx b/src/desktop/components/react/stitch_components/index.tsx index 9cdbdc54d7d..bcbba2f22cd 100644 --- a/src/desktop/components/react/stitch_components/index.tsx +++ b/src/desktop/components/react/stitch_components/index.tsx @@ -1,18 +1,9 @@ -export { ReactionArtwork as Artwork } from "./ReactionArtwork" -export { - ReactionArtworkArtistInfo as ArtworkArtistInfo, -} from "./ReactionArtworkArtistInfo" -export { - ReactionArtworkDetails as ArtworkDetails, -} from "./ReactionArtworkDetails" +export { ReactionArtworkArtistInfo as ArtworkArtistInfo } from "./ReactionArtworkArtistInfo" +export { ArtworkDetailsQueryRenderer as ArtworkDetails } from "reaction/Apps/Artwork/Components/ArtworkDetails" export { ReactionArtworkGrid as ArtworkGrid } from "./ReactionArtworkGrid" export { ReactionFillwidth as Fillwidth } from "./ReactionFillwidth" -export { - ReactionTooltipQuestion as TooltipQuestion, -} from "./ReactionTooltipQuestion" -export { - ReactionArtworkSidebarPageviews as ArtworkSidebarPageviews, -} from "./ReactionArtworkSidebarPageviews" -export { - ReactionUserSettingsPayments as UserSettingsPayments, -} from "./ReactionUserSettingsPayments" +export { ReactionTooltipQuestion as TooltipQuestion } from "./ReactionTooltipQuestion" +export { ReactionArtworkSidebarPageviews as ArtworkSidebarPageviews } from "./ReactionArtworkSidebarPageviews" +export { UserSettingsPaymentsQueryRenderer as UserSettingsPayments } from "reaction/Components/Payment/UserSettingsPayments" +export { StitchWrapper } from './StitchWrapper' + diff --git a/src/desktop/components/split_test/running_tests.coffee b/src/desktop/components/split_test/running_tests.coffee index 0d72add1a6a..979e09c7909 100644 --- a/src/desktop/components/split_test/running_tests.coffee +++ b/src/desktop/components/split_test/running_tests.coffee @@ -24,4 +24,14 @@ # this should export empty Object # module.exports = {} -module.exports = {} +module.exports = { + artwork_sidebar_pageviews: + key: 'artwork_sidebar_pageviews' + outcomes: [ + 'control' + 'experiment' + ] + control_group: 'control' + edge: 'experiment' + weighting: 'equal' +} diff --git a/src/desktop/components/stripe/index.coffee b/src/desktop/components/stripe/index.coffee deleted file mode 100644 index 8f84134eba9..00000000000 --- a/src/desktop/components/stripe/index.coffee +++ /dev/null @@ -1,40 +0,0 @@ -Q = require 'bluebird-q' -{ reduce, every, identity } = require 'underscore' -{ STRIPE_PUBLISHABLE_KEY } = require('sharify').data - -module.exports = - initialize: -> - Stripe.setPublishableKey STRIPE_PUBLISHABLE_KEY - - error: (key) -> { - number: 'Your card number is incorrect.' - exp: 'Your card’s expiration year is invalid.' - cvc: 'Your card’s security code is invalid.' - }[key] - - validate: validate = ({ number, exp, cvc }) -> [ - { name: 'number', value: Stripe.card.validateCardNumber number } - { name: 'exp', value: Stripe.card.validateExpiry exp } - { name: 'cvc', value: Stripe.card.validateCVC cvc } - ] - - isValid: -> - every (validate arguments...), ({ value }) -> value - - serialize: ($form) -> - reduce $form.find('[data-stripe]'), (memo, el) -> - $el = $(el) - memo[$el.data 'stripe'] = $el.val() - memo - , {} - - tokenize: (obj) -> - Q.promise (resolve, reject) -> - Stripe.card.createToken obj, (status, data) -> - if status is 200 - resolve data - else - reject data.error.message - - cardType: (number) -> - Stripe.card.cardType number diff --git a/src/desktop/components/stripe/test/index.coffee b/src/desktop/components/stripe/test/index.coffee deleted file mode 100644 index a221e3687ca..00000000000 --- a/src/desktop/components/stripe/test/index.coffee +++ /dev/null @@ -1,73 +0,0 @@ -benv = require 'benv' -sinon = require 'sinon' -stripe = require '../index' -Stripe = {} - -describe 'stripe', -> - before (done) -> - benv.setup -> - benv.expose - $: benv.require 'jquery' - Stripe: Stripe - done() - - after -> - benv.teardown() - - beforeEach -> - Stripe.card = - validateCardNumber: sinon.stub() - validateExpiry: sinon.stub() - validateCVC: sinon.stub() - createToken: sinon.stub() - cardType: sinon.stub() - - describe '#isValid', -> - beforeEach -> - @card = - number: '6666666666666666' - exp: '10/2020' - cvc: '666' - - it 'returns true if all the validators are also true', -> - Stripe.card.validateCardNumber.returns true - Stripe.card.validateExpiry.returns true - Stripe.card.validateCVC.returns true - - stripe.isValid @card - .should.be.true() - - it 'returns false if any one of the validators is false', -> - Stripe.card.validateCardNumber.returns true - Stripe.card.validateExpiry.returns false - Stripe.card.validateCVC.returns true - - stripe.isValid @card - .should.be.false() - - describe '#serialize', -> - it 'pulls out data from sensitive fields marked with the `data-stripe` attribute', -> - $el = $ """ -
- - - -
- """ - - $el.find('[name="foo"]').val 'Bar' - $el.find('[data-stripe="cc_number"]').val '6666666666666666' - $el.find('[data-stripe="cvc"]').val '666' - - stripe.serialize $el - .should.eql - cc_number: '6666666666666666' - cvc: '666' - - describe '#tokenize', -> - it 'wraps Stripe’s `createToken` with a Promise', -> - Stripe.card.createToken.yields 200, id: 'existy' - - stripe.tokenize {} - .then ({ id }) -> - id.should.equal 'existy' diff --git a/src/desktop/config.coffee b/src/desktop/config.coffee index f6dadb27368..784df679b20 100755 --- a/src/desktop/config.coffee +++ b/src/desktop/config.coffee @@ -17,7 +17,7 @@ module.exports = ARTSY_ID: null ARTSY_SECRET: null BIDDER_H1_COPY: 'Please enter your credit card details' - BIDDER_H2_COPY: 'NOTE: All bidders need to have a valid payment method on file. Winning bidders will have the opportunity to pay by credit card, check or wire transfer.' + BIDDER_H2_COPY: 'A valid credit card is required to bid. If you win, you’ll have the opportunity to pay by credit card, check or wire transfer after the sale closes.' CDN_URL: 'https://d1s2w0upia4e9w.cloudfront.net' CMS_URL: 'https://cms.artsy.net' COLLECT_PAGE_TITLES_URL: 'https://s3.amazonaws.com/artsy-data/collect/collect.json' @@ -26,7 +26,6 @@ module.exports = CONVECTION_APP_ID: null CONVECTION_GEMINI_APP: 'convection-staging' COOKIE_DOMAIN: null - CRITEO_ARTWORKS_ACCOUNT_NUMBER: '35250' CRITEO_AUCTIONS_ACCOUNT_NUMBER: '28539' DD_APM_ENABLED: null DD_TRACE_AGENT_HOSTNAME: 'localhost' @@ -42,6 +41,8 @@ module.exports = EMBEDLY_KEY: 'a1f82558d8134f6cbebceb9e67d04980' EOY_2016_ARTICLE: null EOY_2016: '5829db77b5989e6f98f779a5' + EOY_2018_ARTISTS: '5bf30690d8b9430baaf6c6de' + EOY_2018_CULTURE: '5bf306aad8b9430baaf6c6df' EF_VENICE: '58f5eb75faef6a3a8e7fe1ad' EF_GUCCI: '5a009372c88a280f5e9efa7e' EF_VIDEO_GUIDE: '5901d64b4682400017f0e3cb' @@ -67,8 +68,11 @@ module.exports = GEOIP_ENDPOINT: 'https://artsy-geoip.herokuapp.com/' GOOGLE_ADWORDS_ID: null GOOGLE_MAPS_API_KEY: null - INTERCOM_APP_ID: null - INTERCOM_ENABLED: false + INTERCOM_SELLER_APP_ID: null + INTERCOM_SELLER_ENABLED: false + INTERCOM_BUYER_APP_ID: null + INTERCOM_BUYER_APP_SECRET: null + INTERCOM_BUYER_ENABLED: false IMAGE_PROXY: 'GEMINI' IPHONE_APP_COPY: 'Download the iPhone app: https://itunes.apple.com/us/app/artsy-art-world-in-your-pocket/id703796080?ls=1&mt=8' IP_BLACKLIST: '' diff --git a/src/desktop/index.js b/src/desktop/index.js index 97dc3443e3a..e6749f911e1 100644 --- a/src/desktop/index.js +++ b/src/desktop/index.js @@ -1,5 +1,6 @@ import * as modules from "./lib/global_react_modules" import { data as sd } from "sharify" + const app = (module.exports = require("express")()) // NOTE: @@ -7,7 +8,12 @@ const app = (module.exports = require("express")()) // TODO: Move to src/lib/middleware/locals once done developing; this is just so // we can get hot module reloading which only works in /desktop and /mobile -app.use(require("@artsy/stitch/dist/server").middleware(modules)) +app.use( + require("@artsy/stitch/dist/server").middleware({ + modules, + Wrapper: modules.StitchWrapper, + }) +) // Apps with hardcoded routes or "RESTful" routes app.use(require("./apps/home")) @@ -16,16 +22,16 @@ app.use(require("./apps/apply")) app.use(require("./apps/auctions")) app.use(require("./apps/auctions2")) app.use(require("./apps/auction_lots")) -app.use(require("./apps/artist/server")) +app.use(require("./apps/artist/server").app) app.use(require("./apps/artists")) app.use(require("./apps/auction")) app.use(require("./apps/auction_support")) app.use(require("./apps/artwork")) -app.use(require("./apps/artwork2/server")) +app.use(require("./apps/artwork2/server").app) app.use(require("./apps/about")) app.use(require("./apps/collect_art")) -app.use(require("./apps/collect2/server")) -app.use(require("./apps/collections/server")) +app.use(require("./apps/collect2/server").app) +app.use(require("./apps/collections/server").app) app.use(require("./apps/categories")) app.use(require("./apps/consign")) app.use(require("./apps/contact")) @@ -88,4 +94,4 @@ app.use(require("./apps/user")) // Examples app.use(require("./apps/react_example")) -app.use(require("./apps/isomorphic-relay-example/server")) +app.use(require("./apps/isomorphic-relay-example/server").app) diff --git a/src/desktop/lib/buildServerAppContext.ts b/src/desktop/lib/buildServerAppContext.ts index 45e810322d2..57a696c6f13 100644 --- a/src/desktop/lib/buildServerAppContext.ts +++ b/src/desktop/lib/buildServerAppContext.ts @@ -1,11 +1,6 @@ // @ts-ignore import mediator from "desktop/lib/mediator.coffee" -import { Request as ExpressRequest, Response as ExpressResponse } from "express" - -interface Request extends ExpressRequest { - user: any -} -type Response = ExpressResponse +import { Request, Response } from "express" /** * Builds initial context for Reaction components from server load. Put commonly diff --git a/src/desktop/lib/global_client_setup.coffee b/src/desktop/lib/global_client_setup.coffee index 8b6174a5cb8..73c56fb6b8c 100644 --- a/src/desktop/lib/global_client_setup.coffee +++ b/src/desktop/lib/global_client_setup.coffee @@ -102,12 +102,13 @@ mountStitchBlocks = -> {components, mountOnClient} = componentRenderer({ mode: 'client', modules: globalReactModules + Wrapper: globalReactModules.StitchWrapper }) sd.stitch.renderQueue.forEach (block) -> mountOnClient(block) # Mount renderer for runtime client-side templates. NOTE: must be included - # in template when rendering data; e.g., html = myTemplate({ data, reaction }) + # in template when rendering data; e.g., html = myTemplate({ data, stitch }) sd.stitch.components = components diff --git a/src/desktop/models/fair.coffee b/src/desktop/models/fair.coffee index c02808d936b..742c79432fe 100644 --- a/src/desktop/models/fair.coffee +++ b/src/desktop/models/fair.coffee @@ -216,17 +216,13 @@ module.exports = class Fair extends Backbone.Model @get('published') and not @related().profile.get('published') - hasStarted: -> - Date.parse(@get('start_at')) < new Date - - hasNotStarted: -> - Date.parse(@get('start_at')) > new Date - isNotOver: -> - Date.parse(@get('end_at')) > new Date + not @isOver() isOver: -> - Date.parse(@get('end_at')) < new Date + endOfFair = moment.utc(@get('end_at')).endOf("day") + now = moment() + now.isAfter(endOfFair) isCurrent: -> @isEligible() and @isNotOver() diff --git a/src/desktop/test/models/fair.coffee b/src/desktop/test/models/fair.coffee index 711c8329ef1..8d86da00f5a 100644 --- a/src/desktop/test/models/fair.coffee +++ b/src/desktop/test/models/fair.coffee @@ -15,6 +15,15 @@ describe 'Fair', -> afterEach -> Backbone.sync.restore() + describe '#isNotOver', -> + it 'checks the UTC timezone of a fair\'s end_at field compared to now', -> + fair = new Fair fabricate('fair', end_at: '2018-12-13T22:00:00-05:00') + utcMoment = moment.utc('2018-12-14T07:00:00-00:00') + realNow = Date.now + Date.now = () -> utcMoment + fair.isNotOver().should.equal true + Date.now = realNow + describe '#nameSansYear', -> it 'returns the name without the year', -> new Fair name: 'Foo Fair 2015' diff --git a/src/desktop/apps/gallery_partnerships/client/intercom.js b/src/lib/components/intercom/index.js similarity index 63% rename from src/desktop/apps/gallery_partnerships/client/intercom.js rename to src/lib/components/intercom/index.js index 725e731e579..b88ca180cf2 100644 --- a/src/desktop/apps/gallery_partnerships/client/intercom.js +++ b/src/lib/components/intercom/index.js @@ -1,9 +1,16 @@ import { data as sd } from "sharify" -window.intercomSettings = { - app_id: sd.INTERCOM_APP_ID, -} -;(function() { +export const intercom = function(appID, userData = null) { + window.intercomSettings = { + app_id: appID, + } + + // Logged-in user w/ identity verification + if (userData) { + window.intercomSettings.name = userData.name + window.intercomSettings.email = userData.email + window.intercomSettings.user_hash = userData.userHash + } var w = window var ic = w.Intercom if (typeof ic === "function") { @@ -23,7 +30,7 @@ window.intercomSettings = { var s = d.createElement("script") s.type = "text/javascript" s.async = true - s.src = `https://widget.intercom.io/widget/${sd.INTERCOM_APP_ID}` + s.src = `https://widget.intercom.io/widget/${appID}` var x = d.getElementsByTagName("script")[0] x.parentNode.insertBefore(s, x) } @@ -33,4 +40,4 @@ window.intercomSettings = { w.addEventListener("load", l, false) } } -})() +} diff --git a/src/lib/intercom.ts b/src/lib/intercom.ts new file mode 100644 index 00000000000..ce2e86918dc --- /dev/null +++ b/src/lib/intercom.ts @@ -0,0 +1,29 @@ +const { intercom } = require("./components/intercom/index") +import { data as sd } from "sharify" + +// We show an Intercom chat widget on artwork pages where the +// artwork is commercial (offerable or acquireable). +export function enableIntercom({ is_offerable, is_acquireable }) { + const { + INTERCOM_BUYER_ENABLED, + INTERCOM_BUYER_APP_ID, + INTERCOM_BUYER_HASH, + CURRENT_USER, + } = sd + + const intercomEnabled = INTERCOM_BUYER_ENABLED && INTERCOM_BUYER_APP_ID + const isCommercialWork = is_acquireable || is_offerable + + if (isCommercialWork && intercomEnabled) { + if (CURRENT_USER) { + const userData = { + name: CURRENT_USER.name, + email: CURRENT_USER.email, + userHash: INTERCOM_BUYER_HASH, + } + intercom(INTERCOM_BUYER_APP_ID, userData) + } else { + intercom(INTERCOM_BUYER_APP_ID) + } + } +} diff --git a/src/lib/memory_profiler.js b/src/lib/memory_profiler.js index 60658b97aca..8085f315310 100644 --- a/src/lib/memory_profiler.js +++ b/src/lib/memory_profiler.js @@ -1,7 +1,7 @@ const chalk = require("chalk") const heapdump = require("heapdump") const knox = require("knox") -const memwatch = require("memwatch-ng") +const memwatch = require("@airbnb/node-memwatch") const moment = require("moment") const os = require("os") const path = require("path") diff --git a/src/lib/middleware/intercom.ts b/src/lib/middleware/intercom.ts new file mode 100644 index 00000000000..ce656a9a098 --- /dev/null +++ b/src/lib/middleware/intercom.ts @@ -0,0 +1,14 @@ +import * as crypto from "crypto" +// @ts-ignore +import { INTERCOM_BUYER_APP_SECRET } from "../../config" + +export function addIntercomUserHash(req, res, next) { + console.log(INTERCOM_BUYER_APP_SECRET) + if (req.user && INTERCOM_BUYER_APP_SECRET) { + res.locals.sd.INTERCOM_BUYER_HASH = crypto + .createHmac("sha256", INTERCOM_BUYER_APP_SECRET) + .update(req.user.get("email")) + .digest("hex") + } + return next() +} diff --git a/src/lib/setup.js b/src/lib/setup.js index defe8f93308..eb1a14c2473 100644 --- a/src/lib/setup.js +++ b/src/lib/setup.js @@ -44,6 +44,7 @@ import backboneErrorHelper from "./middleware/backbone_error_helper" import CurrentUser from "./current_user" import splitTestMiddleware from "../desktop/components/split_test/middleware" import marketingModals from "./middleware/marketing_modals" +import { addIntercomUserHash } from "./middleware/intercom" import config from "../config" import compression from "compression" @@ -221,6 +222,7 @@ export default function(app) { app.use(logger) app.use(unsupportedBrowserCheck) app.use(splitTestMiddleware) + app.use(addIntercomUserHash) if (DD_APM_ENABLED) { ddTracer.init({ diff --git a/src/lib/setup_sharify.js b/src/lib/setup_sharify.js index 8c78af1d3bd..2efef1ea650 100644 --- a/src/lib/setup_sharify.js +++ b/src/lib/setup_sharify.js @@ -34,7 +34,6 @@ sharify.data = _.extend( "CONVECTION_APP_URL", "CONVECTION_APP_ID", "CONVECTION_GEMINI_APP", - "CRITEO_ARTWORKS_ACCOUNT_NUMBER", "CRITEO_AUCTIONS_ACCOUNT_NUMBER", "DISABLE_IMAGE_PROXY", "EDITORIAL_ADMINS", @@ -46,6 +45,8 @@ sharify.data = _.extend( "ENABLE_EXPERIMENTAL_STITCH_INJECTION", "EOY_2016_ARTICLE", "EOY_2016", + "EOY_2018_ARTISTS", + "EOY_2018_CULTURE", "EF_GUCCI", "EF_VENICE", "EF_VIDEO_GUIDE", @@ -69,8 +70,10 @@ sharify.data = _.extend( "GOOGLE_MAPS_API_KEY", "GOOGLE_ADWORDS_ID", "IMAGE_PROXY", - "INTERCOM_APP_ID", - "INTERCOM_ENABLED", + "INTERCOM_SELLER_APP_ID", + "INTERCOM_SELLER_ENABLED", + "INTERCOM_BUYER_APP_ID", + "INTERCOM_BUYER_ENABLED", "MARKETING_SIGNUP_MODALS", "MAX_POLLS_FOR_MAX_BIDS", "METAPHYSICS_ENDPOINT", diff --git a/src/mobile/analytics/criteo.js b/src/mobile/analytics/criteo.js index 674f09e2a11..a8fd35865e3 100644 --- a/src/mobile/analytics/criteo.js +++ b/src/mobile/analytics/criteo.js @@ -64,7 +64,7 @@ if (pathSplit[1] === "auctions") { ) } else { window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "m" }, { event: "setEmail", email: userEmail }, { event: "viewItem", item: sd.ARTWORK._id } @@ -75,7 +75,7 @@ if (pathSplit[1] === "auctions") { // https://www.artsy.net/collect - (ARTWORKS viewHome) // 0 1 window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "m" }, { event: "setEmail", email: userEmail }, { event: "viewHome" } @@ -84,7 +84,7 @@ if (pathSplit[1] === "auctions") { // https://www.artsy.net/artist/:artist_id - (ARTWORKS viewList) // 0 1 2 window.criteo_q.push( - { event: "setAccount", account: sd.CRITEO_ARTWORKS_ACCOUNT_NUMBER }, + { event: "setAccount", account: sd.CRITEO_AUCTIONS_ACCOUNT_NUMBER }, { event: "setSiteType", type: "m" }, { event: "setEmail", email: userEmail }, { diff --git a/src/mobile/apps/art_fairs/helpers.coffee b/src/mobile/apps/art_fairs/helpers.coffee index 7e95a0391be..5aef3397fbd 100644 --- a/src/mobile/apps/art_fairs/helpers.coffee +++ b/src/mobile/apps/art_fairs/helpers.coffee @@ -1,5 +1,6 @@ _ = require 'underscore' DateHelpers = require '../../components/util/date_helpers.coffee' +moment = require 'moment' module.exports = @@ -10,13 +11,15 @@ module.exports = fair.is_published and fair.profile?.is_published isNotOver: (fair) -> - Date.parse(fair.end_at) > new Date + not @isOver(fair) isPast: (fair) -> @isEligible(fair) and @isOver(fair) isOver: (fair) -> - Date.parse(fair.end_at) < new Date + endOfFair = moment.utc(fair.end_at).endOf("day") + now = moment() + now.isAfter(endOfFair) isUpcoming: (fair) -> @isEventuallyEligible(fair) and @isNotOver(fair) diff --git a/src/mobile/apps/art_fairs/test/routes.coffee b/src/mobile/apps/art_fairs/test/routes.coffee index 933018bc3cf..bc634a72d4f 100644 --- a/src/mobile/apps/art_fairs/test/routes.coffee +++ b/src/mobile/apps/art_fairs/test/routes.coffee @@ -22,10 +22,6 @@ describe '#index', -> fabricate('fair', profile: profile, id: _.uniqueId('past'), is_published: true, has_full_feature: true, has_listing: true, organizer: fabricate('fair_organizer'), end_at: moment().subtract(10, 'days')) @upcomingFairs = _.times 3, -> fabricate('fair', profile: unpublished_profile, id: _.uniqueId('upcoming'), is_published: true, has_full_feature: true, has_listing: true, organizer: null, end_at: moment().add(10, 'days')) - @invalidFairs = [ - fabricate 'fair', id: _.uniqueId('invalid'), is_published: false - fabricate 'fair', id: _.uniqueId('invalid'), is_published: true, has_full_feature: false, has_listing: false - ] describe 'with fairs', -> @@ -35,7 +31,6 @@ describe '#index', -> @currentFairs @pastFairs @upcomingFairs - @invalidFairs ] routes.__set__ 'metaphysics', => Q.resolve { fairs: @fairs } diff --git a/src/mobile/apps/artwork/client/index.coffee b/src/mobile/apps/artwork/client/index.coffee index 567d334ecf9..e08a19fca3f 100644 --- a/src/mobile/apps/artwork/client/index.coffee +++ b/src/mobile/apps/artwork/client/index.coffee @@ -9,7 +9,7 @@ Artwork = require '../../../models/artwork.coffee' Artworks = require '../../../collections/artworks.coffee' fetchArtworkBuckets = require '../components/related_artworks/index.coffee' { recordArtworkView } = require 'lib/components/record_artwork_view' - +{ enableIntercom } = require 'lib/intercom' module.exports.init = -> bootstrap() artworkTabsView() @@ -17,6 +17,7 @@ module.exports.init = -> user = CurrentUser.orNull() user.initializeDefaultArtworkCollection() if user recordArtworkView(sd.ARTWORK._id, user) + enableIntercom(sd.ARTWORK) new ArtworkImageView artwork: sd.ARTWORK diff --git a/src/mobile/apps/artwork/components/bid/index.jade b/src/mobile/apps/artwork/components/bid/index.jade index 08569cbc8f6..a90a266bda5 100644 --- a/src/mobile/apps/artwork/components/bid/index.jade +++ b/src/mobile/apps/artwork/components/bid/index.jade @@ -2,12 +2,13 @@ if artwork.auction .js-artwork-auction-container .artwork-auction-bid-module - if artwork.auction.is_open && !artwork.auction.is_live_open && !artwork.is_sold && !helpers.moment().isAfter(artwork.auction.end_at) + if artwork.auction.is_preview || artwork.auction.is_open && !artwork.auction.is_live_open && !artwork.is_sold && !helpers.moment().isAfter(artwork.auction.end_at) .artwork-auction-bid-module__current-bid include ./templates/bid.jade - .artwork-auction-bid-module__bid-form - include ./templates/bid_button.jade + if !artwork.auction.is_preview + .artwork-auction-bid-module__bid-form + include ./templates/bid_button.jade if artwork.auction.is_with_buyers_premium .artwork-auction-buyers-premium This work has a  diff --git a/src/mobile/apps/artwork/components/meta_data/templates/price.jade b/src/mobile/apps/artwork/components/meta_data/templates/price.jade index 0e020ee39ea..193d5dffd3e 100644 --- a/src/mobile/apps/artwork/components/meta_data/templates/price.jade +++ b/src/mobile/apps/artwork/components/meta_data/templates/price.jade @@ -2,7 +2,7 @@ if artwork.sale_message .artwork-meta-data-price if artwork.sale_message .sale_message= artwork.sale_message - if artwork.price && artwork.availability !== 'sold' + if artwork.is_acquireable || artwork.is_offerable .shipping-info | #{artwork.shippingInfo} if artwork.shippingOrigin diff --git a/src/mobile/apps/artwork/components/meta_data/test/template.coffee b/src/mobile/apps/artwork/components/meta_data/test/template.coffee index 52e354b43d0..bf84f2f5583 100644 --- a/src/mobile/apps/artwork/components/meta_data/test/template.coffee +++ b/src/mobile/apps/artwork/components/meta_data/test/template.coffee @@ -33,6 +33,37 @@ describe 'Artwork metadata templates', -> $ = cheerio.load(@html) $('.sale_message').text().should.equal '$4,000' + describe 'artwork with shipping information', -> + it 'shows shipping information for ecommerce artworks', -> + @artwork.is_acquireable = true + @artwork.sale_message = '$5,000' + @artwork.shippingInfo = "Shipping: $50 continental US, $100 rest of world" + @artwork.shippingOrigin = "New York, New York, US" + + html = render('price')( + artwork: @artwork + sd: {} + asset: (->) + ) + + $ = cheerio.load html + $('.shipping-info').length.should.eql 1 + + it 'does not show shipping information when unenrolled from ecommerce programs', -> + @artwork.is_acquireable = false + @artwork.sale_message = '$5,000' + @artwork.shippingInfo = "Shipping: $50 continental US, $100 rest of world" + @artwork.shippingOrigin = "New York, New York, US" + + html = render('price')( + artwork: @artwork + sd: {} + asset: (->) + ) + + $ = cheerio.load html + $('.shipping-info').length.should.eql 0 + describe 'artwork without sale message', -> beforeEach -> @html = render('price')( diff --git a/src/mobile/apps/auction_support/client/registration_form.coffee b/src/mobile/apps/auction_support/client/registration_form.coffee index 9a9acb607a8..92c53a1dfc4 100644 --- a/src/mobile/apps/auction_support/client/registration_form.coffee +++ b/src/mobile/apps/auction_support/client/registration_form.coffee @@ -25,28 +25,37 @@ module.exports = class RegistrationForm extends ErrorHandlingForm @$conditionsCheckbox = @$('.artsy-checkbox') @$submit = @$('.registration-form-content .avant-garde-box-button') @setUpFields() + @setUpStripe() setUpFields: -> @fields = 'name on card': { el: @$('input[name=card_name]'), validator: @isPresent } - 'card number': { el: @$('input[name=card_number]'), validator: @isCardNumber } - 'security code': { el: @$('input[name=card_security_code]'), validator: @isPresent } telephone: { el: @$('input.telephone'), validator: @isPresent } - month: { el: @$('.card-expiration .month select'), validator: @isPresent } - year: { el: @$('.card-expiration .year select'), validator: @isPresent } street: { el: @$('input.street'), validator: @isPresent, label: 'address' } city: { el: @$('input.city'), validator: @isPresent, label: 'city' } state: { el: @$('input.region'), validator: @isState, label: 'state' } zip: { el: @$('input.postal-code'), validator: @isZip } @internationalizeFields() + setUpStripe: -> + @stripe = Stripe(STRIPE_PUBLISHABLE_KEY) + elements = @stripe.elements() + @card = elements.create('card', { + style: { + base: { + fontFamily: '"Adobe Garamond W08", Georgia, Serif', + fontSize: '16px', + '::placeholder': { + color: '#cccccc', + }, + } + } + }) + @card.update({ hidePostalCode: true }) + @card.mount('#card-element') cardData: -> name: @fields['name on card'].el.val() - number: @fields['card number'].el.val() - exp_month: @fields.month.el.first().val() - exp_year: @fields.year.el.last().val() - cvc: @fields['security code'].el.val() address_line1: @fields.street.el.val() address_city: @fields.city.el.val() address_state: @fields.state.el.val() @@ -56,12 +65,12 @@ module.exports = class RegistrationForm extends ErrorHandlingForm tokenizeCard: -> Q.Promise (resolve, reject) => # Attempt to tokenize the credit card through Stripe - Stripe.setPublishableKey STRIPE_PUBLISHABLE_KEY - Stripe.card.createToken @cardData(), (status, data) => - if status is 200 - resolve data + @stripe.createToken(@card, @cardData()).then (result) -> + if result.token + resolve result else - reject data.error.message + reject result.error.message + .then (data) => analyticsHooks.trigger 'registration:validated' @@ -69,7 +78,7 @@ module.exports = class RegistrationForm extends ErrorHandlingForm Q.Promise (resolve, reject) => card = new Backbone.Model card.url = "#{sd.API_URL}/api/v1/me/credit_cards" - card.save { token: data.id, provider: 'stripe' }, + card.save { token: data.token.id, provider: 'stripe' }, success: (creditCard) => if creditCard.get("address_zip_check") == "fail" reject @errors.badZip @@ -77,7 +86,8 @@ module.exports = class RegistrationForm extends ErrorHandlingForm reject @errors.badSecurityCode else resolve(creditCard) - error: (m, xhr) => reject(xhr.responseJSON?.message) + error: (m, xhr) => + reject(xhr.responseJSON?.message) .then => analyticsHooks.trigger 'registration:submitted' @@ -113,7 +123,6 @@ module.exports = class RegistrationForm extends ErrorHandlingForm onSubmit: (event) -> event.preventDefault() return unless @validateAcceptConditions() - analyticsHooks.trigger 'registration:submitted-address' @loadingLock @$submit, => diff --git a/src/mobile/apps/auction_support/stylesheets/index.styl b/src/mobile/apps/auction_support/stylesheets/index.styl index dd31c9b26e0..1ce9d834ddd 100644 --- a/src/mobile/apps/auction_support/stylesheets/index.styl +++ b/src/mobile/apps/auction_support/stylesheets/index.styl @@ -90,25 +90,18 @@ checkbox-size = 20px .credit-card-input-section margin-top 10px - .card-expiration - .month, .year - width 49% - display inline-block - vertical-align top - .month - margin-right 2% .registration-form-section margin-top 20px - .card-expiration.credit-card-input-section, .card-security-code.credit-card-input-section, .postal-code.credit-card-input-section, .region.credit-card-input-section + .postal-code.credit-card-input-section, .region.credit-card-input-section display inline-block vertical-align top .postal-code.credit-card-input-section, .region.credit-card-input-section width 49% - .card-expiration.credit-card-input-section, .region.credit-card-input-section + .region.credit-card-input-section margin-right 2% .avant-garde-box-button @@ -128,11 +121,15 @@ checkbox-size = 20px .error color red-color - .card-security-code.credit-card-input-section - width 36% - display block - input - padding 10px 0 11px 0 - .card-expiration - width 62% +#card-element + border 2px solid light-gray-border-color + box-sizing border-box + width 100% + padding 10px + height 44px + +#card-element.StripeElement--focus + border-color highlight-color + outline none + diff --git a/src/mobile/apps/auction_support/test/client/registration_form.coffee b/src/mobile/apps/auction_support/test/client/registration_form.coffee index d0593189c55..f7fa43640fe 100644 --- a/src/mobile/apps/auction_support/test/client/registration_form.coffee +++ b/src/mobile/apps/auction_support/test/client/registration_form.coffee @@ -16,9 +16,15 @@ describe 'RegistrationForm', -> benv.expose $: benv.require('jquery'), Stripe: @Stripe = - setPublishableKey: sinon.stub() - card: - createToken: sinon.stub() + sinon.stub().returns({ + createToken: sinon.stub().returns(Promise.resolve({ token: { id: '123' } })) + elements: sinon.stub().returns({ + create: sinon.stub().returns({ + update: sinon.stub() + mount: sinon.stub() + }) + }) + }) Backbone.$ = $ done() @@ -26,7 +32,7 @@ describe 'RegistrationForm', -> benv.teardown() beforeEach (done) -> - @submitStub = sinon.stub(Backbone, 'sync')#.yieldsTo('success') + @submitStub = sinon.stub(Backbone, 'sync') @sale = new Sale fabricate 'sale' @@ -53,10 +59,6 @@ describe 'RegistrationForm', -> @acceptTerms = => @view.$acceptConditions.prop('checked', true) @submitValidForm = => @view.$('input[name="card_name"]').val 'Foo Bar' - @view.$('select[name="card_expiration_month"]').val '1' - @view.$('select[name="card_expiration_year"]').val '2024' - @view.$('input[name="card_number"]').val '4111111111111111' - @view.$('input[name="card_security_code"]').val '123' @view.$('input[name="address[street]"]').val '456 Foo Bar Ln.' @view.$('input[name="address[city]"]').val 'Foobarrington' @view.$('input[name="address[region]"]').val 'FB' @@ -65,8 +67,6 @@ describe 'RegistrationForm', -> @view.$submit.click() it 'still succeeds if the API throws an error for having already created a bidder', (done) -> - # Successfully create a stripe token - @Stripe.card.createToken.callsArgWith(1, 200, {}) # Successfully save phone number Backbone.sync.onFirstCall().yieldsTo('success') # Successfully save credit card @@ -88,8 +88,6 @@ describe 'RegistrationForm', -> @view.on "submitted", => html = @view.$el.html() html.should.containEql 'Invalid name on card' - html.should.containEql 'Invalid card number' - html.should.containEql 'Invalid security code' html.should.containEql 'Invalid city' html.should.containEql 'Invalid state' html.should.containEql 'Invalid zip' @@ -98,32 +96,29 @@ describe 'RegistrationForm', -> done() it 'lets the user resubmit a corrected form', -> - # Submit a bad form + @acceptTerms() + # Submit a bad form @view.$submit.length.should.be.ok() @view.$submit.click() - @view.on "submitted", => + @view.once "submitted", => html = @view.$el.html() html.should.containEql 'Please review the error(s) above and try again.' # Now submit a good one - - # Successfully create a stripe token - @Stripe.card.createToken.callsArgWith(1, 200, {}) - # Successfully save phone number - Backbone.sync.onFirstCall().yieldsTo('success') - # Successfully save credit card - Backbone.sync.onSecondCall().yieldsTo('success') - # Successfully create the bidder - Backbone.sync.onThirdCall().yieldsTo('success') - - @acceptTerms() + Backbone.sync + .yieldsTo 'success', {} # savePhoneNumber success + .onCall 1 + .yieldsTo 'success', { get: () -> 'pass' } # credit card save passes + .onCall 2 + .yieldsTo 'success', {} + + @view.$submit.removeClass('is-loading') @submitValidForm() - @view.on "submitted", => - @Stripe.card.createToken.args[0][1](200, {}) + @view.once "submitted", => # Saves the phone number - Backbone.sync.args[0][1].changed.phone.should.equal '555-555-5555' + Backbone.sync.args[0][2].attrs.phone.should.equal '555-555-5555' # Saves the credit card Backbone.sync.args[1][1].url.should.containEql '/api/v1/me/credit_cards' @@ -134,8 +129,6 @@ describe 'RegistrationForm', -> Backbone.sync.args[2][2].url.should.containEql '/api/v1/bidder' it 'submits the form correctly', -> - # Successfully create a stripe token - @Stripe.card.createToken.callsArgWith(1, 200, {}) # Successfully save phone number Backbone.sync.onFirstCall().yieldsTo('success') # Successfully save credit card diff --git a/src/mobile/components/credit_card/templates/credit_card.jade b/src/mobile/components/credit_card/templates/credit_card.jade index 8a18925db50..6eba40b0714 100644 --- a/src/mobile/components/credit_card/templates/credit_card.jade +++ b/src/mobile/components/credit_card/templates/credit_card.jade @@ -2,21 +2,6 @@ label( for='card_name' ) Name on Card input.bordered-input( type='text', name='card_name', value=name, autocomplete='cc-name' ) -.card-number.credit-card-input-section - label( for='card_number' ) Credit Card Number - input.bordered-input( type='tel', name='card_number', value=number, autocomplte="cc-number", novalidate=true, pattern="\d*" ) - -.card-expiration.credit-card-input-section - label( for='card_expiration' ) Expiration - .month - select.bordered-select( name='card_expiration_month', autocomplete="cc-exp-month" ) - each month in monthRange - option( value=month )!= month - .year - select.bordered-select( name='card_expiration_year', autocomplete="cc-exp-year" ) - each year in yearRange - option( value=year )!= year - -.card-security-code.credit-card-input-section - label( for='card_security_code' ) Security Code - input.bordered-input( type='tel', name='card_security_code', value=security_code, autocomplte="cc-csc", novalidate=true, pattern="\d*" ) +label Credit Card Number +.credit-card-input-section + #card-element diff --git a/src/mobile/components/layout/templates/mixpanel.html b/src/mobile/components/layout/templates/mixpanel.html deleted file mode 100644 index b9f3537baaf..00000000000 --- a/src/mobile/components/layout/templates/mixpanel.html +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/src/mobile/components/layout/templates/quantcast.html b/src/mobile/components/layout/templates/quantcast.html deleted file mode 100644 index 2c8488c0d3c..00000000000 --- a/src/mobile/components/layout/templates/quantcast.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/mobile/components/layout/templates/scaffold.jade b/src/mobile/components/layout/templates/scaffold.jade index 641bf63545b..9dc03ebd08a 100644 --- a/src/mobile/components/layout/templates/scaffold.jade +++ b/src/mobile/components/layout/templates/scaffold.jade @@ -22,9 +22,8 @@ html( class=htmlClass ) analytics.load("#{sd.SEGMENT_WRITE_KEY}"); }}(); - //- Fonts + Mixpanel, Quantcast, Google analytics script + //- Fonts + Google analytics script if sd.NODE_ENV != 'test' - include ./quantcast.html if sd.GOOGLE_ANALYTICS_ID include ./ga.html @@ -37,7 +36,7 @@ html( class=htmlClass ) script( src=asset('/assets/mobile_analytics.js')) //- Stripe - script( type="text/javascript", src="https://js.stripe.com/v2/" ) + script( type="text/javascript", src="https://js.stripe.com/v3/" ) //- Include any scripts into the scripts block block scripts diff --git a/src/mobile/config.coffee b/src/mobile/config.coffee index 78b843a293b..293ae4cbd2f 100644 --- a/src/mobile/config.coffee +++ b/src/mobile/config.coffee @@ -15,7 +15,6 @@ module.exports = CLIENT_ID: 'e750db60ac506978fc70' CLIENT_SECRET: '3a33d2085cbd1176153f99781bbce7c6' COOKIE_DOMAIN: null - CRITEO_ARTWORKS_ACCOUNT_NUMBER: 35250 CRITEO_AUCTIONS_ACCOUNT_NUMBER: 28539 DEFAULT_CACHE_TIME: 3600 DISABLE_IMAGE_PROXY: false @@ -39,7 +38,6 @@ module.exports = MAX_POLLS_FOR_MAX_BIDS: 20 MAX_SOCKETS: -1 METAPHYSICS_ENDPOINT: 'https://metaphysics-production.artsy.net' - MIXPANEL_ID: null NODE_ENV: 'development' OPENREDIS_URL: null PORT: 3003 diff --git a/src/mobile/index.js b/src/mobile/index.js index 3a47f8e844a..2ef41f7719b 100644 --- a/src/mobile/index.js +++ b/src/mobile/index.js @@ -3,7 +3,12 @@ const app = (module.exports = require("express")()) // TODO: Move to src/lib/middleware/locals once done developing; this is just so // we can get hot module reloading which only works in /desktop and /mobile -app.use(require("@artsy/stitch/dist/server").middleware(modules)) +app.use( + require("@artsy/stitch/dist/server").middleware({ + modules, + Wrapper: modules.StitchWrapper, + }) +) app.use(require("./apps/auth")) app.use(require("./apps/page")) diff --git a/src/mobile/lib/global_client_setup.coffee b/src/mobile/lib/global_client_setup.coffee index 4db9e35eb9e..5bbe0653c82 100644 --- a/src/mobile/lib/global_client_setup.coffee +++ b/src/mobile/lib/global_client_setup.coffee @@ -13,7 +13,8 @@ module.exports = -> mountStitchBlocks = -> {components, mountOnClient} = componentRenderer({ mode: 'client', - modules: globalReactModules + modules: globalReactModules, + Wrapper: globalReactModules.StitchWrapper }) sd.stitch.renderQueue.forEach (block) -> diff --git a/src/mobile/lib/global_react_modules.js b/src/mobile/lib/global_react_modules.js index 74202228763..96538e9195a 100644 --- a/src/mobile/lib/global_react_modules.js +++ b/src/mobile/lib/global_react_modules.js @@ -1,7 +1 @@ -export { - ReactionArtworkDetails as ArtworkDetails, -} from "desktop/components/react/stitch_components/ReactionArtworkDetails" - -export { - ReactionArtworkArtistInfo as ArtworkArtistInfo, -} from "desktop/components/react/stitch_components/ReactionArtworkArtistInfo" +export * from "desktop/components/react/stitch_components" diff --git a/src/mobile/models/fair.coffee b/src/mobile/models/fair.coffee index c5488f623eb..90fb17d317e 100644 --- a/src/mobile/models/fair.coffee +++ b/src/mobile/models/fair.coffee @@ -139,10 +139,12 @@ module.exports = class Fair extends Backbone.Model (not @related().profile.get('published')) isNotOver: -> - Date.parse(@get('end_at')) > new Date + not @isOver() isOver: -> - Date.parse(@get('end_at')) < new Date + endOfFair = moment.utc(@get('end_at')).endOf("day") + now = moment() + now.isAfter(endOfFair) isCurrent: -> @isEligible() and @isNotOver() diff --git a/src/typings/express.d.ts b/src/typings/express.d.ts new file mode 100644 index 00000000000..a7e9b64e29d --- /dev/null +++ b/src/typings/express.d.ts @@ -0,0 +1,7 @@ +import * as express from "express" + +declare module "express" { + interface Request { + user?: any + } +} diff --git a/src/typings/global.d.ts b/src/typings/global.d.ts new file mode 100644 index 00000000000..5adc4067f88 --- /dev/null +++ b/src/typings/global.d.ts @@ -0,0 +1,5 @@ +interface Window { + analytics?: { + page: (object, object) => void + } +} diff --git a/test.config.js b/test.config.js index 915ce36a6f2..3c12ecd06ab 100644 --- a/test.config.js +++ b/test.config.js @@ -1,25 +1,25 @@ -require('@babel/register')({ - extensions: ['.ts', '.js', '.tsx', '.jsx'], +require("@babel/register")({ + extensions: [".ts", ".js", ".tsx", ".jsx"], }) -require('coffeescript/register') -require('@babel/polyfill') -require('raf/polyfill') -require('should') -require('./src/lib/jade_hook') +require("coffeescript/register") +require("@babel/polyfill") +require("raf/polyfill") +require("should") +require("./src/lib/jade_hook") // FIXME: Do we need this? // NOTE: Once we do AOT compilation we probably want to re-enable this on the server in development mode only. // require('source-map-support/register') -const path = require('path') -const Adapter = require('enzyme-adapter-react-16') -const Enzyme = require('enzyme') -const sd = require('sharify').data +const path = require("path") +const Adapter = require("enzyme-adapter-react-16") +const Enzyme = require("enzyme") +const sd = require("sharify").data // TODO: Look into why this bumps user off of other node command-line tab -require('dotenv').config({ - path: path.join(process.cwd(), '.env.test'), +require("dotenv").config({ + path: path.join(process.cwd(), ".env.test"), }) Enzyme.configure({ @@ -27,8 +27,8 @@ Enzyme.configure({ }) sd.AP = { - loginPagePath: '/login', - signupPagePath: '/signup', - facebookPath: '/facebook', - twitterPath: '/twitter', + loginPagePath: "/login", + signupPagePath: "/signup", + facebookPath: "/facebook", + twitterPath: "/twitter", } diff --git a/tsconfig.json b/tsconfig.json index 6aff6ccd0b0..9ba9ecaa98d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,5 +17,5 @@ "reaction/*": ["../node_modules/@artsy/reaction/dist/*"] } }, - "include": ["./src"] -} \ No newline at end of file + "include": ["./src", "typings/*.d.ts"] +} diff --git a/yarn.lock b/yarn.lock index 810a9023546..20741f0c841 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,36 +10,31 @@ bindings "^1.3.0" nan "^2.9.2" -"@artsy/express-reloadable@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@artsy/express-reloadable/-/express-reloadable-1.3.1.tgz#206adcc56ebcf999aece7efcc34eaf1460cf90a7" - integrity sha1-IGrcxW68+Zmuzn78w06vFGDPkKc= +"@artsy/detect-responsive-traits@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@artsy/detect-responsive-traits/-/detect-responsive-traits-0.0.1.tgz#3d83384bf3323fbd4679a2d73155ab85dc73e850" + integrity sha512-jqrC40t1P6w9zIvsJJhWe8pxLWdEC/kJQmDc4/b3vAnGy1EsJpcIJGUys37kwut7tDYPiypUF06SImtZVLGQnQ== + +"@artsy/express-reloadable@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@artsy/express-reloadable/-/express-reloadable-1.4.0.tgz#71db6ca8ac274b2c7a2f38eb633794ce8f9e90fd" + integrity sha1-cdtsqKwnSyx6LzjrYzeUzo+ekP0= dependencies: chalk "^2.3.1" chokidar "^1.7.0" decache "^4.4.0" -"@artsy/palette@^2.18.1": - version "2.18.1" - resolved "https://registry.yarnpkg.com/@artsy/palette/-/palette-2.18.1.tgz#876055f1cb85f32f18a1953aeef8a18aefee8bc6" - integrity sha512-k3LGUBp4dX9ELFlL3T2+LyoDY6oFoYKZPmE5wIOGGQ+CCzq8BEwxpDzcTuV5HhUBFc8mCPGP+mIdXK/TiD6HLA== - dependencies: - rc-slider "^8.6.2" - react "^16.5.0" - styled-reset "^1.3.7" - styled-system "^3.0.3" - -"@artsy/palette@^2.21.0": - version "2.21.0" - resolved "https://registry.yarnpkg.com/@artsy/palette/-/palette-2.21.0.tgz#3187ad4c277e444ca8dbaf7b1c47cc3e8ca3ebb0" - integrity sha512-me1hTzFkoJxQUuy8rmI0zzRArhbqSOfjb8jgRQTrTaZ2D52vJYWJ8T++obhplCzGFq1KxYwFZWEA/rW8iwHUBQ== +"@artsy/palette@2.23.2": + version "2.23.2" + resolved "https://registry.yarnpkg.com/@artsy/palette/-/palette-2.23.2.tgz#7c96deffb8691958f772133c18b233da62f6679e" + integrity sha512-3R9EtI26qkpNPhMSRimFPQzfbNZVJv6YyhI7Ivtm94zEyr/I5sL7oBg47G9RYYmib8DEC1dSYzWflNI4J1NnHw== dependencies: rc-slider "^8.6.2" react "^16.5.0" react-spring "^5.7.2" styled-system "^3.1.11" -"@artsy/passport@^1.1.0": +"@artsy/passport@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@artsy/passport/-/passport-1.1.0.tgz#06120530d7891de0e4b2c8d8163bb088dddce4c2" integrity sha512-hGcgTVk3P8rIF2kJ0P/1lC47AxhlvJBnUlbzbw7hK6bd+hYUD0o4qAzEd+mp1H+a329ofrxL8xu2mUqJKyIRtQ== @@ -58,20 +53,21 @@ superagent "^1.2.0" underscore.string "^3.2.2" -"@artsy/react-responsive-media@^1.0.12": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@artsy/react-responsive-media/-/react-responsive-media-1.0.12.tgz#3a76be54e82d1d658ee1794ecb692ea64468314c" - integrity sha512-BU7HUAVBvCjc3H7s117d+FL6Ufodc2hwgQZ2HuPq23jEXSYR6PHS98Kqd0vMthUeI4IdUY4Yc0du75Qu30OLew== +"@artsy/react-responsive-media@^2.0.0-beta.5": + version "2.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@artsy/react-responsive-media/-/react-responsive-media-2.0.0-beta.5.tgz#d61e1a9f215d38d90ec2bb97dc1ff7409d0346fa" + integrity sha512-XRYA77v/j3mVInwy5EYb8NtmMkkAD1/XDxDi45BO0oduv4hsljJfjtjK7jN0ziJ6JUm2QTx/t6QwuaT9E7vTuA== -"@artsy/reaction@^6.1.8": - version "6.1.8" - resolved "https://registry.yarnpkg.com/@artsy/reaction/-/reaction-6.1.8.tgz#c4153066a12faa72e3402c250bb19f4465094cb2" - integrity sha512-dfN3cCY7G7OiS+yUYiaEekptdYoC4o4UIP1R4bp3nyM1+s4IdsdV+2uohJr4PVV/BBdFOUG2PnP5xfWlqCNWQQ== +"@artsy/reaction@9.1.12": + version "9.1.12" + resolved "https://registry.yarnpkg.com/@artsy/reaction/-/reaction-9.1.12.tgz#006f0a9c34b8e075baf42392e883f777d795fec5" + integrity sha512-jnRLlaJT6T2FMHag3YbxthDtjWc7UlQhn1HdE/dZKUrkrayeG6wCTt0l0FfG6GXxYeRZA9ch0FacgDhL/lT2xQ== dependencies: - "@artsy/palette" "^2.21.0" - "@artsy/react-responsive-media" "^1.0.12" + "@artsy/detect-responsive-traits" "^0.0.1" + "@artsy/react-responsive-media" "^2.0.0-beta.5" "@sentry/browser" "^4.2.3" cheerio "^1.0.0-rc.2" + currency.js "^1.2.1" farce "^0.2.6" formik "^0.11.11" found "^0.3.14" @@ -82,6 +78,7 @@ jsonp "^0.2.1" loadable-components "^2.2.2" lodash "^4.17.4" + memoize-one "^4.0.3" moment-timezone "^0.5.13" numeral "^2.0.4" openseadragon "^2.3.1" @@ -97,8 +94,8 @@ react-oembed-container "^0.3.0" react-overlays "^0.8.3" react-relay "https://github.com/alloy/relay/releases/download/v1.5.0-artsy.3/react-relay-1.5.0-artsy.3.tgz" - react-relay-network-modern "^2.3.1" - react-relay-network-modern-ssr "^1.1.2" + react-relay-network-modern "^2.4.0" + react-relay-network-modern-ssr "^1.2.2" react-responsive-decorator "^0.0.1" react-router "^4.2.0" react-sizeme "^2.4.4" @@ -118,15 +115,16 @@ styled-system "^3.1.11" superagent "^3.6.3" trunc-html "^1.1.2" + underscore.string "^3.3.5" unstated "^2.1.1" unstated-debug "^0.2.0" url "^0.11.0" yup "^0.24.1" -"@artsy/stitch@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@artsy/stitch/-/stitch-2.0.0.tgz#c0a44186b3eac779bd3c7a6b0030c7ad7acff754" - integrity sha512-7k5wf7+5lE57aVF0Ep5QyVEQSEYHdLHIuTJxz5syWEq6C+F5m45x4nqKBz+ZmpU8P5oZkvFv2TbEH0DMVXYOGQ== +"@artsy/stitch@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@artsy/stitch/-/stitch-3.1.0.tgz#6ea39eced27a8323c760f328fa966a0e7442bef1" + integrity sha512-uTgB9wnmgDUsN1DZLZqqiBibTMJbf96VjY//xG038/iEuoGbvzE+fAL7ZSv1AK3QmIagp/UXLW7x+h72tc1knw== dependencies: consolidate "^0.14.5" lodash "^4.17.4" @@ -867,9 +865,9 @@ regenerator-runtime "^0.12.0" "@babel/runtime@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3" - integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg== + version "7.1.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.5.tgz#4170907641cf1f61508f563ece3725150cc6fe39" + integrity sha512-xKnPpXG/pvK1B90JkwwxSGii90rQGKtzcMt2gI5G6+M0REXaq6rOHsGC2ay6/d0Uje7zzvSzjEzfR3ENhFlrfA== dependencies: regenerator-runtime "^0.12.0" @@ -1068,9 +1066,9 @@ integrity sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA== "@types/node@*": - version "10.12.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.0.tgz#ea6dcbddbc5b584c83f06c60e82736d8fbb0c235" - integrity sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ== + version "10.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.7.tgz#195808b2d4b2e7c33e75e7d9b24aeee88f94660d" + integrity sha512-Zh5Z4kACfbeE8aAOYh9mqotRxaZMro8MbBQtR8vEXOMiZo2rGEh2LayJijKdlu48YnS6y2EFU/oo2NCe5P6jGw== "@types/node@^6.0.46": version "6.0.78" @@ -1142,6 +1140,11 @@ dependencies: source-map "^0.6.1" +"@types/webpack-env@^1.13.6": + version "1.13.6" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" + integrity sha512-5Th3OsZ4gTRdr9Mho83BQ23cex4sRhOR4XTG+m+cJc0FhtUBK9Vn62hBJ+pnQYnSxoPOsKoAPOx6FcphxBC8ng== + "@types/webpack@^4.4.11": version "4.4.11" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.11.tgz#0ca832870d55c4e92498c01d22d00d02b0f62ae9" @@ -1613,6 +1616,7 @@ antigravity@artsy/antigravity: version "0.1.3" resolved "https://codeload.github.com/artsy/antigravity/tar.gz/a4438d2fe9d0cdf71f1c47faba371cd3d004e140" dependencies: + coffeescript "1.11.1" express "*" any-observable@^0.2.0: @@ -2720,11 +2724,6 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" integrity sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q= -bindings@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - integrity sha1-FK1hE4EtLTfXLme0ystLtyZQXxE= - bindings@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" @@ -2773,10 +2772,29 @@ bluebird@^3.1.1, bluebird@^3.5.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== -blueimp-file-upload@9.9.0: - version "9.9.0" - resolved "https://registry.yarnpkg.com/blueimp-file-upload/-/blueimp-file-upload-9.9.0.tgz#316f1f710b92f2a5be4f079cf50937dfb2de6ab5" - integrity sha1-MW8fcQuS8qW+Twec9Qk337LearU= +blueimp-canvas-to-blob@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.5.0.tgz#5679ac32f6a2835821f0c3ad661719ff85a9236b" + integrity sha1-VnmsMvaig1gh8MOtZhcZ/4WpI2s= + +blueimp-file-upload@9.22.1: + version "9.22.1" + resolved "https://registry.yarnpkg.com/blueimp-file-upload/-/blueimp-file-upload-9.22.1.tgz#08a9fccbaf1ec930acf6242c217620b057f0ecc6" + integrity sha512-ezGkn/agWUWZOw8mYa5yYC9LvUlrT5bN3zk2fPlpLWgyhbBMz8BSGKO3M48BWlXWAeR+lVtEhy9xiG8FLnHEVw== + optionalDependencies: + blueimp-canvas-to-blob "3.5.0" + blueimp-load-image "2.12.2" + blueimp-tmpl "3.6.0" + +blueimp-load-image@2.12.2: + version "2.12.2" + resolved "https://registry.yarnpkg.com/blueimp-load-image/-/blueimp-load-image-2.12.2.tgz#6a17598aab858d4fbf01543e0631141b51057c87" + integrity sha1-ahdZiquFjU+/AVQ+BjEUG1EFfIc= + +blueimp-tmpl@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/blueimp-tmpl/-/blueimp-tmpl-3.6.0.tgz#a4910975d042e2bc03ba77f0e62d04f1548a524c" + integrity sha1-pJEJddBC4rwDunfw5i0E8VSKUkw= bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.6" @@ -3434,6 +3452,11 @@ class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" classnames@^2.2.5: version "2.2.5" @@ -4175,9 +4198,9 @@ csrf@~3.0.3: uid-safe "2.1.4" css-animation@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.4.1.tgz#5b8813125de0fbbbb0bbe1b472ae84221469b7a8" - integrity sha1-W4gTEl3g+7uwu+G0cq6EIhRpt6g= + version "1.5.0" + resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.5.0.tgz#c96b9097a5ef74a7be8480b45cc44e4ec6ca2bf5" + integrity sha512-hWYoWiOZ7Vr20etzLh3kpWgtC454tW5vn4I6rLANDgpzNSkO7UfOqyCEeaoBSG9CYWQpRkFWTWbWW8o3uZrNLw== dependencies: babel-runtime "6.x" component-classes "^1.2.5" @@ -4269,6 +4292,11 @@ csurf@^1.8.3: csrf "~3.0.3" http-errors "~1.5.0" +currency.js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/currency.js/-/currency.js-1.2.1.tgz#18d42ac74d833795051f4a310c83f041013a882f" + integrity sha512-7t0jYDZeQYCcaxpgwNOiBz8GaG/qdqTdo+kcTYgCuCNx1p2jLMpJt8h34TJ5INtN9jhryAiLK/atmjwUf13t4A== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -4375,8 +4403,6 @@ dd-trace@artsy/dd-trace-js#artsy: semver "^5.5.0" shimmer "^1.2.0" url-parse "^1.4.3" - optionalDependencies: - "@airbnb/node-memwatch" "^1.0.2" debounce@~1.0.0: version "1.0.2" @@ -4785,9 +4811,11 @@ dom-align@^1.7.0: integrity sha512-B85D4ef2Gj5lw0rK0KM2+D5/pH7yqNxg2mB+E8uzFaolpm7RQmsxEfjyEuNiF8UBBkffumYDeKRzTzc3LePP+w== dom-helpers@^3.2.1, dom-helpers@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" - integrity sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg== + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" @@ -4956,7 +4984,16 @@ electron-to-chromium@^1.3.62: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.63.tgz#6485f5f4f3375358aa8fa23c2029ada208483b8d" integrity sha512-Ec35NNY040HKuSxMAzBMgz/uUI78amSWpBUD9x2gN7R7gkb/wgAcClngWklcLP0/lm/g0UUYHnC/tUIlZj8UvQ== -electron@1.7.8, electron@^1.4.4: +electron@1.7.16: + version "1.7.16" + resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.16.tgz#40e6b5553e3a87ce7ea52eb05f7926bc40cab11c" + integrity sha512-k17+2K3hny6g1etWMkWvzEL06yapZGpqG66O3e5vxfzeHT3FgqrNa3xRF7znjaqvWQmmEdGFdSktQADjUZ0gog== + dependencies: + "@types/node" "^7.0.18" + electron-download "^3.0.1" + extract-zip "^1.0.3" + +electron@^1.4.4: version "1.7.8" resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.8.tgz#27b791a6895171a7d52991b99442cdbd10a3539d" integrity sha1-J7eRpolRcafVKZG5lELNvRCjU50= @@ -7212,10 +7249,10 @@ hoist-non-react-statics@^2.5.0: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz#fba3e7df0210eb9447757ca1a7cb607162f0a364" - integrity sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ== +hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.1.0.tgz#42414ccdfff019cd2168168be998c7b3bd5245c0" + integrity sha512-MYcYuROh7SBM69xHGqXEwQqDux34s9tz+sCnxJmN18kgWh6JFdTw/5YdZtqsOdZJXddE/wUpCzfEdDrJj8p0Iw== dependencies: react-is "^16.3.2" @@ -8601,7 +8638,7 @@ jest-worker@^23.2.0: dependencies: merge-stream "^1.0.1" -jest@^23.5.2: +jest@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" integrity sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw== @@ -9342,11 +9379,6 @@ lodash-es@^4.17.3, lodash-es@^4.2.0, lodash-es@^4.2.1: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" integrity sha1-3MHXVS4VCgZABzupyzHXDwMpUOc= -lodash-es@^4.17.5: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0" - integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q== - lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -9791,6 +9823,11 @@ memfs-or-file-map-to-github-branch@^1.1.0: resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.1.2.tgz#9d46c02481b7eca8e5ee8a94f170b7e0138cad67" integrity sha512-D2JKK2DTuVYQqquBWco3K6UfSVyVwmd58dgNqh+TgxHOZdTmR8I130gjMbVCkemDl/EzqDA62417cJxKL3/FFA== +memoize-one@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.3.tgz#cdfdd942853f1a1b4c71c5336b8c49da0bf0273c" + integrity sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw== + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -9799,14 +9836,6 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -memwatch-ng@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/memwatch-ng/-/memwatch-ng-1.2.0.tgz#b5a457a7c46b1db813b45a11ccfe09e8bd7fccfa" - integrity sha1-taRXp8RrHbgTtFoRzP4J6L1/zPo= - dependencies: - bindings "^1.2.1" - nan "^2.3.2" - meow@^3.1.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -10132,9 +10161,9 @@ module-details-from-path@^1.0.3: integrity sha1-EUyUlnPiqKNenTV4hSeqN7Z52is= moment-timezone@^0.5.13: - version "0.5.21" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.21.tgz#3cba247d84492174dbf71de2a9848fa13207b845" - integrity sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A== + version "0.5.23" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" + integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== dependencies: moment ">= 2.9.0" @@ -10235,7 +10264,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@^2.3.0, nan@^2.3.2: +nan@^2.3.0: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" integrity sha1-5P805slf37WuzAjeZZb0NgWn20U= @@ -11470,9 +11499,9 @@ prettier@^1.10.2: integrity sha512-TcdNoQIWFoHblurqqU6d1ysopjq7UX0oRcT/hJ8qvBAELiYWn+Ugf0AXdnzISEJ7vuhNnQ98N8jR8Sh53x4IZg== prettier@^1.14.3: - version "1.14.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895" - integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg== + version "1.15.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" + integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug== pretty-bytes@^1.0.2: version "1.0.4" @@ -12202,9 +12231,9 @@ react-dom@^16.5.0: schedule "^0.3.0" react-head@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/react-head/-/react-head-3.0.1.tgz#c7a3583c77328b9a5f9172100f8446f4b4778f05" - integrity sha512-GTblZjkKp17JhPKJWeK1Ogu2KXL+xVKB/+riYch97UiIfesP+/f4qexF+qb6a+KyiZJz2Sn+quHJRna2SoPToA== + version "3.0.2" + resolved "https://registry.yarnpkg.com/react-head/-/react-head-3.0.2.tgz#efe79b1e5805a18a5ffc58e6a810eeeb609d82bd" + integrity sha512-+wtK+pbxl17GfiBgqaSMrRob2rQZ0UJnRb1iEVzWQPUGC/TNMUTmpIzKx55xVKXbfsNTk8QAq0p3139YEnisUA== dependencies: "@babel/runtime" "^7.0.0" tiny-invariant "^1.0.0" @@ -12233,10 +12262,10 @@ react-is@^16.3.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" integrity sha512-ybEM7YOr4yBgFd6w8dJqwxegqZGJNBZl6U27HnGKuTZmDvVrD5quWOK/wAnMywiZzW+Qsk+l4X2c70+thp/A8Q== -react-is@^16.3.2: - version "16.6.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.0.tgz#456645144581a6e99f6816ae2bd24ee94bdd0c01" - integrity sha512-q8U7k0Fi7oxF1HvQgyBjPwDXeMplEsArnKt2iYhuIF86+GBbgLHdAmokL3XUFjTd7Q363OSNG55FOGUdONVn1g== +react-is@^16.3.2, react-is@^16.6.0: + version "16.6.3" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" + integrity sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA== react-is@^16.4.2: version "16.4.2" @@ -12251,7 +12280,7 @@ react-lazy-load-image-component@^1.1.1: lodash.debounce "^4.0.8" lodash.throttle "^4.1.1" -react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -12302,23 +12331,24 @@ react-redux@^5.0.2: prop-types "^15.5.10" react-redux@^5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" - integrity sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg== + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" + integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== dependencies: - hoist-non-react-statics "^2.5.0" - invariant "^2.0.0" - lodash "^4.17.5" - lodash-es "^4.17.5" + "@babel/runtime" "^7.1.2" + hoist-non-react-statics "^3.1.0" + invariant "^2.2.4" loose-envify "^1.1.0" - prop-types "^15.6.0" + prop-types "^15.6.1" + react-is "^16.6.0" + react-lifecycles-compat "^3.0.0" -react-relay-network-modern-ssr@^1.1.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/react-relay-network-modern-ssr/-/react-relay-network-modern-ssr-1.2.1.tgz#3cb0391f9827c78b5f9bf6dff021d9f1bb4a3351" - integrity sha512-Y2erct+4h6qt9FdB/0sdieLC582l5w1KBgtsSQHIHqz5n7RrFNAcJRSrnUGQT/+NC2jMqvE/ud34DaRSrbi8ng== +react-relay-network-modern-ssr@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/react-relay-network-modern-ssr/-/react-relay-network-modern-ssr-1.2.2.tgz#9f611872113cc91839600939b0d4edf97a4f8adc" + integrity sha512-35SHsvHS6IhZ7Sh9qXwjMhC5nZHhv5P9ERS5oti9u/B6GsQEC3cTwVCeSKIJNs+SxWkwQ6Z7DmzhPMWu1sNI0Q== -react-relay-network-modern@^2.3.1: +react-relay-network-modern@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/react-relay-network-modern/-/react-relay-network-modern-2.4.0.tgz#639ea0745545ab148d0c78714330e93dca7e031c" integrity sha512-LR/RhHcJclDCVEiwRhlRtf1iltSnbGSxS2rag+bAljMFJ0kOVSYUK3+NDPRbcHLRqbha1FuQXBVfHjjPE6jhMA== @@ -14350,12 +14380,7 @@ styled-components@^3.4.5: stylis-rule-sheet "^0.0.10" supports-color "^3.2.3" -styled-reset@^1.3.7: - version "1.6.0" - resolved "https://registry.yarnpkg.com/styled-reset/-/styled-reset-1.6.0.tgz#d10a0d419cd4d3163f324615554fdee7e13b5c47" - integrity sha512-UI+qgAaEPp7tPksAWn5voIoiefGq63jPaIdOBvcxNDwc4ZrEwfmwQMjExrmiJTL0EpsRGJ3nOhS0x7Kkqu5nUg== - -styled-system@^3.0.3, styled-system@^3.1.11: +styled-system@^3.1.11: version "3.1.11" resolved "https://registry.yarnpkg.com/styled-system/-/styled-system-3.1.11.tgz#a91a38cf7a0f0e625b897a04fdd506a359a3629f" integrity sha512-d0p32F7Y55uRWNDb1P0JcIiGVi13ZxiSCvn8zNS68exAKW9Q5jp+IGTXUIavQOD/J8r3tydtE3vRk8Ii2i39HA== @@ -14768,9 +14793,9 @@ timespan@~2.3.0: integrity sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk= tiny-invariant@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.1.tgz#54700d039a0384ef2e83afd0e81af84c6d67b140" - integrity sha512-avcKCNM2yJqD33H/WJLgIu3QNRXFOjARGJSdLi8IpKESCyhm67vyPWTRjnIbX2uI/pn52tqYcao333R3PBVhbQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.3.tgz#91efaaa0269ccb6271f0296aeedb05fc3e067b7a" + integrity sha512-ytQx8T4DL8PjlX53yYzcIC0WhIZbpR0p1qcYjw2pHu3w6UtgWwFJQ/02cnhOnBBhlFx/edUIfcagCaQSe3KMWg== tmp@^0.0.29: version "0.0.29" @@ -15121,7 +15146,12 @@ ua-parser-js@^0.7.12: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" integrity sha1-BMgamb3V3FImPqKdJMa/jUgYpLs= -ua-parser-js@^0.7.18, ua-parser-js@^0.7.9: +ua-parser-js@^0.7.18: + version "0.7.19" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b" + integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ== + +ua-parser-js@^0.7.9: version "0.7.18" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" integrity sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA== @@ -15216,6 +15246,14 @@ underscore.string@^3.2.2: sprintf-js "^1.0.3" util-deprecate "^1.0.2" +underscore.string@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" + integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== + dependencies: + sprintf-js "^1.0.3" + util-deprecate "^1.0.2" + underscore.string@~2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b" @@ -15506,7 +15544,7 @@ uuid@3.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" integrity sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg= -uuid@^3.0.0, uuid@^3.1.0: +uuid@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.0.tgz#b237147804881d7b86f40a7ff8f590f15c37de32" integrity sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw== @@ -15516,7 +15554,7 @@ uuid@^3.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" integrity sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE= -uuid@^3.3.2: +uuid@^3.1.0, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==