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 ( - ) @@ -279,6 +279,7 @@ export class ArtworkSidebarBidAction extends React.Component<