feat: Implement view transitions for smoother navigation

- Added `useViewTransitions` hook to enable view transitions in the app.
- Created `useNavigateWithTransition` for navigation with transitions.
- Integrated view transitions in `ClientRoot`, `HomeTab`, `InboxTab`, and `SettingsTab`.
- Introduced `AnimatedOutlet` and `RouteTransition` components for route-based animations.
- Enhanced CSS for transitions and added a new `twilightTheme`.
- Updated Vite configuration to serve sound and font assets.
- Added support for reaction notifications in the room utility.
- Created `DirectList` and related components for direct messaging functionality.
This commit is contained in:
2026-03-25 06:14:11 +11:00
parent a8eb404ff3
commit 9a00375568
21 changed files with 1049 additions and 70 deletions

View File

@@ -1,7 +1,7 @@
import { lightTheme } from 'folds';
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { onDarkFontWeight, onLightFontWeight } from '../../config.css';
import { butterTheme, darkTheme, discordTheme, discordDarkerTheme, silverTheme } from '../../colors.css';
import { butterTheme, darkTheme, discordTheme, discordDarkerTheme, silverTheme, twilightTheme } from '../../colors.css';
import { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings';
@@ -47,9 +47,14 @@ export const DiscordDarkerTheme: Theme = {
kind: ThemeKind.Dark,
classNames: ['discord-darker-theme', discordDarkerTheme, onDarkFontWeight, 'prism-dark'],
};
export const TwilightTheme: Theme = {
id: 'twilight-theme',
kind: ThemeKind.Dark,
classNames: ['twilight-theme', twilightTheme, onDarkFontWeight, 'prism-dark'],
};
export const useThemes = (): Theme[] => {
const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme, DiscordTheme, DiscordDarkerTheme], []);
const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme, DiscordTheme, DiscordDarkerTheme, TwilightTheme], []);
return themes;
};
@@ -63,6 +68,7 @@ export const useThemeNames = (): Record<string, string> =>
[ButterTheme.id]: 'Butter',
[DiscordTheme.id]: 'Discord',
[DiscordDarkerTheme.id]: 'Discord Darker',
[TwilightTheme.id]: 'Twilight',
}),
[]
);

View File

@@ -0,0 +1,85 @@
import { useCallback, useEffect } from 'react';
import { useLocation, useNavigate, NavigateOptions } from 'react-router-dom';
import { supportsViewTransitions, withViewTransition } from '../utils/viewTransitions';
/**
* Hook to enable View Transitions for React Router navigation
* This intercepts navigation and wraps it in view transitions
*/
export const useViewTransitions = () => {
const location = useLocation();
useEffect(() => {
if (!supportsViewTransitions()) {
return;
}
// Mark that we support view transitions
document.documentElement.classList.add('view-transitions-enabled');
return () => {
document.documentElement.classList.remove('view-transitions-enabled');
};
}, []);
useEffect(() => {
// Route changed - view transitions will be handled by CSS
}, [location]);
return {
supportsViewTransitions: supportsViewTransitions(),
};
};
/**
* Custom navigate hook that wraps navigation in view transitions
*
* **USAGE:**
* Replace `useNavigate()` with `useNavigateWithTransition()` for smooth transitions
*
* ```tsx
* // Before:
* import { useNavigate } from 'react-router-dom';
* const navigate = useNavigate();
*
* // After:
* import { useNavigateWithTransition } from '../hooks/useViewTransitions';
* const navigate = useNavigateWithTransition();
*
* // Usage remains the same:
* navigate('/some/path');
* navigate(-1); // Go back
* ```
*
* This enables smooth animated transitions when navigating between:
* - Different spaces
* - Different rooms
* - Different sections (Home, Inbox, Settings, etc.)
*
* Falls back to instant navigation on browsers that don't support View Transitions API.
*/
export const useNavigateWithTransition = () => {
const navigate = useNavigate();
return useCallback(
(to: string | number, options?: NavigateOptions) => {
if (!supportsViewTransitions()) {
if (typeof to === 'number') {
navigate(to);
} else {
navigate(to, options);
}
return;
}
withViewTransition(() => {
if (typeof to === 'number') {
navigate(to);
} else {
navigate(to, options);
}
});
},
[navigate]
);
};