diff --git a/Dockerfile b/Dockerfile
index 2f07b2390b0..4b8881b587e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,8 +2,6 @@ FROM node:12.14-alpine
WORKDIR /app
-ENV CDN_PRODUCTION_URL=https://d1s2w0upia4e9w.cloudfront.net CDN_STAGING_URL=https://d1rmpw1xlv9rxa.cloudfront.net
-
# Install system dependencies
# Add deploy user
RUN apk --no-cache --quiet add \
diff --git a/package.json b/package.json
index 559f5806737..764948ff759 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
"@artsy/gemup": "0.0.3",
"@artsy/palette": "7.1.0",
"@artsy/passport": "1.1.11",
- "@artsy/reaction": "25.17.1",
+ "@artsy/reaction": "25.18.0",
"@artsy/stitch": "6.1.6",
"@babel/core": "7.6.0",
"@babel/node": "7.6.1",
diff --git a/src/desktop/apps/article/__tests__/routes.jest.ts b/src/desktop/apps/article/__tests__/routes.jest.ts
index 337d4470d16..76499c9ad00 100644
--- a/src/desktop/apps/article/__tests__/routes.jest.ts
+++ b/src/desktop/apps/article/__tests__/routes.jest.ts
@@ -5,18 +5,21 @@ import * as routes from "../routes"
import { extend } from "lodash"
import { GalleryInsightsRedirects } from "../gallery_insights_redirects"
const Article = require("desktop/models/article.coffee")
-const Channel = require("desktop/models/channel.coffee")
jest.mock("desktop/lib/positronql", () => ({
positronql: jest.fn(),
}))
+jest.mock("lib/metaphysics.coffee", () => jest.fn())
+
jest.mock("sharify", () => ({
data: {
ARTSY_EDITORIAL_CHANNEL: "123",
GALLERY_INSIGHTS_CHANNEL: "987",
APP_URL: "https://artsy.net",
EOY_2018_ARTISTS: "5bf30690d8b9430baaf6c6de",
+ PC_ARTSY_CHANNEL: "5759e508b5989e6f98f77999",
+ PC_AUCTION_CHANNEL: "5759e4d7b5989e6f98f77997",
},
}))
@@ -26,6 +29,7 @@ jest.mock("@artsy/stitch", () => ({
const positronql = require("desktop/lib/positronql").positronql as jest.Mock
const stitch = require("@artsy/stitch").stitch as jest.Mock
+const metaphysics = require("lib/metaphysics.coffee") as jest.Mock
describe("Article Routes", () => {
let req
@@ -68,6 +72,7 @@ describe("Article Routes", () => {
afterEach(() => {
positronql.mockClear()
stitch.mockClear()
+ metaphysics.mockClear()
})
describe("#index", () => {
@@ -159,7 +164,9 @@ describe("Article Routes", () => {
positronql.mockReturnValue(Promise.resolve({ article }))
routes.index(req, res, next).then(() => {
- expect(res.render.mock.calls[0][0]).toBe("article")
+ const { data, locals } = stitch.mock.calls[0][0]
+ expect(locals.assetPackage).toBe("article")
+ expect(data.article.title).toBe("New York's Next Art District")
done()
})
})
@@ -333,39 +340,71 @@ describe("Article Routes", () => {
})
describe("#classic", () => {
- let channel
+ it("renders a classic article", done => {
+ routes.classic(req, res, next, fixtures.ClassicArticle).then(() => {
+ const { data } = stitch.mock.calls[0][0]
+ expect(data.article.title).toBe(
+ "New Study of Yale Grads Shows the Gender Pay Gap for Artists Is Not So Simple"
+ )
+ done()
+ })
+ })
- beforeEach(() => {
- channel = new Channel({ name: "Foo" })
- article = extend(fixtures.ClassicArticle, {
- slug: "foobar",
+ it("fetches partner for gallery promoted content", done => {
+ article = Object.assign({}, fixtures.ClassicArticlePromotedContent, {
+ channel_id: sd.PC_ARTSY_CHANNEL,
+ partner_ids: ["123"],
+ sale: null,
})
+ metaphysics.mockReturnValue(
+ Promise.resolve({ partner: fixtures.ClassicArticlePartner })
+ )
- Article.prototype.fetchWithRelated.mockImplementation(options => {
- options.success({ article: new Article(article), channel })
+ routes.classic(req, res, next, article).then(() => {
+ const { data } = stitch.mock.calls[0][0]
+ expect(data.article.partner.name).toBe("Contessa Gallery")
+ done()
})
})
- it("renders a classic article", () => {
- routes.classic(req, res, next)
- expect(res.render.mock.calls[0][1].article.get("slug")).toBe("foobar")
- expect(res.render.mock.calls[0][1].channel.get("name")).toBe("Foo")
+ it("fetches sale for auction promoted content", done => {
+ article = Object.assign({}, fixtures.ClassicArticlePromotedContent)
+ delete article.sale
+ metaphysics.mockReturnValue(
+ Promise.resolve({ sale: fixtures.ClassicArticleSale })
+ )
+
+ routes.classic(req, res, next, article).then(() => {
+ const { data } = stitch.mock.calls[0][0]
+ expect(data.article.sale.name).toBe("ICI: Benefit Auction 2019")
+ done()
+ })
})
- it("renders a ghosted article (no channel)", () => {
- Article.prototype.fetchWithRelated.mockImplementationOnce(options => {
- options.success({ article: new Article(article) })
+ it("renders a ghosted article (no channel)", done => {
+ article = Object.assign({}, fixtures.ClassicArticle)
+ delete article.channel_id
+ delete article.author
+ delete article.partner_channel_id
+
+ routes.classic(req, res, next, article).then(() => {
+ const { data } = stitch.mock.calls[0][0]
+ expect(data.article.title).toBe(
+ "New Study of Yale Grads Shows the Gender Pay Gap for Artists Is Not So Simple"
+ )
+ done()
})
- routes.classic(req, res, next)
- expect(res.render.mock.calls[0][1].article.get("slug")).toBe("foobar")
})
- it("sets the correct jsonld", () => {
- routes.classic(req, res, next)
- expect(res.locals.jsonLD).toMatch(
- "Gender Pay Gap for Artists Is Not So Simple"
- )
- expect(res.locals.jsonLD).toMatch("Partner")
+ it("sets the correct jsonld", done => {
+ routes.classic(req, res, next, fixtures.ClassicArticle).then(() => {
+ const {
+ data: { jsonLD },
+ } = stitch.mock.calls[0][0]
+ expect(jsonLD).toMatch("Gender Pay Gap for Artists Is Not So Simple")
+ expect(jsonLD).toMatch("Partner")
+ done()
+ })
})
})
diff --git a/src/desktop/apps/article/client/__tests__/classic.test.js b/src/desktop/apps/article/client/__tests__/classic.test.js
deleted file mode 100644
index 56b163929c4..00000000000
--- a/src/desktop/apps/article/client/__tests__/classic.test.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import _ from "underscore"
-import sinon from "sinon"
-import Backbone from "backbone"
-import benv from "benv"
-
-describe("Classic Article", () => {
- let init
- let RewireApi
- let rewire
- let rewires = []
-
- beforeEach(done => {
- benv.setup(() => {
- benv.expose({
- $: benv.require("jquery"),
- jQuery: benv.require("jquery"),
- })
- Backbone.$ = window.$
- sinon.stub(Backbone, "sync")
-
- rewire = require("rewire")("../classic")
- init = rewire.init
- rewires.push(rewire.__set__("$", window.$))
- done()
- })
- })
-
- afterEach(() => {
- Backbone.sync.restore()
- benv.teardown()
- rewires.forEach(reset => reset())
- })
-
- it("initializes ArticleView", () => {
- rewire.__set__("ArticleView", sinon.stub())
- rewire.__set__("sd", {
- ARTICLE: {
- title: "Foo",
- },
- })
- const ArticleView = sinon.stub()
- rewire.__set__("ArticleView", ArticleView)
- init()
- ArticleView.args[0][0].article.get("title").should.equal("Foo")
- })
-
- it("initializes TeamChannelNavView", () => {
- rewire.__set__("ArticleView", sinon.stub())
- rewire.__set__("sd", {
- ARTICLE_CHANNEL: {
- type: "team",
- },
- })
- const TeamChannelNavView = sinon.stub()
- rewire.__set__("TeamChannelNavView", TeamChannelNavView)
- init()
- TeamChannelNavView.callCount.should.equal(1)
- })
-
- it("initializes ArticlesGridView", () => {
- rewire.__set__("ArticleView", sinon.stub())
- rewire.__set__("sd", {
- ARTICLE_CHANNEL: {
- type: "team",
- id: "123",
- name: "Team Channel",
- },
- })
- const ArticlesGridView = sinon.stub()
- rewire.__set__("ArticlesGridView", ArticlesGridView)
- init()
- ArticlesGridView.callCount.should.equal(1)
- ArticlesGridView.args[0][0].header.should.equal("More from Team Channel")
- Backbone.sync.callCount.should.equal(1)
- })
-
- xit("sets up promoted content gallery", done => {
- rewire.__set__("sd", {
- ARTICLE: {
- title: "Foo",
- partner_ids: ["789"],
- channel_id: "123",
- },
- PC_ARTSY_CHANNEL: "123",
- PC_AUCTION_CHANNEL: null,
- })
- const data = {
- partner: {
- name: "Lisson Gallery",
- type: "Gallery",
- profile: {
- href: "lisson-gallery",
- image: "",
- },
- },
- }
- rewire.__set__("metaphysics", sinon.stub().resolves(data))
- $("body").html('
')
- init()
- _.defer(() => {
- const html = $("#articles-show").html()
- html.should.containEql("Promoted Content")
- html.should.containEql("Lisson Gallery")
- html.should.containEql("Explore Gallery")
- done()
- })
- })
-
- xit("sets up promoted content auctions", done => {
- rewire.__set__("sd", {
- ARTICLE: {
- title: "Foo",
- auction_ids: ["789"],
- channel_id: "123",
- },
- PC_ARTSY_CHANNEL: null,
- PC_AUCTION_CHANNEL: "123",
- })
- const data = {
- sale: {
- name: "Summer School",
- href: "/auction/summer-school",
- cover_image: "",
- },
- }
- rewire.__set__("metaphysics", sinon.stub().resolves(data))
- $("body").html('')
- init()
- _.defer(() => {
- const html = $("#articles-show").html()
- html.should.containEql("Promoted Content")
- html.should.containEql("Summer School")
- html.should.containEql("Explore Auction")
- done()
- })
- })
-})
diff --git a/src/desktop/apps/article/client/article.js b/src/desktop/apps/article/client/article.tsx
similarity index 100%
rename from src/desktop/apps/article/client/article.js
rename to src/desktop/apps/article/client/article.tsx
diff --git a/src/desktop/apps/article/client/classic.js b/src/desktop/apps/article/client/classic.js
deleted file mode 100644
index 47df07def97..00000000000
--- a/src/desktop/apps/article/client/classic.js
+++ /dev/null
@@ -1,107 +0,0 @@
-// Initializes all client-side Backbone views for "classic" layouts
-
-import _$ from "jquery"
-import { data as _sd } from "sharify"
-import {
- auctionQuery,
- partnerQuery,
-} from "desktop/apps/article/queries/promotedContent"
-import metaphysics from "lib/metaphysics.coffee"
-import Article from "desktop/models/article.coffee"
-import Articles from "desktop/collections/articles.coffee"
-import _ArticlesGridView from "desktop/components/articles_grid/view.coffee"
-import _ArticleView from "desktop/components/article/client/view.coffee"
-import Channel from "desktop/models/channel.coffee"
-import _TeamChannelNavView from "desktop/components/channel_nav/view.coffee"
-
-// FIXME: Rewire
-let $ = _$
-let sd = _sd
-let ArticlesGridView = _ArticlesGridView
-let TeamChannelNavView = _TeamChannelNavView
-let ArticleView = _ArticleView
-
-const promotedTemplate = args => {
- return require("desktop/apps/article/templates/promoted_content.jade")(args)
-}
-
-export const init = () => {
- const article = new Article(sd.ARTICLE)
- const channel = new Channel(sd.ARTICLE_CHANNEL)
-
- new ArticleView({
- el: $("body"),
- article,
- })
-
- if (channel.isTeam()) {
- new TeamChannelNavView({
- el: $("body"),
- $content: $(".article-content"),
- offset: 0,
- })
- }
-
- setupFooterArticles(channel)
- setupPromotedContent(article)
-}
-
-const setupFooterArticles = channel => {
- const data = {
- published: true,
- sort: "-published_at",
- limit: 12,
- channel_id: channel.get("id"),
- }
- const collection = new Articles()
-
- new ArticlesGridView({
- el: $("#articles-footer").addClass("articles-grid"),
- hideMore: true,
- header: `More from ${channel.get("name") || "Artsy"}`,
- collection,
- })
-
- collection.fetch({
- data: data,
- })
-}
-
-const setupPromotedContent = article => {
- const channel = article.get("channel_id")
- if (channel === sd.PC_ARTSY_CHANNEL && article.get("partner_ids")) {
- const send = {
- method: "post",
- query: partnerQuery(article.get("partner_ids")[0]),
- }
- metaphysics(send).then(data => {
- const cropped = data.partner.profile.image.cropped
- const src = cropped ? cropped.url : ""
- $("#articles-show").prepend(
- promotedTemplate({
- src,
- name: data.partner.name,
- href: data.partner.profile.href,
- type: data.partner.type === "Gallery" ? "Gallery" : "Institution",
- })
- )
- })
- } else if (channel === sd.PC_AUCTION_CHANNEL && article.get("auction_ids")) {
- const send = {
- method: "post",
- query: auctionQuery(article.get("auction_ids")[0]),
- }
- metaphysics(send).then(data => {
- const cropped = data.sale.cover_image.cropped
- const src = cropped ? cropped.url : ""
- $("#articles-show").prepend(
- promotedTemplate({
- src,
- name: data.sale.name,
- href: data.sale.href,
- type: "Auction",
- })
- )
- })
- }
-}
diff --git a/src/desktop/apps/article/components/App.tsx b/src/desktop/apps/article/components/App.tsx
index 67c75cb57ae..c2b5bc338e6 100644
--- a/src/desktop/apps/article/components/App.tsx
+++ b/src/desktop/apps/article/components/App.tsx
@@ -7,11 +7,13 @@ import { EditButton } from "desktop/apps/article/components/EditButton"
import { ArticleLayout } from "./layouts/Article"
import { data as sd } from "sharify"
import { ArticleProps } from "@artsy/reaction/dist/Components/Publishing/Article"
+import { ClassicArticleLayout } from "desktop/apps/article/components/layouts/Classic"
export interface AppProps extends ArticleProps {
templates?: {
SuperArticleFooter: string
SuperArticleHeader: string
+ ArticlesGridView: string
}
}
@@ -39,6 +41,9 @@ export class App extends React.Component {
)
}
+ case "classic": {
+ return
+ }
default: {
return
}
diff --git a/src/desktop/apps/article/components/layouts/Article.tsx b/src/desktop/apps/article/components/layouts/Article.tsx
index b150b9c6169..f23c89791c3 100644
--- a/src/desktop/apps/article/components/layouts/Article.tsx
+++ b/src/desktop/apps/article/components/layouts/Article.tsx
@@ -1,23 +1,19 @@
import React from "react"
import { once } from "lodash"
import { Article } from "@artsy/reaction/dist/Components/Publishing/Article"
-import {
- ModalOptions,
- ModalType,
-} from "@artsy/reaction/dist/Components/Authentication/Types"
+import { ModalType } from "@artsy/reaction/dist/Components/Authentication/Types"
import { AppProps } from "../App"
import { InfiniteScrollArticle } from "../InfiniteScrollArticle"
-import { shouldAdRender } from "desktop/apps/article/helpers"
+import {
+ shouldAdRender,
+ handleOpenAuthModal,
+} from "desktop/apps/article/helpers"
const SuperArticleView = require("desktop/components/article/client/super_article.coffee")
const ArticleModel = require("desktop/models/article.coffee")
const Cookies = require("desktop/components/cookies/index.coffee")
const mediator = require("desktop/lib/mediator.coffee")
-interface ArticleModalOptions extends ModalOptions {
- signupIntent: string
-}
-
export class ArticleLayout extends React.Component {
componentDidMount() {
const { article, isSuper } = this.props
@@ -51,7 +47,7 @@ export class ArticleLayout extends React.Component {
"scroll",
once(() => {
setTimeout(() => {
- this.handleOpenAuthModal("register", {
+ handleOpenAuthModal("register", {
mode: ModalType.signup,
intent: "Viewed editorial",
signupIntent: "signup",
@@ -70,13 +66,6 @@ export class ArticleLayout extends React.Component {
}
}
- handleOpenAuthModal = (mode, options: ArticleModalOptions) => {
- mediator.trigger("open:auth", {
- mode,
- ...options,
- })
- }
-
render() {
const {
article,
@@ -110,7 +99,7 @@ export class ArticleLayout extends React.Component {
isMobile={isMobile}
isLoggedIn={isLoggedIn}
isSuper={isSuper}
- onOpenAuthModal={this.handleOpenAuthModal}
+ onOpenAuthModal={handleOpenAuthModal}
relatedArticlesForPanel={article.relatedArticlesPanel}
relatedArticlesForCanvas={article.relatedArticlesCanvas}
showTooltips={showTooltips}
@@ -121,7 +110,7 @@ export class ArticleLayout extends React.Component {
{
+ componentDidMount() {
+ const { article } = this.props
+
+ if (article.channel_id) {
+ this.setupFooterArticles()
+ }
+ }
+
+ setupFooterArticles = () => {
+ const { article } = this.props
+ const collection = new Articles()
+ // @ts-ignore
+ const _ArticlesGridView = new ArticlesGridView({
+ el: document.querySelector("#articles-footer"),
+ hideMore: true,
+ header: `More from ${article.channel.name || "Artsy"}`,
+ collection,
+ })
+
+ collection.fetch({
+ data: {
+ published: true,
+ sort: "-published_at",
+ limit: 12,
+ channel_id: article.channel_id,
+ },
+ })
+ }
+
+ render() {
+ const {
+ article,
+ isLoggedIn,
+ isMobile,
+ templates: { ArticlesGridView } = {} as any,
+ } = this.props
+
+ return (
+
+
+
+ {article.channel_id && (
+
+ )}
+
+ )
+ }
+}
diff --git a/src/desktop/apps/article/components/layouts/__tests__/Classic.jest.tsx b/src/desktop/apps/article/components/layouts/__tests__/Classic.jest.tsx
new file mode 100644
index 00000000000..00821b30b5e
--- /dev/null
+++ b/src/desktop/apps/article/components/layouts/__tests__/Classic.jest.tsx
@@ -0,0 +1,65 @@
+import React from "react"
+import { ClassicArticleLayout } from "../Classic"
+import { mount } from "enzyme"
+import {
+ ClassicArticle,
+ ClassicArticleInternalChannel,
+ ClassicArticlePromotedContent,
+} from "@artsy/reaction/dist/Components/Publishing/Fixtures/Articles"
+jest.mock("desktop/collections/articles.coffee")
+jest.mock("desktop/components/articles_grid/view.coffee")
+const ArticlesGridView = require("desktop/components/articles_grid/view.coffee") as jest.Mock
+
+describe("ClassicArticleLayout", () => {
+ let props
+ const getWrapper = (passedProps = props) => {
+ return mount()
+ }
+
+ beforeEach(() => {
+ props = {
+ article: ClassicArticle,
+ templates: {
+ ArticlesGridView: "articles-grid-view",
+ },
+ }
+ })
+
+ it("renders an article", () => {
+ const component = getWrapper().html()
+ expect(component).toMatch("ClassicHeader")
+ expect(component).toMatch(
+ "New Study of Yale Grads Shows the Gender Pay Gap for Artists Is Not So Simple"
+ )
+ expect(component).toMatch("Joanne Artman Gallery")
+ })
+
+ it("renders a team-channel article", () => {
+ props.article = ClassicArticleInternalChannel
+ const component = getWrapper().html()
+ expect(component).toMatch("ClassicHeader")
+ expect(component).toMatch("Consignments Intern")
+ expect(component).toMatch("Artsy Jobs")
+ })
+
+ it("renders a sponsored article", () => {
+ props.article = ClassicArticlePromotedContent
+ const component = getWrapper().html()
+ expect(component).toMatch("ClassicHeader")
+ expect(component).toMatch("Promoted Content")
+ expect(component).toMatch(
+ "ICI: Benefit Auction 2019 Curatorial Committee Picks"
+ )
+ expect(component).toMatch("Independent Curators International")
+ })
+
+ it("renders ArticlesGrid", () => {
+ props.article = ClassicArticleInternalChannel
+ const component = getWrapper().html()
+ expect(component).toMatch("articles-grid-view")
+ expect(ArticlesGridView).toBeCalled()
+ expect(ArticlesGridView.mock.calls[0][0].header).toMatch(
+ "More from Artsy Jobs"
+ )
+ })
+})
diff --git a/src/desktop/apps/article/gallery_insights_redirects.ts b/src/desktop/apps/article/gallery_insights_redirects.ts
index 055dd0d97f1..ee1b6fe4f70 100644
--- a/src/desktop/apps/article/gallery_insights_redirects.ts
+++ b/src/desktop/apps/article/gallery_insights_redirects.ts
@@ -8,7 +8,7 @@ export const GalleryInsightsRedirects = {
"gallery-insights-brett-gorvy-new-storefont":
"resource/brett-gorvy-online-storefront",
- "gallery-insights-the-pop-up-gallery-checklist?": "resource/pop-up-galleries",
+ "gallery-insights-the-pop-up-gallery-checklist": "resource/pop-up-galleries",
"gallery-insights-artful-pitch": "resource/press-for-your-gallery",
diff --git a/src/desktop/apps/article/helpers.tsx b/src/desktop/apps/article/helpers.tsx
index de0659c395a..58584bca8d6 100644
--- a/src/desktop/apps/article/helpers.tsx
+++ b/src/desktop/apps/article/helpers.tsx
@@ -1,6 +1,8 @@
import { isCustomEditorial } from "./editorial_features"
+import { ModalOptions } from "@artsy/reaction/dist/Components/Authentication/Types"
const { stringifyJSONForWeb } = require("desktop/components/util/json.coffee")
const Article = require("desktop/models/article.coffee")
+const mediator = require("desktop/lib/mediator.coffee")
// Helper method to determine how frequently ads should be rendered in Article components
export const shouldAdRender = (
@@ -70,3 +72,14 @@ export const getJsonLd = article => {
const articleModel = new Article(article)
return stringifyJSONForWeb(articleModel.toJSONLD())
}
+
+interface ArticleModalOptions extends ModalOptions {
+ signupIntent: string
+}
+
+export const handleOpenAuthModal = (mode, options: ArticleModalOptions) => {
+ mediator.trigger("open:auth", {
+ mode,
+ ...options,
+ })
+}
diff --git a/src/desktop/apps/article/queries/articleBody.js b/src/desktop/apps/article/queries/articleBody.js
index 21df63bd546..4b93251b057 100644
--- a/src/desktop/apps/article/queries/articleBody.js
+++ b/src/desktop/apps/article/queries/articleBody.js
@@ -17,6 +17,9 @@ export const articleBody = `
featured
channel_id
partner_channel_id
+ auction_ids
+ partner_ids
+ lead_paragraph
indexable
keywords
published
@@ -64,6 +67,9 @@ export const articleBody = `
author
image_url
}
+ author {
+ name
+ }
authors {
image_url
twitter_handle
@@ -71,6 +77,9 @@ export const articleBody = `
id
bio
}
+ channel {
+ name
+ }
contributing_authors {
name
}
diff --git a/src/desktop/apps/article/routes.tsx b/src/desktop/apps/article/routes.tsx
index c9353fd2295..f16fae259ba 100644
--- a/src/desktop/apps/article/routes.tsx
+++ b/src/desktop/apps/article/routes.tsx
@@ -27,6 +27,10 @@ import {
} from "./helpers"
import cheerio from "cheerio"
import React from "react"
+import {
+ partnerQuery,
+ auctionQuery,
+} from "desktop/apps/article/queries/promotedContent"
import { ArticleMeta } from "@artsy/reaction/dist/Components/Publishing/ArticleMeta"
import { GalleryInsightsRedirects } from "./gallery_insights_redirects"
const Articles = require("desktop/collections/articles.coffee")
@@ -34,6 +38,7 @@ const markdown = require("desktop/components/util/markdown.coffee")
const { crop, resize } = require("desktop/components/resizer/index.coffee")
const Article = require("desktop/models/article.coffee")
const { stringifyJSONForWeb } = require("desktop/components/util/json.coffee")
+const metaphysics = require("lib/metaphysics.coffee")
export const index = async (req, res, next) => {
let articleId = req.params.slug
@@ -72,7 +77,23 @@ export const index = async (req, res, next) => {
return res.redirect("https://partners.artsy.net")
}
}
- return classic(req, res, next)
+ if (req.params.slug !== article.slug) {
+ // Redirect to most recent slug
+ return res.redirect(`/article/${article.slug}`)
+ }
+
+ if (article.partner_channel_id) {
+ // redirect partner channels to partner page
+ const { partner } = await metaphysics({
+ method: "post",
+ query: partnerQuery(article.partner_channel_id),
+ }).then(data => data)
+
+ return res.redirect(
+ `/${partner.default_profile_id}/article/${article.slug}`
+ )
+ }
+ return classic(req, res, next, article)
}
if (isVanguardSubArticle(article)) {
@@ -95,7 +116,7 @@ export const index = async (req, res, next) => {
}
if (
- !["standard", "feature"].includes(article.layout) &&
+ !["classic", "standard", "feature"].includes(article.layout) &&
req.path.includes("/article")
) {
return res.redirect(`/${article.layout}/${article.slug}${search}`)
@@ -177,42 +198,65 @@ export const index = async (req, res, next) => {
}
}
-export const classic = (req, res, _next) => {
- const article = new Article({
- id: req.params.slug,
- })
- const accessToken = req.user ? req.user.get("accessToken") : null
+export const classic = async (_req, res, next, article) => {
+ const { CURRENT_USER, IS_MOBILE } = res.locals.sd
+ const isMobile = IS_MOBILE
+ const isLoggedIn = typeof CURRENT_USER !== "undefined"
- article.fetchWithRelated({
- accessToken,
- error: res.backboneError,
- success: data => {
- if (req.params.slug !== data.article.get("slug")) {
- return res.redirect(`/article/${data.article.get("slug")}`)
- }
+ if (article.channel_id === sd.PC_ARTSY_CHANNEL && article.partner_ids) {
+ const send = {
+ method: "post",
+ query: partnerQuery(article.partner_ids[0]),
+ }
- if (data.partner) {
- return res.redirect(
- `/${data.partner.get(
- "default_profile_id"
- )}/article/${data.article.get("slug")}`
- )
- }
+ await metaphysics(send).then(data => {
+ article.partner = data.partner
+ })
+ }
- res.locals.sd.ARTICLE = data.article.toJSON()
- res.locals.sd.INCLUDE_SAILTHRU =
- res.locals.sd.ARTICLE && res.locals.sd.ARTICLE.published
- res.locals.sd.ARTICLE_CHANNEL = data.channel && data.channel.toJSON()
- res.locals.jsonLD = stringifyJSONForWeb(data.article.toJSONLD())
+ if (article.channel_id === sd.PC_AUCTION_CHANNEL && article.auction_ids) {
+ const send = {
+ method: "post",
+ query: auctionQuery(article.auction_ids[0]),
+ }
- res.render("article", {
- embed,
+ await metaphysics(send).then(data => {
+ article.sale = data.sale
+ })
+ }
+
+ try {
+ const layout = await stitch({
+ basePath: res.app.get("views"),
+ layout: getLayoutTemplate(article),
+ config: {
+ styledComponents: true,
+ },
+ blocks: {
+ head: () => ,
+ body: App,
+ },
+ locals: {
+ ...res.locals,
+ assetPackage: "article",
+ bodyClass: getBodyClass(article),
crop,
- resize,
- ...data,
- })
- },
- })
+ markdown,
+ },
+ data: {
+ article,
+ isLoggedIn,
+ isMobile,
+ jsonLD: getJsonLd(article),
+ },
+ templates: {
+ ArticlesGridView: "../../../components/articles_grid/view.coffee",
+ },
+ })
+ res.send(layout)
+ } catch (error) {
+ next(error)
+ }
}
export const amp = (req, res, next) => {
diff --git a/src/desktop/apps/experimental-app-shell/apps/search/searchMiddleware.tsx b/src/desktop/apps/experimental-app-shell/apps/search/searchMiddleware.tsx
index 0e599366cab..3b86352f8d8 100644
--- a/src/desktop/apps/experimental-app-shell/apps/search/searchMiddleware.tsx
+++ b/src/desktop/apps/experimental-app-shell/apps/search/searchMiddleware.tsx
@@ -66,6 +66,7 @@ export const searchMiddleware = async (req, res, next) => {
html: layout,
}
res.status(status).send(layout)
+ return
} catch (error) {
console.log(error)
next(error)
diff --git a/src/desktop/assets/article.coffee b/src/desktop/assets/article.coffee
index 0870aaa2b58..6a22a1ac143 100644
--- a/src/desktop/assets/article.coffee
+++ b/src/desktop/assets/article.coffee
@@ -5,9 +5,5 @@ layout = sd.ARTICLE?.layout
$ ->
if location.pathname.match('/article/|/video/|/series/|/news/')
- if layout is 'classic'
- { init } = require('../apps/article/client/classic.js')
- init()
- else
- { init } = require('../apps/article/client/article.js')
- init()
+ { init } = require('../apps/article/client/article.tsx')
+ init()
diff --git a/src/desktop/lib/global_client_setup.tsx b/src/desktop/lib/global_client_setup.tsx
index d7baedd3a17..967d0d2b932 100644
--- a/src/desktop/lib/global_client_setup.tsx
+++ b/src/desktop/lib/global_client_setup.tsx
@@ -2,12 +2,27 @@
* Set webpack public-path asset lookup to CDN in production, but only on
* the client, as we use the assetMiddleware helper to map URLs on the server.
* @see https://github.com/artsy/force/blob/master/src/lib/middleware/assetMiddleware.ts
+ *
+ * FIXME: Move this into Circle config and or Docker
*/
if (process.env.NODE_ENV === "production") {
- __webpack_public_path__ =
- (window.location.hostname === "www.artsy.net"
- ? process.env.CDN_PRODUCTION_URL
- : process.env.CDN_STAGING_URL) + "/assets/"
+ const { hostname } = window.location
+ let cdnUrl
+
+ // Production
+ if (hostname === "www.artsy.net") {
+ cdnUrl = "https://d1s2w0upia4e9w.cloudfront.net"
+
+ // Localhost
+ } else if (hostname === "localhost") {
+ cdnUrl = ""
+
+ // Everything else
+ } else {
+ cdnUrl = "https://d1rmpw1xlv9rxa.cloudfront.net"
+ }
+
+ __webpack_public_path__ = cdnUrl + "/assets/"
}
import $ from "jquery"
diff --git a/src/desktop/models/article.coffee b/src/desktop/models/article.coffee
index 2eb424d500f..e1a5701424f 100644
--- a/src/desktop/models/article.coffee
+++ b/src/desktop/models/article.coffee
@@ -208,14 +208,14 @@ module.exports = class Article extends Backbone.Model
new Article(id: id).fetch
cache: true
headers: 'X-Access-Token': accessToken
- success: (article) =>
+ success: (article) ->
superSubArticles.add article
getParselySection: ->
if @get('channel_id') is ARTSY_EDITORIAL_CHANNEL
'Editorial'
else if @get('channel_id')
- @get('channel')?.get('name')
+ @get('channel')?.name || @get('channel')?.get('name')
else if @get('partner_channel_id')
'Partner'
else
diff --git a/webpack/envs/baseConfig.js b/webpack/envs/baseConfig.js
index 71b81941913..8d56878db8e 100644
--- a/webpack/envs/baseConfig.js
+++ b/webpack/envs/baseConfig.js
@@ -65,8 +65,6 @@ exports.baseConfig = {
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify(NODE_ENV),
- CND_PRODUCTION_URL: JSON.stringify(process.env.CND_PRODUCTION_URL),
- CDN_STAGING_URL: JSON.stringify(process.env.CDN_STAGING_URL),
},
}),
// Remove moment.js localization files
diff --git a/yarn.lock b/yarn.lock
index 91c397b726f..d8708fff19e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -76,10 +76,10 @@
resolved "https://registry.yarnpkg.com/@artsy/react-html-parser/-/react-html-parser-3.0.2.tgz#6213d662441acf0bd8c9ee953aa77ac8d9cf1cdd"
integrity sha512-FXiRSqfSvwpz/QgwaqFvjJsbDmo9qMGpvUp/0p1V6muaJbGnfxkTSxAYWmYlFst6bmjhVxCYKxvgQhEVE03Wfg==
-"@artsy/reaction@25.17.1":
- version "25.17.1"
- resolved "https://registry.yarnpkg.com/@artsy/reaction/-/reaction-25.17.1.tgz#98797fdcb76bb24a492c7369fa3a81671b88967c"
- integrity sha512-eqqQPTxcsdfpHXlCWSwxuc2i1l+qcrTW40CB++PFxkdRcyqblWHhR4wq4A8D8lp7I7QLLKixyuKcN6vod3JzXQ==
+"@artsy/reaction@25.18.0":
+ version "25.18.0"
+ resolved "https://registry.yarnpkg.com/@artsy/reaction/-/reaction-25.18.0.tgz#d5b0b6f432bd526e037c5906cc55abb7816c79b7"
+ integrity sha512-65cXbo3vDBHMkuGZjsyZJmRtWx7jk/BtKDkUJ82eTIShx7tUQVsXQULtwmXmiVsXa5nVb70E1Myv+dEJm2d8Nw==
dependencies:
"@artsy/detect-responsive-traits" "^0.0.5"
"@artsy/fresnel" "^1.0.13"