Skip to content

Latest commit

 

History

History
279 lines (197 loc) · 9.58 KB

File metadata and controls

279 lines (197 loc) · 9.58 KB

Migration Guide: 1.4.0

This release introduces global preferences — a new, reader-independent layer that owns locale and derives UI direction from it. It is a breaking change for any integration that set locale or direction on reader/audio preferences, read them back via useSharedPreferences, or dispatched setL10n directly.


Overview of changes

Area Change
ThPreferences / ThAudioPreferences locale and direction fields removed
createPreferences / createAudioPreferences Locale validation removed — use createGlobalPreferences
useSharedPreferences No longer returns locale or direction
ThDirectionSetter No longer accepts a direction prop — derives direction from React Aria context
Per-reader I18nProvider Removed from StatefulReader and StatefulPlayer — now provided globally
preferencesReducer L10nObject, setL10n, and the l10n slice removed
Redux store New globalPreferences key added
Preferences helpers No longer marked "use client" — importable from server components
Settings components New useReaderSetting hook to handle state based on profile epub/wepub

Breaking: locale and direction removed from reader preferences

locale and direction no longer exist on ThPreferences or ThAudioPreferences. They are now managed by the new ThGlobalPreferencesProvider which must be placed above all readers in your component tree.

Before (1.3.x)

import { createPreferences } from "@edrlab/thorium-web/preferences";

const prefs = createPreferences({
  locale: "ar",
  direction: "rtl",
  // ... rest of preferences
});

<ThStoreProvider>
  <StatefulReaderWrapper
    preferences={{ initialPreferences: prefs }}
    { ...props }
  />
</ThStoreProvider>

After (1.4.0)

import { createPreferences } from "@edrlab/thorium-web/preferences";
import { createGlobalPreferences, ThGlobalPreferencesProvider } from "@edrlab/thorium-web/preferences";

const globalPrefs = createGlobalPreferences({ locale: "ar" });

const readerPrefs = createPreferences({
  // locale and direction no longer accepted here
  // ... rest of preferences
});

<ThStoreProvider>
  <ThGlobalPreferencesProvider initialPreferences={ globalPrefs }>
    <StatefulReaderWrapper
      preferences={{ initialPreferences: readerPrefs }}
      { ...props }
    />
  </ThGlobalPreferencesProvider>
</ThStoreProvider>

ThGlobalPreferencesProvider wraps React Aria's I18nProvider internally and derives the document direction from the locale automatically. You no longer store or manage direction anywhere.


Breaking: useSharedPreferences no longer returns locale or direction

Any component reading locale or direction from useSharedPreferences must be updated.

// Before
const { locale, direction } = useSharedPreferences();

// After — locale
import { useGlobalPreferences } from "@edrlab/thorium-web/preferences";
const { preferences: { locale } } = useGlobalPreferences();

// After — direction (derived, not stored)
import { useLocale } from "react-aria";
const { direction } = useLocale();

Important

useLocale() returns the UI direction derived from the locale preference. For publication-level direction (RTL content), continue using state.publication.isRTL from the Redux store.


Breaking: setL10n and L10nObject removed from preferencesReducer

The l10n slice, setL10n action, and L10nObject type have been removed from preferencesReducer. Locale is now persisted in the dedicated globalPreferencesReducer.

// Before
import { setL10n } from "@edrlab/thorium-web/lib";
dispatch(setL10n({ locale: "fr", direction: "ltr" }));

// After
import { setLocale } from "@edrlab/thorium-web/lib";
dispatch(setLocale("fr"));

The Redux store now has a globalPreferences key (GlobalPreferencesReducerState) alongside the existing preferences key. Both are persisted to localStorage automatically.


Breaking: Redux persistence adapter for locale

If you used ThReduxPreferencesAdapter and expected it to sync locale back into your preferences, this no longer happens. Locale is now handled exclusively by ThReduxGlobalPreferencesAdapter.

Use StatefulGlobalPreferencesProvider to wire global preferences to Redux — it follows the same pattern as StatefulPreferencesProvider:

import { StatefulGlobalPreferencesProvider } from "@edrlab/thorium-web/components";

<ThStoreProvider>
  <StatefulGlobalPreferencesProvider initialPreferences={ globalPrefs }>
    <StatefulReaderWrapper { ...props } />
  </StatefulGlobalPreferencesProvider>
</ThStoreProvider>

New: ThGlobalPreferencesProvider

import { ThGlobalPreferencesProvider } from "@edrlab/thorium-web/preferences";

<ThGlobalPreferencesProvider
  initialPreferences={{ locale: "fr" }}
  adapter={ myGlobalAdapter }  // optional — defaults to in-memory
>
  { children }
</ThGlobalPreferencesProvider>

The provider:

  • Validates the locale via createGlobalPreferences (unsupported locales fall back to browser default)
  • Wraps React Aria's I18nProvider so useLocale() works anywhere in the tree
  • Sets document.documentElement.dir reactively via the updated ThDirectionSetter

Props

Prop Type Description
initialPreferences ThGlobalPreferences Optional initial locale
adapter ThGlobalPreferencesAdapter Optional custom persistence adapter
children ReactNode Required

New: createGlobalPreferences

import { createGlobalPreferences } from "@edrlab/thorium-web/preferences";

const prefs = createGlobalPreferences({ locale: "ar" });
// Unsupported locales are silently discarded and fall back to browser language

This function is not marked "use client" and can be called in Server Components and layout.tsx.


New: useGlobalPreferences

import { useGlobalPreferences } from "@edrlab/thorium-web/preferences";

const { preferences, updatePreferences } = useGlobalPreferences();
// preferences.locale — current locale string or undefined

updatePreferences({ locale: "fr" });

New: StatefulGlobalPreferencesProvider

A Redux-backed wrapper around ThGlobalPreferencesProvider. Reads and writes locale through globalPreferencesReducer so it is persisted to localStorage alongside the rest of the app state.

import { StatefulGlobalPreferencesProvider } from "@edrlab/thorium-web/components";

See the updated Handling Preferences guide for full integration examples.


Changed: factory functions are now server-safe

createPreferences, createAudioPreferences, and createGlobalPreferences no longer carry the "use client" directive. They can be imported and called in Server Components or layout.tsx directly:

// layout.tsx (Server Component) — now works without error
import { createGlobalPreferences } from "@edrlab/thorium-web/preferences";

const prefs = createGlobalPreferences({ locale: "ar" });

Previously, importing from the @edrlab/thorium-web/preferences barrel in a server context would throw because the entire barrel was marked "use client". The directive is now scoped to individual component and hook files only.


Provider hierarchy reference

Minimal (no Redux persistence for locale)

<ThStoreProvider>
  <ThGlobalPreferencesProvider initialPreferences={ globalPrefs }>
    <StatefulReaderWrapper { ...props } />
  </ThGlobalPreferencesProvider>
</ThStoreProvider>

With Redux persistence

<ThStoreProvider>
  <StatefulGlobalPreferencesProvider initialPreferences={ globalPrefs }>
    <StatefulReaderWrapper { ...props } />
  </StatefulGlobalPreferencesProvider>
</ThStoreProvider>

New: settings components are now using a hook for getting states

A new centralized hook has been introduced to handle settings’ states:

import { useReaderSetting } from "@edrlab/thorium-web/reader";

// Automatically handles profile (webPub vs epub)
const fontWeight = useReaderSetting("fontWeight");
const zoom = useReaderSetting("zoom"); // Transparently maps to fontSize for epub

The hook:

  • Automatically selects the correct store slice (settings vs webPubSettings) based on reader profile
  • Provides fallback from initial state defaults
  • Transparently maps profile-specific keys (e.g., zoomfontSize for non-webPub profiles)

Before (1.3.x)

const profile = useAppSelector(state => state.reader.profile);
const isWebPub = profile === "webPub";
const resolvedLanguage = useAppSelector(state => state.publication.resolvedLanguage) || "default";

const fontWeight = useAppSelector(state => {
  const w = isWebPub ? state.webPubSettings.fontWeight : state.settings.fontWeight;
  return w?.[resolvedLanguage] ?? 400;
});

After (1.4.0)

const fontWeight = useReaderSetting("fontWeight");

Migration checklist

  • Remove locale and direction from all createPreferences / createAudioPreferences calls
  • Add ThGlobalPreferencesProvider (or StatefulGlobalPreferencesProvider) above readers
  • Replace useSharedPreferences().locale with useGlobalPreferences().preferences.locale
  • Replace useSharedPreferences().direction with useLocale().direction from react-aria
  • Replace dispatch(setL10n({ locale })) with dispatch(setLocale(locale))
  • Remove L10nObject type references
  • Verify no component reads state.preferences.l10n — it no longer exists
  • Migrate settings components to use useReaderSetting instead of manual useAppSelector calls