diff --git a/data/schema.graphql b/data/schema.graphql
index 177ed388fa4..9c87b857f87 100644
--- a/data/schema.graphql
+++ b/data/schema.graphql
@@ -9337,6 +9337,7 @@ enum SearchEntity {
SALE
SHOW
TAG
+ VIEWING_ROOM
}
enum SearchMode {
diff --git a/package.json b/package.json
index 05a71c35b71..323a4c11a2c 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"start:prod": "yarn assets && yarn build:server && NODE_ENV=production yarn start",
"start": "scripts/start.sh",
"storybook": "concurrently --raw --kill-others 'yarn relay --watch' 'start-storybook --quiet -s ./public -p 9001'",
+ "sync-schema": "scripts/sync-schema-pull.sh",
"test:acceptance": "yarn acceptance src/test/acceptance/*.js",
"test:smoke": "scripts/smoke_test.sh",
"test": "scripts/test.sh",
@@ -285,6 +286,7 @@
"babel-plugin-styled-components": "1.11.1",
"benv": "3.3.0",
"cache-loader": "1.2.2",
+ "case-sensitive-paths-webpack-plugin": "2.3.0",
"coffee-loader": "0.8.0",
"coffeescript": "1.11.1",
"cypress": "5.1.0",
diff --git a/src/desktop/analytics/articles.js b/src/desktop/analytics/articles.js
deleted file mode 100644
index 574d439b866..00000000000
--- a/src/desktop/analytics/articles.js
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// Analytics for all things articles. This includes tests for share buttons
-// and potentionally other alternative layout options or more.
-//
-
-if (
- location.pathname.match("/article/") ||
- location.pathname.match("/2016-year-in-art")
-) {
- $(document.body)
- .on("click", ".article-social a", function () {
- const articleId = $(this).closest(".article-container").data("id")
- analytics.track("Article Share", {
- article_id: articleId,
- context_type: sd.ARTICLE ? "article_fixed" : "magazine_fixed",
- service: $(this).attr("data-service"),
- })
- })
- .on("click", ".article-share-fixed > a", function () {
- const articleId = $(this).closest(".article-container").data("id")
- analytics.track("Article Share", {
- article_id: articleId,
- context_type: "article_sticky",
- service: $(this).attr("data-service"),
- })
- })
- .on("click", ".article-sa-primary-logo a", function () {
- analytics.track("Clicked primary partner logo", {
- destination_path: $(this)[0].href,
- impression_type: "sa_primary_logo",
- context_type: "article_fixed",
- })
- })
- .on("click", ".article-sa-secondary-logo a", function () {
- analytics.track("Clicked secondary partner logo", {
- destination_path: $(this)[0].href,
- impression_type: "sa_secondary_logo",
- context_type: "article_fixed",
- })
- })
- .on("click", ".article-sa-cta-container a", function () {
- analytics.track("Clicked partner cta link", {
- destination_path: $(this)[0].href,
- impression_type: "sa_partner_cta",
- context_type: "article_fixed",
- })
- })
- .on("click", ".article-sa-footer-blurb a", function () {
- analytics.track("Clicked partner cta link in footer blurb", {
- destination_path: $(this)[0].href,
- impression_type: "sa_partner_cta",
- context_type: "article_fixed",
- })
- })
-}
diff --git a/src/desktop/analytics/embedded_inquiry.js b/src/desktop/analytics/embedded_inquiry.js
deleted file mode 100644
index f7276918690..00000000000
--- a/src/desktop/analytics/embedded_inquiry.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Events ported from apps/artwork/components/contact.coffee
-// & components/contact/inquiry.coffee
-
-;(function() {
- "use strict"
-
- // DOM events
- var $document = $(document)
-
- $document.on("click", ".js-send-embedded-inquiry", function() {
- analytics.track('Clicked "Contact Gallery" button', {
- id: $(this).data("artwork-id"),
- prequalify:
- sd.ARTWORK && sd.ARTWORK.partner && sd.ARTWORK.partner.pre_qualify,
- })
- })
-
- $document.on("mouseover", ".js-send-embedded-inquiry", function() {
- analytics.track("Hovered over contact form 'Send' button", {
- id: $(this).data("artwork-id"),
- })
- })
-})()
diff --git a/src/desktop/analytics/main_layout.ts b/src/desktop/analytics/main_layout.ts
index 35c1d92ac75..5918d7c2aca 100644
--- a/src/desktop/analytics/main_layout.ts
+++ b/src/desktop/analytics/main_layout.ts
@@ -7,6 +7,8 @@ import { data as sd } from "sharify"
import { reportLoadTimeToVolley } from "lib/volley"
import { match } from "path-to-regexp"
import { timeOnPageListener } from "./timeOnPageListener"
+import { getPageTypeFromClient } from "lib/getPageType"
+import { OwnerType } from "@artsy/cohesion"
// We exclude these routes from analytics.page calls because they're already
// taken care of in Reaction.
@@ -32,8 +34,7 @@ const excludedRoutes = [
// Track pageview
const pathname = new URL(window.location.href).pathname
-let slug = pathname.split("/")[2]
-let pageType = window.sd.PAGE_TYPE || pathname.split("/")[1]
+const { pageType, pageSlug } = getPageTypeFromClient()
const foundExcludedPath = excludedRoutes.some(excludedPath => {
const matcher = match(excludedPath, { decode: decodeURIComponent })
@@ -48,7 +49,7 @@ if (!foundExcludedPath) {
)
}
-if (pageType === "auction") {
+if (pageType === OwnerType.sale) {
// Let reaction track reaction-based app routes
const matcher = match("/auction/:saleID/bid(2)?/:artworkID", {
decode: decodeURIComponent,
@@ -57,7 +58,7 @@ if (pageType === "auction") {
if (!matchedBidRoute) {
window.addEventListener("load", function () {
// distinct event required for marketing integrations (Criteo)
- window.analytics.track("Auction Pageview", { auction_slug: slug })
+ window.analytics.track("Auction Pageview", { auction_slug: pageSlug })
})
}
}
diff --git a/src/desktop/analytics/timeOnPageListener.ts b/src/desktop/analytics/timeOnPageListener.ts
index 9e22fc26136..295933f88a6 100644
--- a/src/desktop/analytics/timeOnPageListener.ts
+++ b/src/desktop/analytics/timeOnPageListener.ts
@@ -1,11 +1,12 @@
import { trackEvent } from "./helpers"
-import { OwnerType, timeOnPage } from "@artsy/cohesion"
+import { timeOnPage } from "@artsy/cohesion"
+import { getPageTypeFromClient } from "lib/getPageType"
export const timeOnPageListener = (delay: number = 15000) => {
setTimeout(() => {
+ const { pageType, pageSlug } = getPageTypeFromClient()
const pathname = new URL(window.location.href).pathname
- const slug = pathname.split("/")[2]
- const pageType = window.sd.PAGE_TYPE || pathname.split("/")[1]
+
const referrer = window.analytics.__artsyClientSideRoutingReferrer
// Grab referrer from our trackingMiddleware in Reaction, since we're in a
// single-page-app context and the value will need to be refreshed on route
@@ -18,12 +19,12 @@ export const timeOnPageListener = (delay: number = 15000) => {
},
}
}
- const contextPageOwnerSlug = pageType === "partner" ? pathname : slug
+ const contextPageOwnerSlug = pageType === "partner" ? pathname : pageSlug
trackEvent(
timeOnPage({
contextPageOwnerSlug,
- contextPageOwnerType: OwnerType[pageType],
+ contextPageOwnerType: pageType,
}),
trackingOptions
)
diff --git a/src/desktop/apps/artsy-v2/apps/artist/artistMiddleware.tsx b/src/desktop/apps/artsy-v2/apps/artist/artistMiddleware.tsx
index 1e1ba7407bf..4daead9b948 100644
--- a/src/desktop/apps/artsy-v2/apps/artist/artistMiddleware.tsx
+++ b/src/desktop/apps/artsy-v2/apps/artist/artistMiddleware.tsx
@@ -1,10 +1,11 @@
-import { getPageType } from "../../utils/getPageType"
+import { OwnerType } from "@artsy/cohesion"
+import { getPageTypeFromReq } from "lib/getPageType"
export const artistMiddleware = (req, res, next) => {
- const { pageType, pageParts } = getPageType(req)
+ const { pageType, pageSlug } = getPageTypeFromReq(req)
- if (pageType === "artist") {
- const artistID = pageParts[2]
+ if (pageType === OwnerType.artist) {
+ const artistID = pageSlug
const user = req.user && req.user.toJSON()
const { APP_URL, IS_MOBILE, REFERRER } = res.locals.sd
const isExternalReferer = !(REFERRER && REFERRER.includes(APP_URL))
diff --git a/src/desktop/apps/artsy-v2/apps/artwork/artworkClient.tsx b/src/desktop/apps/artsy-v2/apps/artwork/artworkClient.tsx
index 1050bbab60d..765ea076547 100644
--- a/src/desktop/apps/artsy-v2/apps/artwork/artworkClient.tsx
+++ b/src/desktop/apps/artsy-v2/apps/artwork/artworkClient.tsx
@@ -1,6 +1,8 @@
import { enableIntercom } from "lib/intercom"
import { recordArtworkView } from "lib/components/record_artwork_view"
import { data as sd } from "sharify"
+import { getPageTypeFromClient } from "lib/getPageType"
+import { OwnerType } from "@artsy/cohesion"
export const artworkClient = () => {
const User = require("desktop/models/user.coffee")
@@ -13,11 +15,10 @@ export const artworkClient = () => {
const $ = require("jquery")
const mediator = require("desktop/lib/mediator.coffee")
- const pageType = window.location.pathname.split("/")[1]
+ const { pageType, pageSlug } = getPageTypeFromClient()
- if (pageType === "artwork") {
- const artworkSlug = location.pathname.replace(/\/artwork\//, "")
- recordArtworkView(artworkSlug, sd.CURRENT_USER)
+ if (pageType === OwnerType.artwork) {
+ recordArtworkView(pageSlug, sd.CURRENT_USER)
}
const openInquireableModal = (
diff --git a/src/desktop/apps/artsy-v2/apps/search/searchMiddleware.tsx b/src/desktop/apps/artsy-v2/apps/search/searchMiddleware.tsx
index 6c1b3367fe4..11b472cba57 100644
--- a/src/desktop/apps/artsy-v2/apps/search/searchMiddleware.tsx
+++ b/src/desktop/apps/artsy-v2/apps/search/searchMiddleware.tsx
@@ -3,12 +3,13 @@ import { stringify } from "querystring"
import { SearchResultsSkeleton } from "v2/Apps/Search/Components/SearchResultsSkeleton"
import { StitchWrapper } from "desktop/components/react/stitch_components/StitchWrapper"
import { stitch } from "@artsy/stitch"
-import { getPageType } from "../../utils/getPageType"
+import { getPageTypeFromReq } from "lib/getPageType"
+import { OwnerType } from "@artsy/cohesion"
export const searchMiddleware = async (req, res, next) => {
- const { pageType } = getPageType(req)
+ const { pageType } = getPageTypeFromReq(req)
- if (pageType === "search") {
+ if (pageType === OwnerType.search) {
try {
if (!req.query.term) {
if (req.query.q) {
diff --git a/src/desktop/apps/artsy-v2/client.tsx b/src/desktop/apps/artsy-v2/client.tsx
index 0e10315a495..0dd9da32ff6 100644
--- a/src/desktop/apps/artsy-v2/client.tsx
+++ b/src/desktop/apps/artsy-v2/client.tsx
@@ -6,6 +6,8 @@ import { data as sd } from "sharify"
import { artworkClient } from "./apps/artwork/artworkClient"
import { artistClient } from "./apps/artist/artistClient"
import { loadableReady } from "@loadable/component"
+import { getPageTypeFromClient } from "lib/getPageType"
+import { OwnerType } from "@artsy/cohesion"
const mediator = require("desktop/lib/mediator.coffee")
@@ -35,9 +37,9 @@ buildClientApp({
,
document.getElementById("react-root"),
() => {
- const pageType = window.location.pathname.split("/")[1]
+ const { pageType } = getPageTypeFromClient()
- if (pageType === "search") {
+ if (pageType === OwnerType.search) {
document.getElementById("loading-container").remove()
document.getElementById("search-page-header").remove()
}
@@ -45,7 +47,7 @@ buildClientApp({
)
})
})
- .catch((error) => {
+ .catch(error => {
console.error(error)
})
diff --git a/src/desktop/apps/artsy-v2/server.tsx b/src/desktop/apps/artsy-v2/server.tsx
index 8ab30a174ee..3d05d5db421 100644
--- a/src/desktop/apps/artsy-v2/server.tsx
+++ b/src/desktop/apps/artsy-v2/server.tsx
@@ -11,6 +11,7 @@ import { artistMiddleware } from "./apps/artist/artistMiddleware"
import { userRequiredMiddleware } from "./middleware/userRequiredMiddleware"
import { searchMiddleware } from "./apps/search/searchMiddleware"
import { handleCollectionToArtistSeriesRedirect } from "./apps/collection/collectionMiddleware"
+import { getPageTypeFromReq } from "lib/getPageType"
export const app = express()
@@ -58,8 +59,7 @@ app.get(
*/
async (req: Request, res, next) => {
try {
- const pageParts = req.path.split("/")
- const pageType = pageParts[1]
+ const { pageType } = getPageTypeFromReq(req)
const {
status,
diff --git a/src/desktop/apps/artsy-v2/utils/__tests__/getPageType.jest.ts b/src/desktop/apps/artsy-v2/utils/__tests__/getPageType.jest.ts
deleted file mode 100644
index 645c91d76d7..00000000000
--- a/src/desktop/apps/artsy-v2/utils/__tests__/getPageType.jest.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { getPageType } from "../getPageType"
-import { Request } from "express"
-
-describe("getPageType", () => {
- it("returns correct props", () => {
- const { pageParts, pageType } = getPageType({
- path: "/artist/test-artist",
- } as Request)
-
- expect(pageParts).toEqual(["", "artist", "test-artist"])
- expect(pageType).toEqual("artist")
- })
-})
diff --git a/src/desktop/apps/artsy-v2/utils/getPageType.ts b/src/desktop/apps/artsy-v2/utils/getPageType.ts
deleted file mode 100644
index 246aab4c8fa..00000000000
--- a/src/desktop/apps/artsy-v2/utils/getPageType.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Request } from "express"
-
-type PageTypes =
- | "artist"
- | "artwork"
- | "auction"
- | "auction-registration"
- | "auction-registration2"
- | "orders"
- | "search"
- | "identity-verification"
-
-export function getPageType(
- req: Request
-): {
- pageParts: string[]
- pageType: PageTypes
-} {
- const pageParts = req.path.split("/")
- const pageType = pageParts[1] as PageTypes
-
- return {
- pageParts,
- pageType,
- }
-}
diff --git a/src/desktop/assets/analytics.ts b/src/desktop/assets/analytics.ts
index 4562badee42..fc0092e76d5 100644
--- a/src/desktop/assets/analytics.ts
+++ b/src/desktop/assets/analytics.ts
@@ -44,11 +44,9 @@ $(() =>
onAnalyticsReady()
setupSplitTests()
- require("../analytics/articles.js")
require("../analytics/artwork.js")
require("../analytics/consignments.js")
require("../analytics/contact.js")
- require("../analytics/embedded_inquiry.js")
require("../analytics/galleries.js")
require("../analytics/inquiry_questionnaire.js")
require("../analytics/recently_viewed_artworks.js")
diff --git a/src/desktop/components/article/client/super_article.coffee b/src/desktop/components/article/client/super_article.coffee
index 04d6c7c209d..ab7680d5f4f 100644
--- a/src/desktop/components/article/client/super_article.coffee
+++ b/src/desktop/components/article/client/super_article.coffee
@@ -4,6 +4,11 @@ initCarousel = require '../../merry_go_round/horizontal_nav_mgr.coffee'
sd = require('sharify').data
module.exports = class SuperArticleView extends Backbone.View
+ events:
+ 'click .article-sa-primary-logo a': 'trackPrimaryLogoClick'
+ 'click .article-sa-secondary-logo a': 'trackSecondaryLogoClick'
+ 'click .article-sa-cta-container a': 'trackCtaClick'
+ 'click .article-sa-footer-blurb a': 'trackFooterBlurbClick'
initialize: (options) ->
{ @article } = options
@@ -79,3 +84,31 @@ module.exports = class SuperArticleView extends Backbone.View
else
@$superArticleNavToc.css 'height', '100vh'
@$body.addClass 'is-open'
+
+ trackPrimaryLogoClick: (e) ->
+ window.analytics.track("Clicked primary partner logo", {
+ destination_path: e.currentTarget.href,
+ impression_type: "sa_primary_logo",
+ context_type: "article_fixed",
+ })
+
+ trackSecondaryLogoClick: (e) ->
+ window.analytics.track("Clicked secondary partner logo", {
+ destination_path: e.currentTarget.href,
+ impression_type: "sa_secondary_logo",
+ context_type: "article_fixed",
+ })
+
+ trackCtaClick: (e) ->
+ window.analytics.track("Clicked partner cta link", {
+ destination_path: e.currentTarget.href,
+ impression_type: "sa_partner_cta",
+ context_type: "article_fixed",
+ })
+
+ trackFooterBlurbClick: (e) ->
+ window.analytics.track("Clicked partner cta link in footer blurb", {
+ destination_path: e.currentTarget.href,
+ impression_type: "sa_partner_cta",
+ context_type: "article_fixed",
+ })
\ No newline at end of file
diff --git a/src/desktop/components/article/client/view.coffee b/src/desktop/components/article/client/view.coffee
index f708ef062af..f0101764771 100644
--- a/src/desktop/components/article/client/view.coffee
+++ b/src/desktop/components/article/client/view.coffee
@@ -20,6 +20,8 @@ module.exports = class ArticleView extends Backbone.View
.articles-section-left-chevron': 'toggleSectionCarousel'
'click .article-video-play-button': 'playVideo'
'click .article-section-image-set__remaining, .article-section-image-set__image-container': 'toggleModal'
+ 'click .article-social a': 'trackArticleSocialShare'
+ 'click .article-share-fixed a': 'trackArticleFixedShare'
initialize: (options) ->
@user = CurrentUser.orNull()
@@ -269,3 +271,17 @@ module.exports = class ArticleView extends Backbone.View
else
$('.article-social').show()
, { offset: 'bottom-in-view' }
+
+ trackArticleSocialShare: (e) =>
+ window.analytics.track("Article Share", {
+ article_id: @article.id,
+ context_type: "article_fixed"
+ service: e.currentTarget.getAttribute('data-service'),
+ })
+
+ trackArticleFixedShare: (e) =>
+ window.analytics.track("Article Share", {
+ article_id: @article.id,
+ context_type: "article_sticky",
+ service: e.currentTarget.getAttribute('data-service'),
+ })
diff --git a/src/lib/__tests__/getPageType.jest.ts b/src/lib/__tests__/getPageType.jest.ts
new file mode 100644
index 00000000000..f3c56a41a57
--- /dev/null
+++ b/src/lib/__tests__/getPageType.jest.ts
@@ -0,0 +1,67 @@
+import {
+ formatOwnerTypes,
+ getPageTypeFromClient,
+ getPageTypeFromReq,
+} from "lib/getPageType"
+import { Request } from "express"
+import { OwnerType } from "@artsy/cohesion"
+
+describe("getPageTypeFromReq", () => {
+ it("returns expected values", () => {
+ const page = getPageTypeFromReq({
+ path: "/artist/test-artist",
+ } as Request)
+
+ expect(page.pageParts).toEqual(["", "artist", "test-artist"])
+ expect(page.pageType).toEqual("artist")
+ expect(page.pageSlug).toEqual("test-artist")
+ })
+
+ it("handles camelcasing", () => {
+ const page = getPageTypeFromReq({
+ path: "/artist-series/test-artist-series",
+ } as Request)
+
+ expect(page.pageParts).toEqual(["", "artist-series", "test-artist-series"])
+ expect(page.pageType).toEqual("artistSeries")
+ expect(page.pageSlug).toEqual("test-artist-series")
+ })
+})
+
+describe("getPageTypeFromClient", () => {
+ it("returns correct props", () => {
+ delete window.location
+ // @ts-ignore
+ window.location = new URL("https://artsy.net/artist/test-artist")
+ const page = getPageTypeFromClient()
+
+ expect(page.pageParts).toEqual(["", "artist", "test-artist"])
+ expect(page.pageType).toEqual("artist")
+ expect(page.pageSlug).toEqual("test-artist")
+ })
+
+ it("handles camelcasing", () => {
+ delete window.location
+ // @ts-ignore
+ window.location = new URL(
+ "https://artsy.net/artist-series/test-artist-series"
+ )
+ const page = getPageTypeFromClient()
+
+ expect(page.pageParts).toEqual(["", "artist-series", "test-artist-series"])
+ expect(page.pageType).toEqual("artistSeries")
+ expect(page.pageSlug).toEqual("test-artist-series")
+ })
+})
+
+describe("formatOwnerTypes", () => {
+ it("handles article path types", () => {
+ expect(formatOwnerTypes("news")).toBe(OwnerType.article)
+ expect(formatOwnerTypes("series")).toBe(OwnerType.article)
+ expect(formatOwnerTypes("video")).toBe(OwnerType.article)
+ })
+
+ it("handles auction page types", () => {
+ expect(formatOwnerTypes("auction")).toBe(OwnerType.sale)
+ })
+})
diff --git a/src/lib/getPageType.ts b/src/lib/getPageType.ts
new file mode 100644
index 00000000000..bab811d63c7
--- /dev/null
+++ b/src/lib/getPageType.ts
@@ -0,0 +1,66 @@
+import { Request } from "express"
+import { OwnerType, PageOwnerType } from "@artsy/cohesion"
+import { camelCase } from "lodash"
+
+export function getPageTypeFromReq(
+ req: Request
+): {
+ pageParts: string[]
+ pageType: PageOwnerType
+ pageSlug: string
+} {
+ const pageParts = req.path.split("/")
+ const pageSlug = pageParts[2]
+ const type = formatOwnerTypes(pageParts[1])
+ const pageType = OwnerType[type] || type
+
+ return {
+ pageParts,
+ pageSlug,
+ pageType,
+ }
+}
+
+export function getPageTypeFromClient(): {
+ pageParts: string[]
+ pageSlug: string
+ pageType: PageOwnerType
+} {
+ if (window) {
+ const PAGE_TYPE = window.sd && window.sd.PAGE_TYPE
+ const { pathname } = window.location
+ const pageParts = pathname.split("/")
+ const pageSlug = pageParts[2]
+ let type = formatOwnerTypes(PAGE_TYPE || pageParts[1])
+ const pageType = OwnerType[type] || type
+
+ return {
+ pageParts,
+ pageSlug,
+ pageType,
+ }
+ }
+}
+
+export const formatOwnerTypes = (type: string) => {
+ let formattedType = camelCase(type)
+
+ switch (type) {
+ case "auction":
+ formattedType = OwnerType.sale
+ break
+ case "news":
+ case "series":
+ case "video":
+ formattedType = OwnerType.article
+ break
+ }
+
+ if (!OwnerType[formattedType]) {
+ console.warn(
+ `OwnerType ${formattedType} is not part of @artsy/cohesion's schema.`
+ )
+ }
+
+ return formattedType
+}
diff --git a/src/lib/middleware/__tests__/pageCacheMiddleware.jest.ts b/src/lib/middleware/__tests__/pageCacheMiddleware.jest.ts
new file mode 100644
index 00000000000..c48c1a8e79f
--- /dev/null
+++ b/src/lib/middleware/__tests__/pageCacheMiddleware.jest.ts
@@ -0,0 +1,61 @@
+import { pageCacheMiddleware } from "../pageCacheMiddleware"
+const cache = require("lib/cache")
+
+jest.mock("lib/cache", () => ({
+ get: jest.fn().mockResolvedValue(true),
+ set: jest.fn(),
+}))
+
+jest.mock("config", () => ({
+ PAGE_CACHE_ENABLED: true,
+ PAGE_CACHE_TYPES: "artist",
+ PAGE_CACHE_NAMESPACE: "page-cache",
+ PAGE_CACHE_VERSION: "1",
+ PAGE_CACHE_EXPIRY_SECONDS: 600,
+ PAGE_CACHE_RETRIEVAL_TIMEOUT_MS: 400,
+}))
+
+describe("pageCacheMiddleware", () => {
+ let req
+ let res
+ let next
+ beforeEach(() => {
+ req = {
+ path: "/artist/test-artist",
+ url: "https://artsy.net/artist/test-artist",
+ }
+ res = {
+ once: jest.fn((_e, cb) => cb()),
+ locals: {
+ PAGE_CACHE: {
+ status: 200,
+ key: "key",
+ html: "html",
+ },
+ },
+ }
+ next = jest.fn()
+ cache.get.mockClear()
+ cache.set.mockClear()
+ })
+
+ it("sets up cache for valid pageTypes", async () => {
+ await pageCacheMiddleware(req, res, next)
+ expect(cache.set).toBeCalledWith("page-cache|1|key", "html", 600)
+ expect(cache.get.mock.calls[0][0]).toBe(
+ "page-cache|1|https://artsy.net/artist/test-artist"
+ )
+ expect(next).toBeCalled()
+ })
+
+ it("skips cache for invalid pageTypes", async () => {
+ req = {
+ path: "/artist-series/test-artist",
+ url: "https://artsy.net/artist-series/test-artist",
+ }
+ await pageCacheMiddleware(req, res, next)
+ expect(cache.set).not.toBeCalled()
+ expect(cache.get).not.toBeCalled()
+ expect(next).toBeCalled()
+ })
+})
diff --git a/src/lib/middleware/pageCacheMiddleware.ts b/src/lib/middleware/pageCacheMiddleware.ts
index 432f1744fac..ffeab9cc5ec 100644
--- a/src/lib/middleware/pageCacheMiddleware.ts
+++ b/src/lib/middleware/pageCacheMiddleware.ts
@@ -18,6 +18,7 @@ const runningTests = Object.keys(
require("desktop/components/split_test/running_tests.coffee")
).sort()
const cacheablePageTypes: string[] = PAGE_CACHE_TYPES.split("|")
+import { getPageTypeFromReq } from "lib/getPageType"
// Middleware will `next` and do nothing if any of the following is true:
//
@@ -34,13 +35,12 @@ export const pageCacheMiddleware = async (
if (!PAGE_CACHE_ENABLED) return next()
// Returns true if the page type corresponding to `url` is configured cacheable.
- const isCacheablePageType = (url: string) => {
- const pathWithQueryParams = url.split("/")[1]
- const pageType = pathWithQueryParams.split("?")[0]
+ const isCacheablePageType = (req: Request) => {
+ const { pageType } = getPageTypeFromReq(req)
return cacheablePageTypes.includes(pageType)
}
- if (!isCacheablePageType(req.url)) return next()
+ if (!isCacheablePageType(req)) return next()
// @ts-ignore
const hasUser = !!req.user
diff --git a/src/mobile/analytics/before_ready.js b/src/mobile/analytics/before_ready.ts
similarity index 60%
rename from src/mobile/analytics/before_ready.js
rename to src/mobile/analytics/before_ready.ts
index 97909b2099e..cdf458eec67 100644
--- a/src/mobile/analytics/before_ready.js
+++ b/src/mobile/analytics/before_ready.ts
@@ -1,26 +1,29 @@
+import { data as sd } from "sharify"
+import { reportLoadTimeToVolley } from "lib/volley"
+import { OwnerType } from "@artsy/cohesion"
+import { getPageTypeFromClient } from "lib/getPageType"
+
//
// Analytics code that needs to run before page load and analytics.ready
//
-import { data as sd } from "sharify"
-import { reportLoadTimeToVolley } from "lib/volley"
+const { pageType } = getPageTypeFromClient()
// Disable Parsely firing on non-article/section pages
-if (!location.pathname.match(/^\/article/)) {
+if (pageType !== OwnerType.article) {
window.PARSELY = { autotrack: false }
}
-const pageType = window.sd.PAGE_TYPE || window.location.pathname.split("/")[1]
-var properties = { path: location.pathname }
+const properties = { path: location.pathname }
-if (pageType == "artwork") {
+if (pageType === OwnerType.artwork) {
properties["acquireable"] = sd.ARTWORK.is_acquireable
properties["availability"] = sd.ARTWORK.availability
properties["price_listed"] = sd.ARTWORK.price && sd.ARTWORK.price.length > 0
}
// Track pageview
-analytics.page(properties, { integrations: { Marketo: false } })
+window.analytics.page(properties, { integrations: { Marketo: false } })
// Track pageload speed
if (
@@ -28,10 +31,10 @@ if (
window.performance.timing &&
sd.TRACK_PAGELOAD_PATHS
) {
- window.addEventListener("load", function() {
+ window.addEventListener("load", function () {
if (sd.TRACK_PAGELOAD_PATHS.split("|").includes(pageType)) {
- window.setTimeout(function() {
- var deviceType = sd.IS_MOBILE ? "mobile" : "desktop"
+ window.setTimeout(function () {
+ let deviceType = sd.IS_MOBILE ? "mobile" : "desktop"
reportLoadTimeToVolley(pageType, deviceType)
}, 0)
}
diff --git a/src/mobile/apps/fair/test/client/for_you.test.js b/src/mobile/apps/fair/test/client/for_you.test.js
index 58cd27d54a1..97eac44396d 100644
--- a/src/mobile/apps/fair/test/client/for_you.test.js
+++ b/src/mobile/apps/fair/test/client/for_you.test.js
@@ -1,8 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
const _ = require("underscore")
const Backbone = require("backbone")
const CurrentUser = require("../../../../models/current_user")
@@ -14,69 +9,63 @@ const sinon = require("sinon")
const benv = require("benv")
const { resolve } = require("path")
-describe("For You View", function () {
- beforeEach(function (done) {
- this.user = new CurrentUser(fabricate("user", { id: "current-user-id" }))
- return benv.setup(() => {
+describe("For You View", () => {
+ let view
+ let user
+ let fair
+ beforeEach(done => {
+ const profile = new Profile(fabricate("fair_profile"))
+ user = new CurrentUser(fabricate("user", { id: "current-user-id" }))
+ benv.setup(() => {
benv.expose({ $: benv.require("jquery") })
- Backbone.$ = $
$.onInfiniteScroll = function () {}
- sinon.stub(CurrentUser, "orNull").returns(this.user)
+ sinon.stub(CurrentUser, "orNull").returns(user)
sinon.stub(Backbone, "sync")
- return benv.render(
+ benv.render(
resolve(__dirname, "../../templates/for_you.jade"),
{
- fair: (this.fair = new Fair(fabricate("fair"))),
- profile: (this.profile = new Profile(fabricate("fair_profile"))),
+ fair: (fair = new Fair(fabricate("fair"))),
+ profile,
sd: {},
},
() => {
const { ForYouView } = require("../../client/for_you")
- this.view = new ForYouView({
+ Backbone.$ = $
+ view = new ForYouView({
el: $("#fair-for-you"),
- fair: this.fair,
- profile: this.profile,
+ fair: fair,
+ profile,
})
- return done()
+ done()
}
)
})
})
- afterEach(function () {
+ afterEach(() => {
benv.teardown()
Backbone.sync.restore()
- return CurrentUser.orNull.restore()
+ CurrentUser.orNull.restore()
})
- describe("init code", () =>
- it("creates a for you fair page view", function () {
- return this.view.$("#fair-page-title").text().should.containEql("Guide")
- }))
-
- xdescribe("logged out / not many follows", () =>
- xit("renders exhibitors to follow", function () {}))
+ describe("init code", () => {
+ it("creates a for you fair page view", () => {
+ view.$("#fair-page-title").text().should.containEql("Guide")
+ })
+ })
- return describe("logged in", function () {
- describe("#updateAndShowTitle", () =>
- it("renders the user's name in the heading", function () {
- return this.view
- .$("#fair-page-title")
- .text()
- .should.containEql(this.user.get("name"))
- }))
+ describe("logged in", () => {
+ describe("#updateAndShowTitle", () => {
+ it("renders the user's name in the heading", () => {
+ view.$("#fair-page-title").text().should.containEql(user.get("name"))
+ })
+ })
- describe("#fetchFollowingExhibitors", () =>
+ describe("#fetchFollowingExhibitors", () => {
it("renders fair exhibitor booths of partner profiles the current user follows", function () {
Backbone.sync.args[0][2].url.should.containEql("me/follow/profiles")
- return Backbone.sync.args[0][2].data.fair_id.should.equal(
- this.fair.get("id")
- )
- }))
- //Backbone.sync.args[0][2].success({results: [fabricate('partner_profile')]})
- //Backbone.sync.args[0][2].success([])
-
- return xdescribe("#fetchFollowingArtists", () =>
- it("renders links to booths of artists the current user follows", function () {}))
+ Backbone.sync.args[0][2].data.fair_id.should.equal(fair.get("id"))
+ })
+ })
})
})
diff --git a/src/mobile/apps/fair/test/client/main_page.test.js b/src/mobile/apps/fair/test/client/main_page.test.js
index 1feccb0e5f0..d4176e8a474 100644
--- a/src/mobile/apps/fair/test/client/main_page.test.js
+++ b/src/mobile/apps/fair/test/client/main_page.test.js
@@ -1,8 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
const _ = require("underscore")
const Backbone = require("backbone")
const Fair = require("../../../../models/fair")
@@ -12,15 +7,15 @@ const sinon = require("sinon")
const benv = require("benv")
const { resolve } = require("path")
-describe("Main page client-side code", function () {
- beforeEach(function (done) {
- return benv.setup(() => {
+describe("Main page client-side code", () => {
+ let view
+ beforeEach(done => {
+ benv.setup(() => {
benv.expose({ $: benv.require("jquery") })
- Backbone.$ = $
$.onInfiniteScroll = function () {}
sinon.stub(Backbone.history, "start")
sinon.stub(Backbone, "sync")
- return benv.render(
+ benv.render(
resolve(__dirname, "../../templates/main_page.jade"),
{
fair: new Fair(fabricate("fair")),
@@ -47,33 +42,34 @@ describe("Main page client-side code", function () {
"imageNavItemTemplate",
]
)
- this.view = new FairMainPageView({
+ Backbone.$ = $
+ view = new FairMainPageView({
el: $("body"),
model: new Fair(fabricate("fair")),
})
- return done()
+ done()
}
)
})
})
- afterEach(function () {
+ afterEach(() => {
benv.teardown()
Backbone.sync.restore()
- return Backbone.history.start.restore()
+ Backbone.history.start.restore()
})
- return describe("FairMainPageView", () =>
- describe("#renderFeaturedLinks", function () {
- it("fetches the fairs sets", function () {
- this.view.renderFeaturedLinks()
- return _.last(Backbone.sync.args)[2].url.should.containEql(
+ describe("FairMainPageView", () => {
+ describe("#renderFeaturedLinks", () => {
+ it("fetches the fairs sets", () => {
+ view.renderFeaturedLinks()
+ _.last(Backbone.sync.args)[2].url.should.containEql(
"/sets?owner_type=Fair"
)
})
- return it("renders the featured links", function () {
- this.view.renderFeaturedLinks()
+ it("renders the featured links", () => {
+ view.renderFeaturedLinks()
_.last(Backbone.sync.args)[2].success([
fabricate("set", { key: "editorial", display_on_martsy: true }),
])
@@ -83,9 +79,8 @@ describe("Main page client-side code", function () {
display_on_martsy: true,
}),
])
- return this.view.$el
- .html()
- .should.containEql("Featured link for this awesome page")
+ view.$el.html().should.containEql("Featured link for this awesome page")
})
- }))
+ })
+ })
})
diff --git a/src/mobile/assets/mobile_analytics.ts b/src/mobile/assets/mobile_analytics.ts
index 1411501da0c..5552275782a 100644
--- a/src/mobile/assets/mobile_analytics.ts
+++ b/src/mobile/assets/mobile_analytics.ts
@@ -8,7 +8,7 @@ const Events = require("../../v2/Utils/Events").default
// Send Reaction events to Segment
Events.onEvent(trackEvent)
-require("../analytics/before_ready.js")
+require("../analytics/before_ready")
$(() =>
window.analytics.ready(function () {
diff --git a/src/mobile/components/article/test/view.test.js b/src/mobile/components/article/test/view.test.js
index d0f6d40a7ae..99a054f5771 100644
--- a/src/mobile/components/article/test/view.test.js
+++ b/src/mobile/components/article/test/view.test.js
@@ -1,8 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
const _ = require("underscore")
const benv = require("benv")
const sinon = require("sinon")
@@ -13,16 +8,16 @@ const sd = require("sharify").data
const { resolve } = require("path")
const { fabricate } = require("@artsy/antigravity")
-describe("ArticleView", function () {
- beforeEach(function (done) {
- return benv.setup(() => {
+describe("ArticleView", () => {
+ let article
+ let ArticleView
+ beforeEach(done => {
+ benv.setup(() => {
benv.expose({
$: benv.require("jquery"),
Element: window.Element,
})
- // benv bug that doesn't attach globals to window
- Backbone.$ = $
- this.article = fabricate("article", {
+ article = fabricate("article", {
sections: [
{
type: "video",
@@ -37,10 +32,10 @@ describe("ArticleView", function () {
section_ids: [],
})
- return benv.render(
+ benv.render(
resolve(__dirname, "../templates/index.jade"),
{
- article: new Article(this.article),
+ article: new Article(article),
asset() {},
sd: {},
resize() {},
@@ -51,28 +46,32 @@ describe("ArticleView", function () {
relatedArticles: new Articles(),
},
() => {
- this.ArticleView = benv.requireWithJadeify(
+ Backbone.$ = $
+ ArticleView = benv.requireWithJadeify(
resolve(__dirname, "../client/view"),
[]
)
sinon.stub(Backbone, "sync")
- return done()
+ done()
}
)
})
})
- afterEach(function () {
+ afterEach(() => {
benv.teardown()
- return Backbone.sync.restore()
+ Backbone.sync.restore()
})
- return describe("#clickPlay", () =>
- it("replaces iFrame with an autoplay attribute", function () {
- this.view = new this.ArticleView({ el: $("body") })
- $(".article-video-play-button").click()
- return $(".article-section-video iframe")
+ describe("#clickPlay", () => {
+ let view
+ it("replaces iFrame with an autoplay attribute", () => {
+ view = new ArticleView({ el: $("body") })
+ view.$(".article-video-play-button").click()
+ view
+ .$(".article-section-video iframe")
.attr("src")
.should.containEql("autoplay=1")
- }))
+ })
+ })
})
diff --git a/src/typings/sharify.d.ts b/src/typings/sharify.d.ts
index 1816592b140..9cef57b57da 100644
--- a/src/typings/sharify.d.ts
+++ b/src/typings/sharify.d.ts
@@ -63,6 +63,7 @@ declare module "sharify" {
ARTIST_PAGE_CTA_ENABLED: string
ARTSY_EDITORIAL_CHANNEL: string
ARTSY_XAPP_TOKEN: string
+ ARTWORK?: any // mobile artist app data
AUCTION?: any
CSRF_TOKEN: string
CHANNEL?: { slug?: string } // Articles
diff --git a/src/v2/Apps/Artwork/Components/ArtworkSidebar/ArtworkSidebarBidAction.tsx b/src/v2/Apps/Artwork/Components/ArtworkSidebar/ArtworkSidebarBidAction.tsx
index 6f68e3783d6..2ebae9ab72e 100644
--- a/src/v2/Apps/Artwork/Components/ArtworkSidebar/ArtworkSidebarBidAction.tsx
+++ b/src/v2/Apps/Artwork/Components/ArtworkSidebar/ArtworkSidebarBidAction.tsx
@@ -33,7 +33,7 @@ const RegisterToBidButton: React.FC<{ onClick: () => void }> = ({
onClick,
}) => {
return (
-