diff --git a/CHANGELOG.md b/CHANGELOG.md index f1fac777..794c51a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 1.6.1 (2026-05-04) + +### Features + +- **Updates**: Update-available detection with non-dismissible notice and dev-reload refresh +- **Plugins**: New plugin hooks for compose, attachments, search, lifecycle, and routing +- **Sharing**: Share indicators for calendars and contacts, updated JMAP capabilities (#244) +- **Mail**: Auto-add recipients to trusted senders when replying +- **Identity**: Sanitize identity display name to prevent invalid `From` headers + +### Fixes + +- **Mobile**: Synchronize mobile submenu view with browser history for better navigation +- **Viewer**: Update email viewer styles to improve overflow handling +- **Auth**: Ensure `cookieSlot` consistency during account updates in auth store +- **Auth**: Thread per-account cookie slot through OAuth flows +- **Calendar**: Square the colored left marker on calendar events +- **About**: Show git commit in About instead of "unknown" + +### i18n + +- Update mailbox context menu translations across 12 locales + ## 1.6.0 (2026-05-01) ### Features diff --git a/README.md b/README.md index 964c9e72..0694d9a8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A modern, self-hosted webmail client for [Stalwart Mail Server](https://stalw.ar [![License: AGPL v3](https://img.shields.io/badge/license-AGPL%20v3-blue.svg?logo=gnu&logoColor=white)](LICENSE) [![Discord](https://img.shields.io/discord/1482128142939455674?color=7289da&label=discord&logo=discord&logoColor=white)](https://discord.gg/tYCujymGrT) -[![Version](https://img.shields.io/badge/version-1.6.0-green.svg?logo=git&logoColor=white)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-1.6.1-green.svg?logo=git&logoColor=white)](CHANGELOG.md) [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fbulwarkmail%2Fwebmail-blue?logo=docker&logoColor=white)](https://ghcr.io/bulwarkmail/webmail) diff --git a/VERSION b/VERSION index ce6a70b9..2eda823f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.0 \ No newline at end of file +1.6.1 \ No newline at end of file diff --git a/app/[locale]/login/page.tsx b/app/[locale]/login/page.tsx index 8e0872cb..5bdd1938 100644 --- a/app/[locale]/login/page.tsx +++ b/app/[locale]/login/page.tsx @@ -7,6 +7,7 @@ import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useAuthStore } from "@/stores/auth-store"; +import { useAccountStore } from "@/stores/account-store"; import { useThemeStore } from "@/stores/theme-store"; import { useShallow } from "zustand/react/shallow"; import { useConfig } from "@/hooks/use-config"; @@ -460,6 +461,16 @@ export default function LoginPage() { sessionStorage.setItem("oauth_add_account_mode", "true"); } + // Persist the next-free cookie slot so loginWithOAuth (in stores/auth-store.ts) + // writes the refresh token to the correct per-account jmap_rt_ cookie. + // loginWithOAuth reads this key but it was previously never written, so every + // OAuth account collapsed onto slot 0 and clobbered earlier accounts' refresh + // tokens. getNextCookieSlot() returns 0 when no accounts exist (correct for + // first sign-in) and the lowest unused slot otherwise (correct for "+ Add + // Account"). + const nextSlot = useAccountStore.getState().getNextCookieSlot(); + sessionStorage.setItem("oauth_cookie_slot", nextSlot.toString()); + const authUrl = new URL(oauthMetadata.authorization_endpoint); authUrl.searchParams.set("response_type", "code"); authUrl.searchParams.set("client_id", oauthClientId); diff --git a/app/[locale]/settings/page.tsx b/app/[locale]/settings/page.tsx index eeda92b8..543c3b24 100644 --- a/app/[locale]/settings/page.tsx +++ b/app/[locale]/settings/page.tsx @@ -213,6 +213,22 @@ export default function SettingsPage() { } }, [initialCheckDone, isAuthenticated, authLoading]); + // Sync the mobile submenu view with browser history so the system back + // button (or gesture) returns to the settings list before exiting /settings. + useEffect(() => { + if (isDesktop) return; + if (typeof window === 'undefined') return; + if (!mobileShowContent) return; + + window.history.pushState({ __settingsSubmenu: true }, ''); + + const handlePop = () => { + setMobileShowContent(false); + }; + window.addEventListener('popstate', handlePop); + return () => window.removeEventListener('popstate', handlePop); + }, [isDesktop, mobileShowContent]); + if (!isAuthenticated) { return null; } @@ -321,7 +337,7 @@ export default function SettingsPage() {