feat(pwa): на экране только включение уведомлений, выключения нет
Строка предлагает включить, пока уведомления выключены, и исчезает, когда они включены. Кнопка «выключить» рядом с балансом только подталкивала бы отказаться от двух событий, ради которых приложение и открывают. Отключение при этом никуда не делось: оно осталось системным — в настройках уведомлений телефона или в настройках сайта в браузере, там же, где человек управляет всеми остальными приложениями. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
vi.mock("@/lib/push", () => ({
|
||||
currentState: vi.fn(async () => "off"),
|
||||
enable: vi.fn(async () => "on"),
|
||||
}));
|
||||
|
||||
import { NotificationsRow } from "./NotificationsRow";
|
||||
import * as push from "@/lib/push";
|
||||
|
||||
describe("строка уведомлений", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(push.currentState).mockResolvedValue("off");
|
||||
});
|
||||
|
||||
it("выключенные уведомления предлагает включить", async () => {
|
||||
render(<NotificationsRow />);
|
||||
await userEvent.click(await screen.findByRole("button", { name: /Включить уведомления/ }));
|
||||
expect(push.enable).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("включённые — строки нет: отказаться от них мы не предлагаем", async () => {
|
||||
// Отключение остаётся системным (настройки телефона или сайта в браузере) —
|
||||
// рядом с балансом кнопка «выключить» только подталкивала бы к отказу.
|
||||
vi.mocked(push.currentState).mockResolvedValue("on");
|
||||
const { container } = render(<NotificationsRow />);
|
||||
await vi.waitFor(() => expect(container.textContent).toBe(""));
|
||||
expect(screen.queryByText(/Выключить/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("запрещённые в браузере — объясняем, а не предлагаем кнопку", async () => {
|
||||
vi.mocked(push.currentState).mockResolvedValue("denied");
|
||||
render(<NotificationsRow />);
|
||||
expect(await screen.findByText(/запрещены в настройках браузера/)).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Bell, BellOff } from "lucide-react";
|
||||
import { Bell } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { currentState, disable, enable, type PushState } from "@/lib/push";
|
||||
import { currentState, enable, type PushState } from "@/lib/push";
|
||||
|
||||
/**
|
||||
* Включение уведомлений — одной строкой на главном экране.
|
||||
@@ -9,6 +9,13 @@ import { currentState, disable, enable, type PushState } from "@/lib/push";
|
||||
* Не всплывающим окном на входе: разрешение спрашивается один раз за установку,
|
||||
* и потраченная впустую попытка закрывает канал навсегда. Здесь водитель жмёт
|
||||
* сам, увидев, ради чего.
|
||||
*
|
||||
* Выключения здесь НЕТ намеренно (решение владельца 19.08.2026). Включённые
|
||||
* уведомления — это ровно те два события, ради которых приложение и открывают;
|
||||
* кнопка «выключить» рядом с балансом только предлагает от них отказаться.
|
||||
* Способ отключить остаётся системный: настройки уведомлений телефона или
|
||||
* настройки сайта в браузере — там же, где человек управляет всеми остальными
|
||||
* приложениями.
|
||||
*/
|
||||
export function NotificationsRow() {
|
||||
const [state, setState] = useState<PushState | null>(null);
|
||||
@@ -20,14 +27,15 @@ export function NotificationsRow() {
|
||||
return () => { alive = false; };
|
||||
}, []);
|
||||
|
||||
if (state === null || state === "unsupported") return null;
|
||||
// Включено — строки нет: предлагать нечего.
|
||||
if (state === null || state === "unsupported" || state === "on") return null;
|
||||
|
||||
async function toggle() {
|
||||
async function turnOn() {
|
||||
setBusy(true);
|
||||
try {
|
||||
setState(state === "on" ? await disable() : await enable());
|
||||
setState(await enable());
|
||||
} catch {
|
||||
toast.error("Не удалось изменить настройку уведомлений");
|
||||
toast.error("Не удалось включить уведомления");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -53,10 +61,9 @@ export function NotificationsRow() {
|
||||
<button
|
||||
className="flex items-center justify-center gap-1.5 text-muted text-xs mt-6 w-full"
|
||||
disabled={busy}
|
||||
onClick={toggle}
|
||||
onClick={turnOn}
|
||||
>
|
||||
{state === "on" ? <BellOff size={15} /> : <Bell size={15} />}
|
||||
{state === "on" ? "Выключить уведомления" : "Включить уведомления"}
|
||||
<Bell size={15} /> Включить уведомления
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user