show notification dot when balance changes (#5214)

This commit is contained in:
Florian Dold 2017-12-12 16:39:55 +01:00
parent 1bcc5022c2
commit b5a4bcb247
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
6 changed files with 91 additions and 1 deletions

View File

@ -247,6 +247,16 @@ export interface Badge {
* Stop indicating background activity. * Stop indicating background activity.
*/ */
stopBusy(): void; stopBusy(): void;
/**
* Show the notification in the badge.
*/
showNotification(): void;
/**
* Stop showing the notification.
*/
clearNotification(): void;
} }
@ -982,7 +992,7 @@ export class Wallet {
.put(Stores.purchases, t) .put(Stores.purchases, t)
.putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin)) .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
.finish(); .finish();
this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
} }
@ -1198,7 +1208,11 @@ export class Wallet {
return c; return c;
} }
await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin) await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin)
// Show notifications only for accepted tips
this.badge.showNotification();
} }
} else {
this.badge.showNotification();
} }
this.notifier.notify(); this.notifier.notify();
@ -1720,6 +1734,7 @@ export class Wallet {
console.log("suspending coin", c); console.log("suspending coin", c);
c.suspended = true; c.suspended = true;
q.put(Stores.coins, c); q.put(Stores.coins, c);
this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
}); });
await q.finish(); await q.finish();
@ -2673,6 +2688,7 @@ export class Wallet {
.put(Stores.coinsReturns, coinsReturnRecord) .put(Stores.coinsReturns, coinsReturnRecord)
.putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin)) .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
.finish(); .finish();
this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
this.depositReturnedCoins(coinsReturnRecord); this.depositReturnedCoins(coinsReturnRecord);
@ -2835,6 +2851,7 @@ export class Wallet {
this.refresh(perm.coin_pub); this.refresh(perm.coin_pub);
} }
this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
} }
@ -2968,6 +2985,7 @@ export class Wallet {
} }
await q.finish(); await q.finish();
this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
} }
@ -3047,4 +3065,8 @@ export class Wallet {
// FIXME(#5210) also GC coins // FIXME(#5210) also GC coins
} }
clearNotification(): void {
this.badge.clearNotification();
}
} }

View File

@ -62,6 +62,11 @@ export class ChromeBadge implements Badge {
*/ */
private gapWidth: number = 0; private gapWidth: number = 0;
/**
* Should we show the notification dot?
*/
private hasNotification = false;
/** /**
* Maximum value for our rotationAngle, corresponds to 2 Pi. * Maximum value for our rotationAngle, corresponds to 2 Pi.
*/ */
@ -150,6 +155,21 @@ export class ChromeBadge implements Badge {
// go back to the origin // go back to the origin
this.ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2); this.ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2);
if (this.hasNotification) {
// We draw a circle with a soft border in the
// lower right corner.
const r = 8;
const cw = this.canvas.width;
const ch = this.canvas.height;
this.ctx.beginPath();
this.ctx.arc(cw - r, ch - r, r, 0, 2 * Math.PI, false);
const gradient = this.ctx.createRadialGradient(cw - r, ch - r, r, cw - r, ch - r, 5);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(1, "blue");
this.ctx.fillStyle = gradient;
this.ctx.fill();
}
// Allow running outside the extension for testing // Allow running outside the extension for testing
// tslint:disable-next-line:no-string-literal // tslint:disable-next-line:no-string-literal
if (window["chrome"] && window.chrome["browserAction"]) { if (window["chrome"] && window.chrome["browserAction"]) {
@ -211,6 +231,23 @@ export class ChromeBadge implements Badge {
rAF(step); rAF(step);
} }
/**
* Draw the badge such that it shows the
* user that something happened (balance changed).
*/
showNotification() {
this.hasNotification = true;
this.draw();
}
/**
* Draw the badge without the notification mark.
*/
clearNotification() {
this.hasNotification = false;
this.draw();
}
startBusy() { startBusy() {
if (this.isBusy) { if (this.isBusy) {
return; return;

View File

@ -208,6 +208,10 @@ export interface MessageMap {
request: types.TipStatusRequest; request: types.TipStatusRequest;
response: void; response: void;
}; };
"clear-notification": {
request: { };
response: void;
};
} }
/** /**

View File

@ -566,4 +566,7 @@ const el = (
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
ReactDOM.render(el, document.getElementById("content")!); ReactDOM.render(el, document.getElementById("content")!);
// Will be used by the backend to detect when the popup gets closed,
// so we can clear notifications
chrome.runtime.connect({name: "popup"});
}); });

View File

@ -381,3 +381,7 @@ export function acceptTip(merchantDomain: string, tipId: string): Promise<TipSta
export function processTipResponse(merchantDomain: string, tipId: string, tipResponse: TipResponse): Promise<void> { export function processTipResponse(merchantDomain: string, tipId: string, tipResponse: TipResponse): Promise<void> {
return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse }); return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse });
} }
export function clearNotification(): Promise<void> {
return callBackend("clear-notification", { });
}

View File

@ -337,6 +337,9 @@ function handleMessage(sender: MessageSender,
const req = GetTipPlanchetsRequest.checked(detail); const req = GetTipPlanchetsRequest.checked(detail);
return needsWallet().getTipPlanchets(req.merchantDomain, req.tipId, req.amount, req.deadline, req.exchangeUrl, req.nextUrl); return needsWallet().getTipPlanchets(req.merchantDomain, req.tipId, req.amount, req.deadline, req.exchangeUrl, req.nextUrl);
} }
case "clear-notification": {
return needsWallet().clearNotification();
}
default: default:
// Exhaustiveness check. // Exhaustiveness check.
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html // See https://www.typescriptlang.org/docs/handbook/advanced-types.html
@ -698,6 +701,23 @@ export async function wxMain() {
}); });
// Clear notifications both when the popop opens,
// as well when it closes.
chrome.runtime.onConnect.addListener((port) => {
if (port.name == "popup") {
if (currentWallet) {
currentWallet.clearNotification();
}
port.onDisconnect.addListener(() => {
if (currentWallet) {
currentWallet.clearNotification();
}
});
}
});
// Handlers for catching HTTP requests // Handlers for catching HTTP requests
chrome.webRequest.onHeadersReceived.addListener((details) => { chrome.webRequest.onHeadersReceived.addListener((details) => {
const wallet = currentWallet; const wallet = currentWallet;