From 5b66a35b1feb84b104289f0326d737caa0003ab4 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Wed, 24 Jul 2019 15:04:34 -0700 Subject: [PATCH] Fixes to purchase flow. (#37) * revise beta deploy and subscrription hooks * add in new beta icon * update eager update to include subscriber status. * add in a couple more fixes * update test and snapshots --- package.json | 11 ++-- public/favicon-beta.png | Bin 2727 -> 5749 bytes public/manifest.json | 2 +- scripts/beta-decorate.js | 3 +- src/__snapshots__/app.test.js.snap | 7 +++ src/app.test.js | 6 ++ .../__snapshots__/index.test.js.snap | 23 ++++++++ src/components/info-modal/modal-data.js | 4 +- src/constants.js | 2 +- src/hooks/use-subscription.js | 38 +++++++++---- src/hooks/use-subscription.test.js | 52 ++++++------------ src/index.js | 9 ++- src/lib/aggregation.js | 11 +++- 13 files changed, 108 insertions(+), 60 deletions(-) create mode 100644 src/__snapshots__/app.test.js.snap diff --git a/package.json b/package.json index bbac6e95..604c3f80 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "Mujo", - "version": "1.2.0", + "version": "1.2.4", "private": true, + "homepage": "./", "dependencies": { "@babel/core": "7.4.3", "@emotion/cache": "^10.0.14", @@ -74,15 +75,17 @@ "scripts": { "serve": "serve", "start": "node scripts/watch.js", - "build": "PUBLIC_URL=./ node scripts/build.js && npm run append:sw && npm run append:sm", + "build:beta": "BETA='true' node scripts/build.js && npm run build:post && npm run append:beta", + "build:post": "npm run append:sw && npm run append:sm", + "build": "PUBLIC_URL=./ node scripts/build.js && npm run build:post", "append:sw": "cra-append-sw --mode replace --skip-compile ./public/service-worker.js", "append:sm": "node scripts/modify-source-maps.js", "test": "npm run build && node scripts/test.js --env=jsdom", "fmt": "prettier '{src,public}/**/*.js' --write", "lint": "eslint '{src,public}/**/*.js'", "dist": "npm run build && ./scripts/dist.sh", - "dist:beta": "npm run build && npm run beta && ./scripts/dist.sh", - "beta": "node scripts/beta-decorate.js" + "dist:beta": "npm run build:beta && ./scripts/dist.sh", + "append:beta": "node scripts/beta-decorate.js" }, "browserslist": [ "last 3 Chrome versions" diff --git a/public/favicon-beta.png b/public/favicon-beta.png index 32df06dde1e1fb37004523a0ef0ab91f5af7cbd2..ba23b6e417c7265c80501e80a698ef4bcef81ba2 100644 GIT binary patch literal 5749 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 literal 2727 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 diff --git a/public/manifest.json b/public/manifest.json index 766900ce..3d2a130b 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { "manifest_version": 2, - "version": "1.2.0", + "version": "1.2.1", "short_name": "Mujo", "name": "Mujō - Be mindful of your time", "description": "Mujō is a extension that reminds you not to over work yourself.", diff --git a/scripts/beta-decorate.js b/scripts/beta-decorate.js index 27443bde..13b9f35e 100644 --- a/scripts/beta-decorate.js +++ b/scripts/beta-decorate.js @@ -13,6 +13,7 @@ const readJSON = async dir => JSON.parse(await read(dir)) const serialize = json => JSON.stringify(json, null, '\t') const decorateBETA = async () => { + const secondsInWeek = `${Math.floor(+new Date() / 1000)}`.slice(-4) const manifest = await readJSON(MANIFEST_PATH) const betaManifest = Object.assign({}, manifest, { name: beta(manifest.name), @@ -26,7 +27,7 @@ const decorateBETA = async () => { 128: BETA_ICON, 512: BETA_ICON, }, - version: `${manifest.version}.${`${+new Date()}`.slice(0, 4)}`, + version: `${manifest.version}.${secondsInWeek}`, version_name: `${manifest.version} BETA`, }) return write(MANIFEST_PATH, serialize(betaManifest)) diff --git a/src/__snapshots__/app.test.js.snap b/src/__snapshots__/app.test.js.snap new file mode 100644 index 00000000..f62f3d95 --- /dev/null +++ b/src/__snapshots__/app.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should match the snapshot 1`] = ` +
+`; diff --git a/src/app.test.js b/src/app.test.js index 524fca66..339f9577 100644 --- a/src/app.test.js +++ b/src/app.test.js @@ -1,8 +1,14 @@ import React from 'react' import ReactDOM from 'react-dom' +import ReactTestRenderer from 'react-test-renderer' import App from './app' it('renders without crashing', () => { const div = document.createElement('div') ReactDOM.render(, div) }) + +it('should match the snapshot', async () => { + const tree = ReactTestRenderer.create().toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/src/components/info-modal/__snapshots__/index.test.js.snap b/src/components/info-modal/__snapshots__/index.test.js.snap index db83655b..35c73870 100644 --- a/src/components/info-modal/__snapshots__/index.test.js.snap +++ b/src/components/info-modal/__snapshots__/index.test.js.snap @@ -53,6 +53,29 @@ exports[`InfoModal should match snapshot 1`] = ` > You have set the max number of break timers.

+

+ To get access to unlimited break timers and add a break timer for foo subscribe to Mujō. +

+
+
+
`; diff --git a/src/components/info-modal/modal-data.js b/src/components/info-modal/modal-data.js index 16050470..d02ec793 100644 --- a/src/components/info-modal/modal-data.js +++ b/src/components/info-modal/modal-data.js @@ -14,7 +14,7 @@ const modals = { [SUB_SUCCESS_MODAL]: subscriptionSuccess, } -export const getModalData = (context, subDetails) => { +export const getModalData = (context, subDetails, options) => { const modalData = modals[context.name] || identity - return modalData(context, subDetails) + return modalData(context, subDetails, options) } diff --git a/src/constants.js b/src/constants.js index 6f41cd37..f6b84e3d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -46,7 +46,7 @@ export const SET_STORAGE = 'SET_STORAGE' // Feature Flags export const SCREEN_TIME_FEATURE = true export const BREAK_TIMER_FEATURE = true -export const SUBSCRIBE_FEATURE = false +export const SUBSCRIBE_FEATURE = true // Upsell modals export const MAX_BREAKTIMER_MODAL = 'breakTimerMax' diff --git a/src/hooks/use-subscription.js b/src/hooks/use-subscription.js index c260df55..e206be28 100644 --- a/src/hooks/use-subscription.js +++ b/src/hooks/use-subscription.js @@ -1,4 +1,9 @@ -import { useState, useEffect, useCallback } from 'react' +import React, { + useState, + useEffect, + useCallback, + useContext, +} from 'react' import { ACTIVE_PRODUCT } from '../constants' import { compose, first } from '../lib/functional' import { @@ -25,7 +30,8 @@ export const hydrate = async ({ setProducts, setUser }) => { setProducts(await getProducts()) } -export const useSubscription = () => { +const context = React.createContext({ user: userFactory([]) }) +export const SubscriptionProvider = props => { const [isInitialized, setIsInitialized] = useState(false) const [products, setProducts] = useState([]) const [user, setUser] = useState(userFactory()) @@ -42,16 +48,18 @@ export const useSubscription = () => { try { await buyProduct(sku) } catch (e) { - return setPurchaseError(e) + setPurchaseError(e) + return } // eager update user setUser( - Object.assign({}, user, { products: [getProduct(sku)] }) + Object.assign({}, user, { + products: [getProduct(sku)], + isSubscribed: true, + }) ) - // rehydrate user - return hydrate({ setProducts, setUser }) }, - [user, getProduct] + [getProduct, user] ) useEffect(() => { @@ -61,11 +69,19 @@ export const useSubscription = () => { } }, [setProducts, setUser, isInitialized, setIsInitialized]) - return { - user, - products, + const { Provider } = context + + const value = { buy, - purchaseError, getProduct, + isInitialized, + products, + purchaseError, + user, } + // eslint-disable-next-line no-underscore-dangle + window.__MUJO_SUBSCRIPTION__ = value + return } + +export const useSubscription = () => useContext(context) diff --git a/src/hooks/use-subscription.test.js b/src/hooks/use-subscription.test.js index 14d8be14..5798ca2a 100644 --- a/src/hooks/use-subscription.test.js +++ b/src/hooks/use-subscription.test.js @@ -1,11 +1,17 @@ import { renderHook, act } from '@testing-library/react-hooks' import { ACTIVE_PRODUCT, CANCELLED_PRODUCT } from '../constants' -import { useSubscription, activeProducts } from './use-subscription' +import { + useSubscription, + activeProducts, + SubscriptionProvider, +} from './use-subscription' jest.mock('../lib/payment') /* eslint-disable-next-line import-order-alphabetical/order */ const { getPurchases, getProducts, buy } = require('../lib/payment') +const hookOptions = { wrapper: SubscriptionProvider } + beforeEach(() => { getPurchases.mockReset() getProducts.mockReset() @@ -23,8 +29,9 @@ test('activeProduct should filter to only active products', () => { test('useSubscription should initialize products and purchases', async () => { getPurchases.mockResolvedValue([{ state: ACTIVE_PRODUCT }]) getProducts.mockResolvedValue(['foo', 'bar']) - const { result, waitForNextUpdate } = renderHook(() => - useSubscription() + const { result, waitForNextUpdate } = renderHook( + () => useSubscription(), + hookOptions ) // Two updates [product, user] await waitForNextUpdate() @@ -39,8 +46,9 @@ test('useSubscription should initialize products and purchases', async () => { test('useSubscription should be unsubbed if not active product', async () => { getPurchases.mockResolvedValue([{ state: CANCELLED_PRODUCT }]) getProducts.mockResolvedValue(['foo', 'bar']) - const { result, waitForNextUpdate } = renderHook(() => - useSubscription() + const { result, waitForNextUpdate } = renderHook( + () => useSubscription(), + hookOptions ) // Two updates [product, user] await waitForNextUpdate() @@ -48,41 +56,13 @@ test('useSubscription should be unsubbed if not active product', async () => { expect(result.current.user.isSubscribed).toBe(false) }) -test('useSubscription should get purchases after calling buy', async () => { - getPurchases.mockResolvedValue([]) - getProducts.mockResolvedValue(['foo', 'bar']) - buy.mockResolvedValue(true) - const { result, waitForNextUpdate } = renderHook(() => - useSubscription() - ) - // Init calls - await waitForNextUpdate() - await waitForNextUpdate() - expect(result.current.user.isSubscribed).toBe(false) - expect(result.current.user.products).toEqual([]) - // mock purchase again with purchased value - getPurchases - .mockReset() - .mockResolvedValue([{ state: ACTIVE_PRODUCT }]) - act(() => { - result.current.buy('foo') - }) - // Update calls - await waitForNextUpdate() - await waitForNextUpdate() - expect(result.current.user.isSubscribed).toBe(true) - expect(result.current.user.products).toEqual([ - { state: ACTIVE_PRODUCT }, - ]) - expect(result.current.products).toEqual(['foo', 'bar']) -}) - test('useSubscription a failure to buy should set purchaseError', async () => { getPurchases.mockResolvedValue([]) getProducts.mockResolvedValue([]) buy.mockRejectedValue(new Error('fail')) - const { result, waitForNextUpdate } = renderHook(() => - useSubscription() + const { result, waitForNextUpdate } = renderHook( + () => useSubscription(), + hookOptions ) // Init calls await waitForNextUpdate() diff --git a/src/index.js b/src/index.js index e0a8621a..e25491c5 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import { useColorScheme } from 'use-color-scheme' import App from './app' import { ErrorBox } from './components/error-box' import { Font } from './components/fonts' +import { SubscriptionProvider } from './hooks/use-subscription' import { ColorThemeProvider } from './hooks/use-theme' import './lib/tracker' @@ -13,9 +14,11 @@ const NewHomePage = () => { return ( - - - + + + + + ) } diff --git a/src/lib/aggregation.js b/src/lib/aggregation.js index 026034c6..b315102b 100644 --- a/src/lib/aggregation.js +++ b/src/lib/aggregation.js @@ -1,3 +1,4 @@ +import { FOURTY_FIVE_MINUTES } from '../constants' import { shortURL } from './url' import { set } from './util' @@ -30,7 +31,15 @@ export const toSiteInfo = (times, timers) => Object.keys(times).reduce((accum, url) => { set(accum, url, accum[url] || {}) set(accum[url], 'time', times[url]) - set(accum[url], 'breakTimer', timers[url] || {}) + set( + accum[url], + 'breakTimer', + timers[url] || { + enabled: false, + time: FOURTY_FIVE_MINUTES, + url, + } + ) return accum }, {})