From 7342a6c9d538ec5691af10f5c08f83d7cea6529d Mon Sep 17 00:00:00 2001 From: vladtechno Date: Wed, 19 Aug 2026 13:30:19 +1000 Subject: [PATCH] =?UTF-8?q?feat(pwa):=20=D0=BD=D0=B0=20=D1=8D=D0=BA=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20?= =?UTF-8?q?=D0=B2=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=83?= =?UTF-8?q?=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9,?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Строка предлагает включить, пока уведомления выключены, и исчезает, когда они включены. Кнопка «выключить» рядом с балансом только подталкивала бы отказаться от двух событий, ради которых приложение и открывают. Отключение при этом никуда не делось: оно осталось системным — в настройках уведомлений телефона или в настройках сайта в браузере, там же, где человек управляет всеми остальными приложениями. Co-Authored-By: Claude Opus 5 --- .../src/components/NotificationsRow.test.tsx | 40 +++++++++++++++++++ .../src/components/NotificationsRow.tsx | 25 +++++++----- 2 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 driver-pwa/frontend/src/components/NotificationsRow.test.tsx diff --git a/driver-pwa/frontend/src/components/NotificationsRow.test.tsx b/driver-pwa/frontend/src/components/NotificationsRow.test.tsx new file mode 100644 index 0000000..ab019f2 --- /dev/null +++ b/driver-pwa/frontend/src/components/NotificationsRow.test.tsx @@ -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(); + await userEvent.click(await screen.findByRole("button", { name: /Включить уведомления/ })); + expect(push.enable).toHaveBeenCalled(); + }); + + it("включённые — строки нет: отказаться от них мы не предлагаем", async () => { + // Отключение остаётся системным (настройки телефона или сайта в браузере) — + // рядом с балансом кнопка «выключить» только подталкивала бы к отказу. + vi.mocked(push.currentState).mockResolvedValue("on"); + const { container } = render(); + await vi.waitFor(() => expect(container.textContent).toBe("")); + expect(screen.queryByText(/Выключить/)).not.toBeInTheDocument(); + }); + + it("запрещённые в браузере — объясняем, а не предлагаем кнопку", async () => { + vi.mocked(push.currentState).mockResolvedValue("denied"); + render(); + expect(await screen.findByText(/запрещены в настройках браузера/)).toBeInTheDocument(); + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); +}); diff --git a/driver-pwa/frontend/src/components/NotificationsRow.tsx b/driver-pwa/frontend/src/components/NotificationsRow.tsx index a9161a2..74fc0a6 100644 --- a/driver-pwa/frontend/src/components/NotificationsRow.tsx +++ b/driver-pwa/frontend/src/components/NotificationsRow.tsx @@ -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(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() { ); }