mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): show cached auth splash art without a fade-in flash (#1907)
This commit is contained in:
1 parent
79cf57abe4
commit
5eb7822691
4 files changed
+28
-51
No files matched your search
@@ -41,7 +41,7 @@ const AuthLayoutContent = observer(function AuthLayoutContent({children}: {child
|
|||||||
const registerFormDraftsRef = useRef<Map<string, AuthRegisterFormDraft>>(new Map());
|
const registerFormDraftsRef = useRef<Map<string, AuthRegisterFormDraft>>(new Map());
|
||||||
const scrollerRef = useRef<ScrollerHandle>(null);
|
const scrollerRef = useRef<ScrollerHandle>(null);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const {patternReady, splashLoaded, splashDimensions} = useAuthBackground(splashUrl, foodPatternUrl);
|
const {patternReady, splashDimensions} = useAuthBackground(splashUrl, foodPatternUrl);
|
||||||
const handleSetSplashUrl = useCallback(
|
const handleSetSplashUrl = useCallback(
|
||||||
(url: string | null) => {
|
(url: string | null) => {
|
||||||
if (splashUrlRef.current === url) return;
|
if (splashUrlRef.current === url) return;
|
||||||
@@ -196,7 +196,6 @@ const AuthLayoutContent = observer(function AuthLayoutContent({children}: {child
|
|||||||
>
|
>
|
||||||
<AuthBackground
|
<AuthBackground
|
||||||
splashUrl={splashUrl}
|
splashUrl={splashUrl}
|
||||||
splashLoaded={splashLoaded}
|
|
||||||
splashDimensions={splashDimensions}
|
splashDimensions={splashDimensions}
|
||||||
splashScale={splashScale}
|
splashScale={splashScale}
|
||||||
patternReady={patternReady}
|
patternReady={patternReady}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
|
||||||
import styles from '@app/features/app/components/layout/AuthLayout.module.css';
|
import styles from '@app/features/app/components/layout/AuthLayout.module.css';
|
||||||
import {GuildSplashCardAlignment} from '@fluxer/constants/src/GuildConstants';
|
import {GuildSplashCardAlignment} from '@fluxer/constants/src/GuildConstants';
|
||||||
import type {ValueOf} from '@fluxer/constants/src/ValueOf';
|
import type {ValueOf} from '@fluxer/constants/src/ValueOf';
|
||||||
import {motion} from 'framer-motion';
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
|
|
||||||
const getSplashAlignmentStyles = (alignment: ValueOf<typeof GuildSplashCardAlignment>) => {
|
const getSplashAlignmentStyles = (alignment: ValueOf<typeof GuildSplashCardAlignment>) => {
|
||||||
@@ -20,7 +18,6 @@ const getSplashAlignmentStyles = (alignment: ValueOf<typeof GuildSplashCardAlign
|
|||||||
|
|
||||||
export interface AuthBackgroundProps {
|
export interface AuthBackgroundProps {
|
||||||
splashUrl: string | null;
|
splashUrl: string | null;
|
||||||
splashLoaded: boolean;
|
|
||||||
splashDimensions?: {width: number; height: number} | null;
|
splashDimensions?: {width: number; height: number} | null;
|
||||||
splashScale?: number | null;
|
splashScale?: number | null;
|
||||||
patternReady: boolean;
|
patternReady: boolean;
|
||||||
@@ -32,7 +29,6 @@ export interface AuthBackgroundProps {
|
|||||||
|
|
||||||
export const AuthBackground: React.FC<AuthBackgroundProps> = ({
|
export const AuthBackground: React.FC<AuthBackgroundProps> = ({
|
||||||
splashUrl,
|
splashUrl,
|
||||||
splashLoaded,
|
|
||||||
splashDimensions,
|
splashDimensions,
|
||||||
splashScale,
|
splashScale,
|
||||||
patternReady,
|
patternReady,
|
||||||
@@ -47,13 +43,7 @@ export const AuthBackground: React.FC<AuthBackgroundProps> = ({
|
|||||||
if (useFullCover) {
|
if (useFullCover) {
|
||||||
return (
|
return (
|
||||||
<div className={className} data-flx="auth.flow.auth-background.div">
|
<div className={className} data-flx="auth.flow.auth-background.div">
|
||||||
<motion.div
|
<div style={{position: 'absolute', inset: 0}} data-flx="auth.flow.auth-background.div--2">
|
||||||
initial={{opacity: 0}}
|
|
||||||
animate={{opacity: splashLoaded ? 1 : 0}}
|
|
||||||
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.5, ease: 'easeInOut'}}
|
|
||||||
style={{position: 'absolute', inset: 0}}
|
|
||||||
data-flx="auth.flow.auth-background.div--2"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
src={splashUrl}
|
src={splashUrl}
|
||||||
alt=""
|
alt=""
|
||||||
@@ -68,17 +58,14 @@ export const AuthBackground: React.FC<AuthBackgroundProps> = ({
|
|||||||
data-flx="auth.flow.auth-background.img"
|
data-flx="auth.flow.auth-background.img"
|
||||||
/>
|
/>
|
||||||
<div className={styles.splashOverlay} data-flx="auth.flow.auth-background.splash-overlay" />
|
<div className={styles.splashOverlay} data-flx="auth.flow.auth-background.splash-overlay" />
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.rightSplit} data-flx="auth.flow.auth-background.right-split">
|
<div className={styles.rightSplit} data-flx="auth.flow.auth-background.right-split">
|
||||||
<motion.div
|
<div
|
||||||
className={styles.splashImage}
|
className={styles.splashImage}
|
||||||
initial={{opacity: 0}}
|
|
||||||
animate={{opacity: splashLoaded ? 1 : 0}}
|
|
||||||
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.5, ease: 'easeInOut'}}
|
|
||||||
style={{
|
style={{
|
||||||
width: splashDimensions.width,
|
width: splashDimensions.width,
|
||||||
height: splashDimensions.height,
|
height: splashDimensions.height,
|
||||||
@@ -104,7 +91,7 @@ export const AuthBackground: React.FC<AuthBackgroundProps> = ({
|
|||||||
data-flx="auth.flow.auth-background.img--2"
|
data-flx="auth.flow.auth-background.img--2"
|
||||||
/>
|
/>
|
||||||
<div className={styles.splashOverlay} data-flx="auth.flow.auth-background.splash-overlay--2" />
|
<div className={styles.splashOverlay} data-flx="auth.flow.auth-background.splash-overlay--2" />
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import * as ImageCacheUtils from '@app/features/messaging/utils/ImageCacheUtils';
|
||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
interface ImageDimensions {
|
interface ImageDimensions {
|
||||||
@@ -12,62 +13,53 @@ interface PatternImageLoaderResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SplashImageLoaderResult {
|
interface SplashImageLoaderResult {
|
||||||
loaded: boolean;
|
|
||||||
dimensions: ImageDimensions | null;
|
dimensions: ImageDimensions | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthBackgroundResult {
|
interface AuthBackgroundResult {
|
||||||
patternReady: boolean;
|
patternReady: boolean;
|
||||||
splashLoaded: boolean;
|
|
||||||
splashDimensions: ImageDimensions | null;
|
splashDimensions: ImageDimensions | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePatternImageLoader(patternUrl: string): PatternImageLoaderResult {
|
export function usePatternImageLoader(patternUrl: string): PatternImageLoaderResult {
|
||||||
const [patternReady, setPatternReady] = useState(false);
|
const [patternReady, setPatternReady] = useState(() => ImageCacheUtils.hasImage(patternUrl));
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const img = new Image();
|
if (ImageCacheUtils.hasImage(patternUrl)) {
|
||||||
const handleLoad = () => setPatternReady(true);
|
setPatternReady(true);
|
||||||
img.addEventListener('load', handleLoad, {once: true});
|
return;
|
||||||
img.src = patternUrl;
|
}
|
||||||
return () => img.removeEventListener('load', handleLoad);
|
setPatternReady(false);
|
||||||
|
return ImageCacheUtils.loadImage(patternUrl, () => setPatternReady(true));
|
||||||
}, [patternUrl]);
|
}, [patternUrl]);
|
||||||
return {patternReady};
|
return {patternReady};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSplashImageLoader(imageUrl: string | null): SplashImageLoaderResult {
|
export function useSplashImageLoader(imageUrl: string | null): SplashImageLoaderResult {
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [dimensions, setDimensions] = useState<ImageDimensions | null>(
|
||||||
const [dimensions, setDimensions] = useState<ImageDimensions | null>(null);
|
() => ImageCacheUtils.getImageSize(imageUrl) ?? null,
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!imageUrl) {
|
const cached = ImageCacheUtils.getImageSize(imageUrl);
|
||||||
setLoaded(false);
|
if (cached) {
|
||||||
setDimensions(null);
|
setDimensions(cached);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let isMounted = true;
|
setDimensions(null);
|
||||||
const img = new Image();
|
if (!imageUrl) {
|
||||||
const handleLoad = () => {
|
return;
|
||||||
if (!isMounted) return;
|
}
|
||||||
setLoaded(true);
|
return ImageCacheUtils.loadImage(imageUrl, () => {
|
||||||
setDimensions({
|
setDimensions(ImageCacheUtils.getImageSize(imageUrl) ?? null);
|
||||||
width: img.naturalWidth,
|
|
||||||
height: img.naturalHeight,
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
img.addEventListener('load', handleLoad, {once: true});
|
|
||||||
img.src = imageUrl;
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, [imageUrl]);
|
}, [imageUrl]);
|
||||||
return {loaded, dimensions};
|
return {dimensions};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthBackground(splashUrl: string | null, patternUrl: string): AuthBackgroundResult {
|
export function useAuthBackground(splashUrl: string | null, patternUrl: string): AuthBackgroundResult {
|
||||||
const {patternReady} = usePatternImageLoader(patternUrl);
|
const {patternReady} = usePatternImageLoader(patternUrl);
|
||||||
const {loaded: splashLoaded, dimensions: splashDimensions} = useSplashImageLoader(splashUrl);
|
const {dimensions: splashDimensions} = useSplashImageLoader(splashUrl);
|
||||||
return {
|
return {
|
||||||
patternReady,
|
patternReady,
|
||||||
splashLoaded,
|
|
||||||
splashDimensions,
|
splashDimensions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ export const InvitePagePreviewModal: React.FC<InvitePagePreviewModalProps> = obs
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [previewSplashUrl, guild?.id, guild?.splash]);
|
}, [previewSplashUrl, guild?.id, guild?.splash]);
|
||||||
const {patternReady, splashLoaded, splashDimensions} = useAuthBackground(splashUrl, foodPatternUrl);
|
const {patternReady, splashDimensions} = useAuthBackground(splashUrl, foodPatternUrl);
|
||||||
const shouldShowSplash = Boolean(splashUrl && splashDimensions);
|
const shouldShowSplash = Boolean(splashUrl && splashDimensions);
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
ModalCommands.pop();
|
ModalCommands.pop();
|
||||||
@@ -122,7 +122,6 @@ export const InvitePagePreviewModal: React.FC<InvitePagePreviewModalProps> = obs
|
|||||||
<AuthBackground
|
<AuthBackground
|
||||||
className={clsx(styles.background, !shouldShowSplash && authLayoutStyles.patternHost)}
|
className={clsx(styles.background, !shouldShowSplash && authLayoutStyles.patternHost)}
|
||||||
splashUrl={splashUrl}
|
splashUrl={splashUrl}
|
||||||
splashLoaded={splashLoaded}
|
|
||||||
splashDimensions={splashDimensions}
|
splashDimensions={splashDimensions}
|
||||||
patternReady={patternReady}
|
patternReady={patternReady}
|
||||||
patternImageUrl={foodPatternUrl}
|
patternImageUrl={foodPatternUrl}
|
||||||
|
|||||||
Reference in new issue
Block a user