Compare commits

...
5 changed files with 21 additions and 2 deletions

No files matched your search

@@ -270,4 +270,5 @@ export const EN_REMOTE: Record<string, string> = {
"这台手机还没有被电脑批准。": "This phone has not been approved by the computer yet.",
"需要重新登录账号后再试。": "Sign in to your account again, then try again.",
"账号服务暂时不可用,请稍后再试。": "The account service is unavailable right now; try again later.",
"浏览器没能向它的推送服务登记,所以这台手机暂时收不到提醒。常见原因是网络连不上浏览器厂商的推送服务。": "The browser could not register with its push service, so this phone cannot get reminders for now. A common cause is a network that cannot reach the browser maker's push service.",
};
@@ -68,6 +68,13 @@ describe("switching phone notifications on", () => {
expect(b.subscribed).toEqual([]);
});
it("names a browser that cannot reach its push service, and sends the account nothing", async () => {
const s = server();
const b = browser({ subscribe: async () => { throw new DOMException("Registration failed - permission denied", "AbortError"); } });
await expect(enablePush(s.account, b, async () => "", HOST, DEVICE, 7, CATEGORIES)).rejects.toMatchObject({ reason: "push_service" });
expect(s.calls.map((c) => c.path)).toEqual(["/me/remote-push/key"]);
});
it("refuses a browser without push and a phone the desktop has not approved", async () => {
const s = server();
await expect(enablePush(s.account, browser({ supported: () => false }), async () => "", HOST, DEVICE, 7, CATEGORIES)).rejects.toBeInstanceOf(EnableRefused);
+4 -2
View File
@@ -54,7 +54,7 @@ export function realPushBrowser(): PushBrowser {
};
}
export type EnableRefusal = "unsupported" | "denied" | "not_active";
export type EnableRefusal = "unsupported" | "denied" | "not_active" | "push_service";
export class EnableRefused extends Error {
constructor(readonly reason: EnableRefusal) {
@@ -75,7 +75,9 @@ export async function enablePush(
const answer = browser.permission() === "granted" ? "granted" : await browser.requestPermission();
if (answer !== "granted") throw new EnableRefused("denied");
const { publicKey } = await account.call<{ publicKey: string }>("GET", "/me/remote-push/key");
const subscription = await browser.subscribe(fromBase64url(publicKey));
// Whatever the browser says when it cannot register with its own push service
// is its answer, not the account's.
const subscription = await browser.subscribe(fromBase64url(publicKey)).catch(() => { throw new EnableRefused("push_service"); });
const { nonce } = await account.call<{ nonce: string }>("POST", "/me/remote-controllers/challenge", { host, purpose: "push" });
const signature = await sign_(await pushMessage({
nonce, userId, host, deviceId: device.id, endpoint: subscription.endpoint, p256dh: subscription.p256dh, auth: subscription.auth,
@@ -20,6 +20,7 @@ function problemFor(error: unknown): string {
if (error instanceof EnableRefused) {
if (error.reason === "denied") return t("浏览器没有允许通知。请在浏览器的网站设置里允许这个页面发通知,再打开这个开关。");
if (error.reason === "unsupported") return t("这个浏览器不能接收提醒。");
if (error.reason === "push_service") return t("浏览器没能向它的推送服务登记,所以这台手机暂时收不到提醒。常见原因是网络连不上浏览器厂商的推送服务。");
return t("这台手机还没有被电脑批准。");
}
if (error instanceof AccountError && (error.status === 401 || error.code === "remote_reauth_required")) {
@@ -100,6 +100,14 @@ describe("this phone's reminders", () => {
expect((await master()).getAttribute("aria-checked")).toBe("false");
});
it("says the browser could not reach its push service, not that the account is down", async () => {
draw({ kind: "off" }, { enable: vi.fn(async () => { throw new EnableRefused("push_service"); }) });
await userEvent.click(await master());
const alert = await screen.findByRole("alert");
expect(alert.textContent).toContain("推送服务");
expect(alert.textContent).not.toContain("账号服务");
});
it("asks for a new sign-in when the account no longer knows this session", async () => {
draw({ kind: "off" }, { enable: vi.fn(async () => { throw new AccountError(401, "unauthorized"); }) });
await userEvent.click(await master());