Files
webmail/components/pro/pro-interface-redirect.tsx
T
Linus Rath aa99f038d1 fix: keep a scheduled message when its new time loses a recipient
"Send now" and "Reschedule" create a replacement submission and then
cancel the original. The server checks the recipients again for the
replacement, and a throttle used up since the original was taken ("452
4.4.5 Rate limit exceeded") can refuse some of them. The original was
kept only when every recipient was refused; on a partial refusal it was
cancelled, and a recipient the original had taken lost the message
without anyone being told.

- The original's delivery status is read with its envelope. When the
  replacement refuses a recipient the original took, the replacement is
  cancelled (its record only destroyed when it took nobody) and the
  original goes at its old time. A recipient the original refused as
  well loses nothing, so the reschedule goes ahead; checked live on
  0.16.23 and 1.0 with a relay-refused recipient.
- The toast for a kept schedule leads with "The message is still
  scheduled for ..." and says "Nothing was changed: the server refused
  ...". It used to open with "Not sent", which invited sending the
  message a second time.
- Recipients refused while the others were taken are now named after
  "Send now", a reschedule from the list or the viewer, and a quick
  reply, which said "Email sent".

Files, from the review of d9ffe8a7:

- An own folder opened from search while Files showed a shared folder
  opened the shared folder again: its link had no query, and the page
  read `?shared=` from the address bar. The link now says it has none.
- The Pro shell kept only the path of a Files link across its redirect,
  so a shared folder's link (`/files?shared=...`) didn't survive a
  reload, and neither did `?preview=`. The query goes along now.
- A search hit for a file on a "Files from" page opened the first file
  of that name there; it is found by the id the link names now.
- Undoing a paste of files cut on a "Files from" page moved them to
  "*", the page's id; they go back to their own folders now.
2026-09-26 02:25:40 +02:00

68 lines
2.7 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { usePathname, useRouter } from "@/i18n/navigation";
import { useSettingsStore } from "@/stores/settings-store";
import { useIsDesktop } from "@/hooks/use-media-query";
import { useProTabStore, type ProAppTabKind } from "@/stores/pro-tab-store";
import { matchSurface, type AppSurface } from "@/lib/deep-links";
import { setPendingDeepLink } from "@/lib/deep-link-handoff";
const SURFACE_TO_TAB: Record<AppSurface, ProAppTabKind> = {
mail: 'mail',
calendar: 'calendar',
contacts: 'contacts',
files: 'files',
settings: 'settings',
};
/**
* Whether the Pro shell is currently taking over the standard routes. Surfaces
* use this to keep off the address bar: under Pro the only route is /pro, so a
* surface rewriting the URL to its own permalink would fight the redirect.
*/
export function useProInterfaceActive(): boolean {
const proInterface = useSettingsStore((s) => s.proInterface);
const isDesktop = useIsDesktop();
return proInterface && isDesktop;
}
/**
* When the Pro interface is enabled, the standard mail/calendar/contacts/
* files/settings routes are taken over by the Pro shell - the user shouldn't
* have to click "Open" in settings to land there. Mobile/tablet keeps the
* standard layout because Pro is desktop-only (see pro/page.tsx).
*
* Surfaces are matched by prefix rather than by exact path, and any deep-link
* segments are handed to the tab that opens (#733), so `/mail/message/<id>`
* opens that message in a Pro mail tab instead of silently landing on the
* inbox.
*/
export function ProInterfaceRedirect() {
const router = useRouter();
const pathname = usePathname();
const proInterface = useSettingsStore((s) => s.proInterface);
const isDesktop = useIsDesktop();
useEffect(() => {
if (!proInterface || !isDesktop) return;
const match = matchSurface(pathname);
if (!match) return;
// Focus the tab before handing over the link: when the surface is already
// mounted the handoff delivers live, and its handler may itself open a
// tab (a message permalink opens an email tab) that must keep focus.
useProTabStore.getState().openTab(SURFACE_TO_TAB[match.surface]);
// The query half goes with it (`?shared=` of a shared drive's folder,
// `?preview=`, `?account=`): /pro has none left by the time the surface
// reads the link, and a link that is only a query - a shared folder is
// named by id - would be lost on reload.
const search = window.location.search;
if (match.segments.length > 0 || search) {
setPendingDeepLink(match.surface, match.segments, search || undefined);
}
router.replace('/pro');
}, [proInterface, isDesktop, pathname, router]);
return null;
}