Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

(exchanges) - implement the focusExchange (#903)

* implement the focusExchange

* Update refocusExchange.ts

* use Map.forEach and remove keys array

* add tests & docs

* Update docs/api/refocus-exchange.md

Co-authored-by: Phil Pluckthun <phil@kitten.sh>

* fix pkg.json

* Update exchanges/refocus/src/refocusExchange.ts

Co-authored-by: Phil Pluckthun <phil@kitten.sh>

* save bytes by using operation rather than a new object

* add docs to api-index page

Co-authored-by: Phil Pluckthun <phil@kitten.sh>

authored by

Jovi De Croock
Phil Pluckthun
and committed by
GitHub
0835fcab 1a439fd2

+275
+1
docs/api/README.md
··· 21 21 - [`@urql/exchange-persisted-fetch` API docs](./persisted-fetch-exchange.md) 22 22 - [`@urql/exchange-request-policy` API docs](./request-policy-exchange.md) 23 23 - [`@urql/exchange-auth` API docs](./auth-exchange.md) 24 + - [`@urql/exchange-refocus` API docs](./refocus-exchange.md)
+32
docs/api/refocus-exchange.md
··· 1 + --- 2 + title: '@urql/exchange-refocus' 3 + order: 10 4 + --- 5 + 6 + # Refocus exchange 7 + 8 + `@urql/exchange-refocus` is an exchange for the `urql` that tracks currently active operations and redispatches them when the 9 + window regains focus 10 + 11 + ## Quick Start Guide 12 + 13 + First install `@urql/exchange-refocus` alongside `urql`: 14 + 15 + ```sh 16 + yarn add @urql/exchange-refocus 17 + # or 18 + npm install --save @urql/exchange-refocus 19 + ``` 20 + 21 + Then add it to your `Client`, preferably after the `dedupExchange` but in front of any asynchronous 22 + exchanges, like the `fetchExchange`: 23 + 24 + ```js 25 + import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 26 + import { refocusExchange } from '@urql/exchange-refocus'; 27 + 28 + const client = createClient({ 29 + url: '/graphql', 30 + exchanges: [dedupExchange, refocusExchange(), cacheExchange, fetchExchange], 31 + }); 32 + ```
+2
docs/concepts/exchanges.md
··· 32 32 - [`authExchange`](../api/auth-exchange.md): Allows complex authentication flows to be implemented 33 33 easily. 34 34 - [`requestPolicyExchange`](../api/request-policy-exchange.md): Automatically upgrades `cache-only` and `cache-first` operations to `cache-and-network` after a given amount of time. 35 + - [`refocusExchange`](../api/refocus-exchange.md): Tracks open queries and refetches them 36 + when the window regains focus. 35 37 - `devtoolsExchange`: Provides the ability to use the [urql-devtools](https://github.com/FormidableLabs/urql-devtools) 36 38 37 39 It is also possible to apply custom exchanges to override the default logic.
+5
exchanges/refocus/CHANGELOG.md
··· 1 + # Changelog 2 + 3 + ## v0.1.0 4 + 5 + **Initial Release**
+27
exchanges/refocus/README.md
··· 1 + # @urql/exchange-refocus 2 + 3 + `@urql/exchange-refocus` is an exchange for the [`urql`](../../README.md) GraphQL client that tracks currently active operations and redispatches them when the 4 + window regains focus 5 + 6 + ## Quick Start Guide 7 + 8 + First install `@urql/exchange-refocus` alongside `urql`: 9 + 10 + ```sh 11 + yarn add @urql/exchange-refocus 12 + # or 13 + npm install --save @urql/exchange-refocus 14 + ``` 15 + 16 + Then add it to your `Client`, preferably after the `dedupExchange` but in front of any asynchronous 17 + exchanges, like the `fetchExchange`: 18 + 19 + ```js 20 + import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 21 + import { refocusExchange } from '@urql/exchange-refocus'; 22 + 23 + const client = createClient({ 24 + url: '/graphql', 25 + exchanges: [dedupExchange, refocusExchange(), cacheExchange, fetchExchange], 26 + }); 27 + ```
+69
exchanges/refocus/package.json
··· 1 + { 2 + "name": "@urql/exchange-refocus", 3 + "version": "0.1.0", 4 + "description": "An exchange that dispatches active operations when the window regains focus", 5 + "sideEffects": false, 6 + "homepage": "https://formidable.com/open-source/urql/docs/", 7 + "bugs": "https://github.com/FormidableLabs/urql/issues", 8 + "license": "MIT", 9 + "repository": { 10 + "type": "git", 11 + "url": "https://github.com/FormidableLabs/urql.git", 12 + "directory": "exchanges/refocus" 13 + }, 14 + "keywords": [ 15 + "urql", 16 + "graphql client", 17 + "formidablelabs", 18 + "exchanges", 19 + "react", 20 + "focus" 21 + ], 22 + "main": "dist/urql-exchange-refocus", 23 + "module": "dist/urql-exchange-refocus.mjs", 24 + "types": "dist/types/index.d.ts", 25 + "source": "src/index.ts", 26 + "exports": { 27 + ".": { 28 + "import": "./dist/urql-exchange-refocus.mjs", 29 + "require": "./dist/urql-exchange-refocus.js", 30 + "types": "./dist/types/index.d.ts", 31 + "source": "./src/index.ts" 32 + }, 33 + "./package.json": "./package.json" 34 + }, 35 + "files": [ 36 + "LICENSE", 37 + "CHANGELOG.md", 38 + "README.md", 39 + "dist/" 40 + ], 41 + "scripts": { 42 + "test": "jest", 43 + "clean": "rimraf dist", 44 + "check": "tsc --noEmit", 45 + "lint": "eslint --ext=js,jsx,ts,tsx .", 46 + "build": "rollup -c ../../scripts/rollup/config.js", 47 + "prepare": "node ../../scripts/prepare/index.js", 48 + "prepublishOnly": "run-s clean build" 49 + }, 50 + "jest": { 51 + "preset": "../../scripts/jest/preset" 52 + }, 53 + "devDependencies": { 54 + "@types/react": "^16.9.19", 55 + "graphql": "^15.1.0", 56 + "react": "^16.13.0", 57 + "react-dom": "^16.13.0" 58 + }, 59 + "peerDependencies": { 60 + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" 61 + }, 62 + "dependencies": { 63 + "@urql/core": ">=1.12.0", 64 + "wonka": "^4.0.14" 65 + }, 66 + "publishConfig": { 67 + "access": "public" 68 + } 69 + }
+1
exchanges/refocus/src/index.ts
··· 1 + export { refocusExchange } from './refocusExchange';
+92
exchanges/refocus/src/refocusExchange.test.ts
··· 1 + import gql from 'graphql-tag'; 2 + 3 + import { pipe, map, makeSubject, publish, tap } from 'wonka'; 4 + 5 + import { 6 + createClient, 7 + Operation, 8 + OperationResult, 9 + ExchangeIO, 10 + } from '@urql/core'; 11 + import { refocusExchange } from './refocusExchange'; 12 + 13 + const dispatchDebug = jest.fn(); 14 + 15 + const queryOne = gql` 16 + { 17 + author { 18 + id 19 + name 20 + } 21 + } 22 + `; 23 + 24 + const queryOneData = { 25 + __typename: 'Query', 26 + author: { 27 + __typename: 'Author', 28 + id: '123', 29 + name: 'Author', 30 + }, 31 + }; 32 + 33 + let client, op, ops$, next; 34 + beforeEach(() => { 35 + client = createClient({ url: 'http://0.0.0.0' }); 36 + op = client.createRequestOperation('query', { 37 + key: 1, 38 + query: queryOne, 39 + }); 40 + 41 + ({ source: ops$, next } = makeSubject<Operation>()); 42 + }); 43 + 44 + it(`attaches a listener and redispatches queries on call`, () => { 45 + const response = jest.fn( 46 + (forwardOp: Operation): OperationResult => { 47 + return { 48 + operation: forwardOp, 49 + data: queryOneData, 50 + }; 51 + } 52 + ); 53 + 54 + let listener; 55 + const spy = jest 56 + .spyOn(window, 'addEventListener') 57 + .mockImplementation((_keyword, fn) => { 58 + listener = fn; 59 + }); 60 + const reexecuteSpy = jest 61 + .spyOn(client, 'reexecuteOperation') 62 + .mockImplementation(() => ({})); 63 + 64 + const result = jest.fn(); 65 + const forward: ExchangeIO = ops$ => { 66 + return pipe(ops$, map(response)); 67 + }; 68 + 69 + pipe( 70 + refocusExchange()({ 71 + forward, 72 + client, 73 + dispatchDebug, 74 + })(ops$), 75 + tap(result), 76 + publish 77 + ); 78 + 79 + expect(spy).toBeCalledTimes(1); 80 + expect(spy).toBeCalledWith('focus', expect.anything()); 81 + 82 + next(op); 83 + 84 + listener(); 85 + expect(reexecuteSpy).toBeCalledTimes(1); 86 + expect(reexecuteSpy).toBeCalledWith({ 87 + context: expect.anything(), 88 + key: 1, 89 + query: queryOne, 90 + operationName: 'query', 91 + }); 92 + });
+33
exchanges/refocus/src/refocusExchange.ts
··· 1 + import { pipe, tap } from 'wonka'; 2 + import { Exchange, Operation } from '@urql/core'; 3 + 4 + export const refocusExchange = (): Exchange => { 5 + return ({ client, forward }) => ops$ => { 6 + const watchedOperations = new Map<number, Operation>(); 7 + const observedOperations = new Map<number, number>(); 8 + 9 + window.addEventListener('focus', () => { 10 + watchedOperations.forEach(op => { 11 + client.reexecuteOperation( 12 + client.createRequestOperation('query', op, { 13 + requestPolicy: 'cache-and-network', 14 + }) 15 + ); 16 + }); 17 + }); 18 + 19 + const processIncomingOperation = (op: Operation) => { 20 + if (op.operationName === 'query' && !observedOperations.has(op.key)) { 21 + observedOperations.set(op.key, 1); 22 + watchedOperations.set(op.key, op); 23 + } 24 + 25 + if (op.operationName === 'teardown' && observedOperations.has(op.key)) { 26 + observedOperations.delete(op.key); 27 + watchedOperations.delete(op.key); 28 + } 29 + }; 30 + 31 + return forward(pipe(ops$, tap(processIncomingOperation))); 32 + }; 33 + };
+13
exchanges/refocus/tsconfig.json
··· 1 + { 2 + "extends": "../../tsconfig.json", 3 + "include": ["src"], 4 + "compilerOptions": { 5 + "baseUrl": "./", 6 + "paths": { 7 + "urql": ["../../node_modules/urql/src"], 8 + "*-urql": ["../../node_modules/*-urql/src"], 9 + "@urql/core/*": ["../../node_modules/@urql/core/src/*"], 10 + "@urql/*": ["../../node_modules/@urql/*/src"] 11 + } 12 + } 13 + }