From 938b4bf3f1b1f0ca7334989731fc83d1a4f31e80 Mon Sep 17 00:00:00 2001 From: Morley Zhi Date: Fri, 25 Oct 2019 13:04:16 -0400 Subject: [PATCH] DataProvider.watchTransfers (#115) See also #34. --- package.json | 2 +- playground/package.json | 1 + playground/src/components/Transfers.js | 71 ++++++++++++--------- playground/yarn.lock | 5 ++ src/data/DataProvider.ts | 88 ++++++++++++++++++++++---- 5 files changed, 125 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 1fef3a44..70e95077 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stellar/wallet-sdk", - "version": "0.0.6-rc.12", + "version": "0.0.6-rc.13", "description": "Libraries to help you write Stellar-enabled wallets in Javascript", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/playground/package.json b/playground/package.json index a9183bc1..43ff47a0 100644 --- a/playground/package.json +++ b/playground/package.json @@ -5,6 +5,7 @@ "dependencies": { "@stellar/elements": "^0.0.0", "lodash": "^4.17.14", + "moment": "^2.24.0", "react": "^16.8.5", "react-dom": "^16.8.5", "react-json-view": "^1.19.1", diff --git a/playground/src/components/Transfers.js b/playground/src/components/Transfers.js index cdbd0c34..6ac3c7c0 100644 --- a/playground/src/components/Transfers.js +++ b/playground/src/components/Transfers.js @@ -1,4 +1,6 @@ import React, { Component } from "react"; +import moment from "moment"; +import Json from "react-json-view"; class Transfers extends Component { state = { @@ -42,25 +44,25 @@ class Transfers extends Component { streamEnder: null, }); - // const streamEnder = dataProvider.watchTransfers({ - // onMessage: (transfer) => { - // this.setState({ - // transfers: [ - // { transfer, updateTime: new Date() }, - // ...this.state.transfers, - // ], - // }); - // }, - // onError: (err) => { - // console.log("error: ", err); - // this.setState({ err }); - // streamEnder(); - // }, - // }); + const streamEnder = dataProvider.watchTransfers({ + onMessage: (transfer) => { + this.setState({ + transfers: [ + { transfer, updateTime: new Date() }, + ...this.state.transfers, + ], + }); + }, + onError: (err) => { + console.log("error: ", err); + this.setState({ err }); + streamEnder(); + }, + }); - // this.setState({ - // streamEnder, - // }); + this.setState({ + streamEnder, + }); }; render() { @@ -68,19 +70,28 @@ class Transfers extends Component { return (

Transfers

- -

- not implemented yet -

- {err &&

Error: {err.toString()}

} diff --git a/playground/yarn.lock b/playground/yarn.lock index b599b440..c49e45ac 100644 --- a/playground/yarn.lock +++ b/playground/yarn.lock @@ -6598,6 +6598,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" +moment@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" diff --git a/src/data/DataProvider.ts b/src/data/DataProvider.ts index 704422e0..25c08537 100644 --- a/src/data/DataProvider.ts +++ b/src/data/DataProvider.ts @@ -30,16 +30,22 @@ function isAccount(obj: any): obj is Account { interface CallbacksObject { accountDetails?: () => void; + transfers?: () => void; } interface ErrorHandlersObject { accountDetails?: (error: any) => void; + transfers?: (error: any) => void; +} + +interface WatcherTimeoutsObject { + [name: string]: ReturnType; } export class DataProvider { private accountKey: string; private serverUrl: string; - private unfundedWatcherTimeout: any; + private _watcherTimeouts: WatcherTimeoutsObject; private effectStreamEnder?: () => void; private callbacks: CallbacksObject; @@ -69,7 +75,7 @@ export class DataProvider { this.errorHandlers = {}; this.serverUrl = params.serverUrl; this.accountKey = accountKey; - this.unfundedWatcherTimeout = null; + this._watcherTimeouts = {}; } /** @@ -209,7 +215,7 @@ export class DataProvider { // otherwise, if it's a 404, try again in a bit. .catch((err) => { if (err.isUnfunded) { - this.unfundedWatcherTimeout = setTimeout(() => { + this._watcherTimeouts.watchAccountDetails = setTimeout(() => { this.watchAccountDetails(params); }, 2000); } else { @@ -219,8 +225,68 @@ export class DataProvider { // if they exec this function, don't make the balance callback do anything return () => { - if (this.unfundedWatcherTimeout) { - clearTimeout(this.unfundedWatcherTimeout); + if (this._watcherTimeouts.watchAccountDetails) { + clearTimeout(this._watcherTimeouts.watchAccountDetails); + } + + delete this.callbacks.accountDetails; + delete this.errorHandlers.accountDetails; + }; + } + + /** + * Fetch transfers, then re-fetch whenever the details update. + * Returns a function you can execute to stop the watcher. + */ + public watchTransfers(params: WatcherParams): () => void { + const { onMessage, onError } = params; + + let getNextTransfers: () => Promise>; + + this.fetchTransfers() + + // if the account is funded, watch for effects. + .then((res) => { + // reset the transfer cache + getNextTransfers = res.prev; + + // onMessage each transfer separately + res.records.forEach(onMessage); + + this.callbacks.transfers = debounce(() => { + getNextTransfers() + .then((nextRes) => { + getNextTransfers = nextRes.prev; + + // get new things + if (nextRes.records.length) { + nextRes.records.forEach(onMessage); + } + }) + .catch(onError); + }, 2000); + this.errorHandlers.transfers = onError; + + this._startEffectWatcher().catch((err) => { + onError(err); + }); + }) + + // otherwise, if it's a 404, try again in a bit. + .catch((err) => { + if (err.isUnfunded) { + this._watcherTimeouts.watchTransfers = setTimeout(() => { + this.watchTransfers(params); + }, 2000); + } else { + onError(err); + } + }); + + // if they exec this function, don't make the balance callback do anything + return () => { + if (this._watcherTimeouts.watchTransfers) { + clearTimeout(this._watcherTimeouts.watchTransfers); } delete this.callbacks.accountDetails; @@ -244,8 +310,8 @@ export class DataProvider { const tradeResponses = await Promise.all(tradeRequests); return { - next: () => offers.next().then(this._processOpenOffers), - prev: () => offers.prev().then(this._processOpenOffers), + next: () => offers.next().then((res) => this._processOpenOffers(res)), + prev: () => offers.prev().then((res) => this._processOpenOffers(res)), records: makeDisplayableOffers( { publicKey: this.accountKey }, { @@ -263,8 +329,8 @@ export class DataProvider { trades: ServerApi.CollectionPage, ): Promise> { return { - next: () => trades.next().then(this._processTrades), - prev: () => trades.prev().then(this._processTrades), + next: () => trades.next().then((res) => this._processTrades(res)), + prev: () => trades.prev().then((res) => this._processTrades(res)), records: makeDisplayableTrades( { publicKey: this.accountKey }, trades.records, @@ -280,8 +346,8 @@ export class DataProvider { >, ): Promise> { return { - next: () => transfers.next().then(this._processTransfers), - prev: () => transfers.prev().then(this._processTransfers), + next: () => transfers.next().then((res) => this._processTransfers(res)), + prev: () => transfers.prev().then((res) => this._processTransfers(res)), records: makeDisplayableTransfers( { publicKey: this.accountKey }, transfers.records,