Add notifications listener

This commit is contained in:
Fritz Lumnitz 2022-01-09 17:02:43 +01:00 committed by woodser
parent 640c89aa87
commit 7cdcdffb72
2 changed files with 89 additions and 4 deletions

View File

@ -4,7 +4,7 @@
import {HavenoDaemon} from "./HavenoDaemon";
import {HavenoUtils} from "./HavenoUtils";
import * as grpcWeb from 'grpc-web';
import {XmrBalanceInfo, OfferInfo, TradeInfo, MarketPriceInfo} from './protobuf/grpc_pb'; // TODO (woodser): better names; haveno_grpc_pb, haveno_pb
import {MarketPriceInfo, NotificationMessage, OfferInfo, TradeInfo, XmrBalanceInfo} from './protobuf/grpc_pb'; // TODO (woodser): better names; haveno_grpc_pb, haveno_pb
import {PaymentAccount} from './protobuf/pb_pb';
import {XmrDestination, XmrTx, XmrIncomingTransfer, XmrOutgoingTransfer} from './protobuf/grpc_pb';
@ -188,6 +188,32 @@ test("Can register as dispute agents", async () => {
}
});
test("Can receive push notifications", async () => {
// add notification listener
let notifications: NotificationMessage[] = [];
await alice.addNotificationListener(notification => {
notifications.push(notification);
});
// send test notification
for (let i = 0; i < 3; i++) {
await alice._sendNotification(new NotificationMessage()
.setTimestamp(Date.now())
.setTitle("Test title")
.setMessage("Test message"));
}
// test notification
await wait(1000);
assert.equal(3, notifications.length);
for (let i = 0; i < 3; i++) {
assert(notifications[i].getTimestamp() > 0);
assert.equal("Test title", notifications[i].getTitle());
assert.equal("Test message", notifications[i].getMessage());
}
});
test("Can get market prices", async () => {
// get all market prices
@ -596,6 +622,12 @@ test("Can complete a trade", async () => {
let aliceBalancesBefore = await alice.getBalances();
let bobBalancesBefore: XmrBalanceInfo = await bob.getBalances();
// register to receive notifications
let aliceNotifications: NotificationMessage[] = [];
let bobNotifications: NotificationMessage[] = [];
await alice.addNotificationListener(notification => { aliceNotifications.push(notification); });
await bob.addNotificationListener(notification => { bobNotifications.push(notification); });
// alice posts offer to buy xmr
console.log("Alice posting offer");
let direction = "buy";
@ -637,6 +669,11 @@ test("Can complete a trade", async () => {
expect(trade.getPhase()).toEqual("DEPOSIT_PUBLISHED");
console.log("Bob done taking offer in " + (Date.now() - startTime) + " ms");
// alice is notified offer is taken
assert.equal(1, aliceNotifications.length);
assert.equal("Offer Taken", aliceNotifications[0].getTitle());
assert.equal("Your offer " + offer.getId() + " has been accepted", aliceNotifications[0].getMessage());
// bob can get trade
let fetchedTrade: TradeInfo = await bob.getTrade(trade.getTradeId());
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED");

View File

@ -1,8 +1,8 @@
import {HavenoUtils} from "./HavenoUtils";
import * as grpcWeb from 'grpc-web';
import {GetVersionClient, DisputeAgentsClient, PriceClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient} from './protobuf/GrpcServiceClientPb';
import {GetVersionRequest, GetVersionReply, RegisterDisputeAgentRequest, MarketPriceRequest, MarketPriceReply, MarketPricesRequest, MarketPricesReply, MarketPriceInfo, GetBalancesRequest, GetBalancesReply, XmrBalanceInfo, GetOffersRequest, GetOffersReply, OfferInfo, GetPaymentAccountsRequest, GetPaymentAccountsReply, CreateCryptoCurrencyPaymentAccountRequest, CreateCryptoCurrencyPaymentAccountReply, CreateOfferRequest, CreateOfferReply, CancelOfferRequest, TakeOfferRequest, TakeOfferReply, TradeInfo, GetTradeRequest, GetTradeReply, GetTradesRequest, GetTradesReply, GetNewDepositSubaddressRequest, GetNewDepositSubaddressReply, ConfirmPaymentStartedRequest, ConfirmPaymentReceivedRequest, XmrTx, GetXmrTxsRequest, GetXmrTxsReply, XmrDestination, CreateXmrTxRequest, CreateXmrTxReply, RelayXmrTxRequest, RelayXmrTxReply} from './protobuf/grpc_pb';
import {PaymentAccount, AvailabilityResult} from './protobuf/pb_pb';
import {DisputeAgentsClient, GetVersionClient, NotificationsClient, PriceClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient} from './protobuf/GrpcServiceClientPb';
import {CancelOfferRequest, ConfirmPaymentReceivedRequest, ConfirmPaymentStartedRequest, CreateCryptoCurrencyPaymentAccountReply, CreateCryptoCurrencyPaymentAccountRequest, CreateOfferReply, CreateOfferRequest, CreateXmrTxReply, CreateXmrTxRequest, GetBalancesReply, GetBalancesRequest, GetNewDepositSubaddressReply, GetNewDepositSubaddressRequest, GetOffersReply, GetOffersRequest, GetPaymentAccountsReply, GetPaymentAccountsRequest, GetTradeReply, GetTradeRequest, GetTradesReply, GetTradesRequest, GetVersionReply, GetVersionRequest, GetXmrTxsReply, GetXmrTxsRequest, MarketPriceInfo, MarketPriceReply, MarketPriceRequest, MarketPricesReply, MarketPricesRequest, NotificationMessage, OfferInfo, RegisterDisputeAgentRequest, RegisterNotificationListenerRequest, RelayXmrTxReply, RelayXmrTxRequest, SendNotificationRequest, TakeOfferReply, TakeOfferRequest, TradeInfo, XmrBalanceInfo, XmrDestination, XmrTx} from './protobuf/grpc_pb';
import {AvailabilityResult, PaymentAccount} from './protobuf/pb_pb';
const console = require('console');
/**
@ -15,9 +15,11 @@ class HavenoDaemon {
_password: string;
_process: any;
_processLogging: boolean = false;
_notificationListeners: ((notification: NotificationMessage) => void)[] = [];
_walletRpcPort: number|undefined;
_getVersionClient: GetVersionClient;
_disputeAgentsClient: DisputeAgentsClient;
_notificationsClient: NotificationsClient;
_priceClient: PriceClient;
_walletsClient: WalletsClient;
_paymentAccountsClient: PaymentAccountsClient;
@ -43,6 +45,7 @@ class HavenoDaemon {
this._paymentAccountsClient = new PaymentAccountsClient(this._url);
this._offersClient = new OffersClient(this._url);
this._tradesClient = new TradesClient(this._url);
this._notificationsClient = new NotificationsClient(this._url);
}
/**
@ -220,6 +223,16 @@ class HavenoDaemon {
});
}
/**
* Add a listener to receive notifications from the Haveno daemon.
*
* @param {HavenoDaemonListener} listener - the notification listener to add
*/
async addNotificationListener(listener: (notification: NotificationMessage) => void): Promise<void> {
this._notificationListeners.push(listener);
if (this._notificationListeners.length === 1) return this._registerNotificationListener();
}
/**
* Get the current market price per 1 XMR in the given currency.
*
@ -568,6 +581,41 @@ class HavenoDaemon {
});
});
}
// ------------------------------- HELPERS ----------------------------------
/**
* Register a listener to receive notifications.
* Due to the nature of grpc streaming, this method returns a promise
* which may be resolved before the listener is actually registered.
*/
async _registerNotificationListener(): Promise<void> {
let that = this;
return new Promise(function(resolve) {
that._notificationsClient.registerNotificationListener(new RegisterNotificationListenerRequest(), {password: that._password})
.on("data", (data) => {
if (data instanceof NotificationMessage) {
for (let listener of that._notificationListeners) listener(data);
}
});
setTimeout(function() { resolve(); }, 1000); // TODO: call returns before listener registered
});
}
/**
* Send a notification.
*
* @param {NotificationMessage} notification - notification to send
*/
async _sendNotification(notification: NotificationMessage): Promise<void> {
let that = this;
return new Promise(function(resolve, reject) {
that._notificationsClient.sendNotification(new SendNotificationRequest().setNotification(notification), {password: that._password}, function(err: grpcWeb.RpcError) {
if (err) reject(err);
else resolve();
});
});
}
}
export {HavenoDaemon};