From 4b282e7064ccb827e44ee9fee3b108a9bd4ff0b9 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Sun, 11 Oct 2026 20:09:31 +0200
Subject: [PATCH] fix: give touch tips in the welcome banner on a phone
---
.../ui/__tests__/welcome-banner.test.tsx | 37 +++++++++++++++++++
components/ui/welcome-banner.tsx | 9 +++--
locales/en/common.json | 2 +
3 files changed, 45 insertions(+), 3 deletions(-)
create mode 100644 components/ui/__tests__/welcome-banner.test.tsx
diff --git a/components/ui/__tests__/welcome-banner.test.tsx b/components/ui/__tests__/welcome-banner.test.tsx
new file mode 100644
index 000000000..f91c9b069
--- /dev/null
+++ b/components/ui/__tests__/welcome-banner.test.tsx
@@ -0,0 +1,37 @@
+import { render, screen } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import React from 'react';
+
+// On a phone the banner said "Press c to compose" and pointed at a sidebar
+// that the phone layout does not have.
+
+const device = vi.hoisted(() => ({ isMobile: false }));
+vi.mock('@/hooks/use-media-query', () => ({
+ useDeviceDetection: () => ({ isMobile: device.isMobile, isTablet: false, isDesktop: !device.isMobile }),
+}));
+vi.mock('@/components/tour/tour-provider', () => ({ useTour: () => ({ startTour: vi.fn() }) }));
+vi.mock('@/i18n/navigation', () => ({ useRouter: () => ({ push: vi.fn() }) }));
+
+const { WelcomeBanner } = await import('../welcome-banner');
+const { useSettingsStore } = await import('@/stores/settings-store');
+
+describe('WelcomeBanner tips', () => {
+ beforeEach(() => {
+ useSettingsStore.setState({ onboardingCompleted: false });
+ });
+
+ it('gives keyboard tips on a desktop', () => {
+ device.isMobile = false;
+ render();
+ expect(screen.getByText('tip_compose')).toBeInTheDocument();
+ expect(screen.getByText('tip_shortcuts')).toBeInTheDocument();
+ });
+
+ it('gives touch tips on a phone', () => {
+ device.isMobile = true;
+ render();
+ expect(screen.getByText('tip_compose_touch')).toBeInTheDocument();
+ expect(screen.getByText('tip_sidebar_touch')).toBeInTheDocument();
+ expect(screen.queryByText('tip_shortcuts')).toBeNull();
+ });
+});
diff --git a/components/ui/welcome-banner.tsx b/components/ui/welcome-banner.tsx
index a2082b7f3..736418b37 100644
--- a/components/ui/welcome-banner.tsx
+++ b/components/ui/welcome-banner.tsx
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
import { useRouter } from "@/i18n/navigation";
import { useTour } from "@/components/tour/tour-provider";
import { useSettingsStore } from "@/stores/settings-store";
+import { useDeviceDetection } from "@/hooks/use-media-query";
const ONBOARDING_KEY = "onboarding_completed";
@@ -19,6 +20,8 @@ export function WelcomeBanner() {
const updateSetting = useSettingsStore((s) => s.updateSetting);
const [visible, setVisible] = useState(false);
const [dismissed, setDismissed] = useState(false);
+ // A phone has no keyboard shortcuts and keeps the apps in the bottom bar.
+ const { isMobile } = useDeviceDetection();
useEffect(() => {
// One-time migration: if the legacy per-device flag is set but the synced
@@ -89,9 +92,9 @@ export function WelcomeBanner() {
{t("title")}
- - {t("tip_compose")}
- - {t("tip_shortcuts")}
- - {t("tip_sidebar")}
+ - {isMobile ? t("tip_compose_touch") : t("tip_compose")}
+ {!isMobile && - {t("tip_shortcuts")}
}
+ - {isMobile ? t("tip_sidebar_touch") : t("tip_sidebar")}
- {t("tip_settings")}
diff --git a/locales/en/common.json b/locales/en/common.json
index 95fe437da..b3aab598e 100644
--- a/locales/en/common.json
+++ b/locales/en/common.json
@@ -3600,6 +3600,8 @@
"tip_shortcuts": "Press ? to see all keyboard shortcuts",
"tip_sidebar": "Find Calendar, Contacts and Settings in the sidebar menu",
"tip_settings": "Customize your experience in Settings",
+ "tip_compose_touch": "Tap the pencil button to write a new email",
+ "tip_sidebar_touch": "Find Calendar, Contacts and Settings in the bar at the bottom",
"got_it": "Got it",
"settings": "Settings",
"dismiss": "Dismiss",