From 2cf84ed348e5ffa2ceb4e710f9154757cb1f5ab5 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Fri, 18 Jan 2019 06:09:28 -0200 Subject: [PATCH] [Android] Fix bugs that appeared after react-spring migration So many random things stopped working. They were mainly caused by a rerender happening in a child view while some react-spring animation was happening. Also I could not scroll on some places anymore, so I had to add collapsable={false} to some views as seem here: https://github.com/facebook/react-native/issues/21026#issuecomment-442785268 [Web] Workaround for white edge bug on images https://github.com/necolas/react-native-web/issues/1228 --- .../components/buttons/GitHubLoginButton.tsx | 21 +++--- .../src/components/columns/Column.tsx | 1 + .../columns/ColumnOptionsRenderer.tsx | 2 + .../components/columns/ColumnOptionsRow.tsx | 14 ++-- .../src/components/columns/Columns.tsx | 9 ++- .../columns/EventOrNotificationColumn.tsx | 25 ++++--- .../src/components/common/Button.tsx | 20 +++--- .../src/components/common/CenterGuide.tsx | 2 +- .../src/components/common/Checkbox.tsx | 10 ++- .../components/src/components/common/FAB.tsx | 2 + .../common/GradientLayerOverlay.tsx | 1 + .../common/GradientLayerOverlay.web.tsx | 1 + .../components/common/ImageWithLoading.tsx | 67 +++++++++++++------ .../common/ScrollViewWithOverlay.tsx | 10 +-- .../common/TransparentTextOverlay.tsx | 1 + .../src/components/layout/FABRenderer.tsx | 2 +- .../src/components/modals/AddColumnModal.tsx | 25 ++++--- .../src/components/modals/ModalRenderer.tsx | 4 ++ patches/react-native-web+0.9.9.patch | 5 +- 19 files changed, 143 insertions(+), 79 deletions(-) diff --git a/packages/components/src/components/buttons/GitHubLoginButton.tsx b/packages/components/src/components/buttons/GitHubLoginButton.tsx index 11a0abce..87327ca5 100644 --- a/packages/components/src/components/buttons/GitHubLoginButton.tsx +++ b/packages/components/src/components/buttons/GitHubLoginButton.tsx @@ -95,26 +95,31 @@ export function GitHubLoginButton(props: GitHubLoginButtonProps) { }) const touchableRef = useRef(null) - useHover(touchableRef, isHovered => { + const initialIsHovered = useHover(touchableRef, isHovered => { cacheRef.current.isHovered = isHovered updateStyles() }) - const cacheRef = useRef({ isHovered: false, theme: initialTheme }) + const cacheRef = useRef({ + isHovered: initialIsHovered, + isPressing: false, + theme: initialTheme, + }) const [springAnimatedStyles, setSpringAnimatedStyles] = useSpring< ReturnType >(getStyles) function getStyles() { - const { isHovered, theme } = cacheRef.current + const { isHovered, isPressing, theme } = cacheRef.current return { config: { duration: 100 }, native: true, - backgroundColor: isHovered - ? theme.backgroundColorLess16 - : rgba(theme.backgroundColorLess16, 0), + backgroundColor: + isHovered || isPressing + ? theme.backgroundColorLess16 + : rgba(theme.backgroundColorLess16, 0), } } @@ -130,13 +135,13 @@ export function GitHubLoginButton(props: GitHubLoginButtonProps) { onPressIn={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = true + cacheRef.current.isPressing = true updateStyles() }} onPressOut={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = false + cacheRef.current.isPressing = false updateStyles() }} style={[ diff --git a/packages/components/src/components/columns/Column.tsx b/packages/components/src/components/columns/Column.tsx index 6412d06d..4a9cfbd4 100644 --- a/packages/components/src/components/columns/Column.tsx +++ b/packages/components/src/components/columns/Column.tsx @@ -61,6 +61,7 @@ export const Column = React.memo((props: ColumnProps) => { {!!showFocusBorder && ( {!!overlayTransition && ( { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = true + cacheRef.current.isPressing = true updateStyles() }} onPressOut={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = false + cacheRef.current.isPressing = false updateStyles() }} > diff --git a/packages/components/src/components/columns/Columns.tsx b/packages/components/src/components/columns/Columns.tsx index 5ea363f7..ce1ffc3b 100644 --- a/packages/components/src/components/columns/Columns.tsx +++ b/packages/components/src/components/columns/Columns.tsx @@ -16,11 +16,7 @@ import { ColumnContainer } from '../../containers/ColumnContainer' import { useEmitter } from '../../hooks/use-emitter' import { bugsnag } from '../../libs/bugsnag' import { columnHeaderHeight, contentPadding } from '../../styles/variables' -import { - Separator, - separatorSize, - separatorTickSize, -} from '../common/Separator' +import { Separator, separatorTickSize } from '../common/Separator' import { AnimatedTransparentTextOverlay } from '../common/TransparentTextOverlay' import { useColumnWidth } from '../context/ColumnWidthContext' import { useAppLayout } from '../context/LayoutContext' @@ -198,11 +194,13 @@ export const Columns = React.memo((props: ColumnsProps) => { {showHorizontalGradientOverlays && ( <> @@ -235,6 +233,7 @@ export const Columns = React.memo((props: ColumnsProps) => { diff --git a/packages/components/src/components/columns/EventOrNotificationColumn.tsx b/packages/components/src/components/columns/EventOrNotificationColumn.tsx index d0136aec..edc7d6f6 100644 --- a/packages/components/src/components/columns/EventOrNotificationColumn.tsx +++ b/packages/components/src/components/columns/EventOrNotificationColumn.tsx @@ -65,23 +65,30 @@ export const EventOrNotificationColumn = React.memo( // when this one opens, I "lock" it, // so the nested ones doesnt trigger an animation on this one. + if (!accordionRef.current) return + if (showColumnOptions) { - if (accordionRef.current!.isLocked()) { - accordionRef.current!.setOnFinishListener(() => { - accordionRef.current!.setOnFinishListener(null) + if (accordionRef.current.isLocked()) { + accordionRef.current.setOnFinishListener(() => { + if (accordionRef.current) { + accordionRef.current.setOnFinishListener(null) + } + setShowColumnOptions(false) }) - accordionRef.current!.unlock() + accordionRef.current.unlock() } else { - accordionRef.current!.setOnFinishListener(null) - accordionRef.current!.unlock() + accordionRef.current.setOnFinishListener(null) + accordionRef.current.unlock() setShowColumnOptions(false) } } else { - accordionRef.current!.setOnFinishListener(() => { - accordionRef.current!.setOnFinishListener(null) - accordionRef.current!.lock() + accordionRef.current.setOnFinishListener(() => { + if (accordionRef.current) { + accordionRef.current.setOnFinishListener(null) + accordionRef.current.lock() + } }) setShowColumnOptions(true) diff --git a/packages/components/src/components/common/Button.tsx b/packages/components/src/components/common/Button.tsx index 4db80b20..f567bee2 100644 --- a/packages/components/src/components/common/Button.tsx +++ b/packages/components/src/components/common/Button.tsx @@ -44,12 +44,16 @@ export const Button = React.memo((props: ButtonProps) => { }) const touchableRef = useRef(null) - useHover(touchableRef, isHovered => { + const initialIsHovered = useHover(touchableRef, isHovered => { cacheRef.current.isHovered = isHovered updateStyles() }) - const cacheRef = useRef({ isHovered: false, theme: initialTheme }) + const cacheRef = useRef({ + isHovered: initialIsHovered, + isPressing: false, + theme: initialTheme, + }) const springAnimatedTheme = useCSSVariablesOrSpringAnimatedTheme() @@ -58,7 +62,7 @@ export const Button = React.memo((props: ButtonProps) => { >(getStyles) function getStyles() { - const { isHovered, theme } = cacheRef.current + const { isHovered, isPressing, theme } = cacheRef.current return { config: { duration: 100 }, @@ -66,12 +70,12 @@ export const Button = React.memo((props: ButtonProps) => { activityIndicatorColor: theme.foregroundColor, touchableBorderColor: useBrandColor ? colors.brandBackgroundColor - : isHovered + : isHovered || isPressing ? theme.backgroundColorLess16 : theme.backgroundColorLess08, innerContainerBackgroundColor: borderOnly ? rgba(theme.backgroundColorLess08, 0) - : isHovered + : isHovered || isPressing ? useBrandColor ? theme.backgroundColorTransparent10 : theme.backgroundColorLess16 @@ -79,7 +83,7 @@ export const Button = React.memo((props: ButtonProps) => { textColor: useBrandColor ? colors.brandForegroundColor : borderOnly - ? isHovered + ? isHovered || isPressing ? theme.foregroundColor : theme.foregroundColorMuted50 : theme.foregroundColor, @@ -97,13 +101,13 @@ export const Button = React.memo((props: ButtonProps) => { onPressIn={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = true + cacheRef.current.isPressing = true updateStyles() }} onPressOut={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = false + cacheRef.current.isPressing = false updateStyles() }} style={[ diff --git a/packages/components/src/components/common/CenterGuide.tsx b/packages/components/src/components/common/CenterGuide.tsx index ba593b4f..4569c4eb 100644 --- a/packages/components/src/components/common/CenterGuide.tsx +++ b/packages/components/src/components/common/CenterGuide.tsx @@ -10,7 +10,7 @@ export function CenterGuide(props: CenterGuideProps) { const { color = 'red', size = 1 } = props return ( - + - + - + { return ( { return ( { onLoad, onLoadEnd, onLoadStart, - style, ...otherProps } = props - const [error, setError] = useState(false) - const [loading, setLoading] = useState(false) + const imageRef = useRef(null) + const cacheRef = useRef({ error: false, isLoading: false }) + + useEffect(() => { + updateStyles() + }, []) const handleLoad = useCallback( e => { - setLoading(false) - setError(false) + cacheRef.current.isLoading = false + cacheRef.current.error = false + updateStyles() + if (typeof onLoad === 'function') onLoad(e) }, [onLoad], @@ -43,7 +49,9 @@ export const ImageWithLoading = React.memo((props: ImageWithLoadingProps) => { const handleLoadStart = useCallback( () => { - setLoading(true) + cacheRef.current.isLoading = true + updateStyles() + if (typeof onLoadStart === 'function') onLoadStart() }, [onLoadStart], @@ -51,7 +59,9 @@ export const ImageWithLoading = React.memo((props: ImageWithLoadingProps) => { const handleLoadEnd = useCallback( () => { - setLoading(false) + cacheRef.current.isLoading = false + updateStyles() + if (typeof onLoadEnd === 'function') onLoadEnd() }, [onLoadEnd], @@ -59,32 +69,49 @@ export const ImageWithLoading = React.memo((props: ImageWithLoadingProps) => { const handleError = useCallback( e => { - setLoading(false) - setError(true) + cacheRef.current.isLoading = false + cacheRef.current.error = true + updateStyles() + if (typeof onError === 'function') onError(e) }, [onError], ) + function updateStyles() { + const { error, isLoading } = cacheRef.current + + if (imageRef.current) { + const imageURL = + otherProps && otherProps.source && (otherProps.source as any).uri + + imageRef.current.setNativeProps({ + style: { + backgroundColor: error + ? backgroundColorFailed + : isLoading + ? backgroundColorLoading + : backgroundColorLoaded, + ...(Platform.OS === 'web' && + !!imageURL && { + backgroundImage: `url(${JSON.stringify(imageURL)})`, + backgroundSize: 'cover', + }), + }, + }) + } + } + const ImageComponent = animated ? SpringAnimatedImage : Image return ( ) }) diff --git a/packages/components/src/components/common/ScrollViewWithOverlay.tsx b/packages/components/src/components/common/ScrollViewWithOverlay.tsx index 54702f9e..8a7044b2 100644 --- a/packages/components/src/components/common/ScrollViewWithOverlay.tsx +++ b/packages/components/src/components/common/ScrollViewWithOverlay.tsx @@ -130,19 +130,15 @@ export const ScrollViewWithOverlay = React.forwardRef( return ( + { + const initialIsHovered = useHover(touchableRef, isHovered => { cacheRef.current.isHovered = isHovered updateStyles() }) - const cacheRef = useRef({ isHovered: false, theme: initialTheme }) + const cacheRef = useRef({ + isHovered: initialIsHovered, + isPressing: false, + theme: initialTheme, + }) const [springAnimatedStyles, setSpringAnimatedStyles] = useSpring(getStyles) @@ -113,15 +118,15 @@ function AddColumnModalItem({ const pushModal = useReduxAction(actions.pushModal) function getStyles() { - const { isHovered: _isHovered, theme } = cacheRef.current - const isHovered = _isHovered && !disabled + const { isHovered, isPressing, theme } = cacheRef.current return { config: { duration: 100 }, native: true, - backgroundColor: isHovered - ? theme.backgroundColorLess08 - : rgba(theme.backgroundColor, 0), + backgroundColor: + (isHovered || isPressing) && !disabled + ? theme.backgroundColorLess08 + : rgba(theme.backgroundColor, 0), } } @@ -145,13 +150,13 @@ function AddColumnModalItem({ onPressIn={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = true + cacheRef.current.isPressing = true updateStyles() }} onPressOut={() => { if (Platform.realOS === 'web') return - cacheRef.current.isHovered = false + cacheRef.current.isPressing = false updateStyles() }} style={{ @@ -198,7 +203,7 @@ export function AddColumnModal(props: AddColumnModalProps) { const columnWidth = useColumnWidth() const outerSpacing = (3 / 4) * contentPadding - const availableWidth = columnWidth - 2 * outerSpacing + const availableWidth = columnWidth - separatorSize - 2 * outerSpacing const hasReachedColumnLimit = columnIds.length >= constants.COLUMNS_LIMIT diff --git a/packages/components/src/components/modals/ModalRenderer.tsx b/packages/components/src/components/modals/ModalRenderer.tsx index dad39fbe..2a03aa59 100644 --- a/packages/components/src/components/modals/ModalRenderer.tsx +++ b/packages/components/src/components/modals/ModalRenderer.tsx @@ -125,6 +125,7 @@ export function ModalRenderer(props: ModalRendererProps) { <> {!!overlayTransition && ( (