[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
This commit is contained in:
Bruno Lemos
2019-01-18 06:09:28 -02:00
parent 57576ea45a
commit 2cf84ed348
19 changed files with 143 additions and 79 deletions

View File

@@ -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<typeof getStyles>
>(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={[

View File

@@ -61,6 +61,7 @@ export const Column = React.memo((props: ColumnProps) => {
{!!showFocusBorder && (
<SpringAnimatedView
collapsable={false}
pointerEvents="box-none"
style={[
{

View File

@@ -54,6 +54,7 @@ export const ColumnOptionsRenderer = React.memo(
<>
{!!overlayTransition && (
<SpringAnimatedView
collapsable={false}
style={{
...StyleSheet.absoluteFillObject,
...overlayTransition.props,
@@ -76,6 +77,7 @@ export const ColumnOptionsRenderer = React.memo(
)}
<View
collapsable={false}
style={{
position: 'absolute',
top: 0,

View File

@@ -63,7 +63,11 @@ export function ColumnOptionsRow(props: ColumnOptionsRowProps) {
updateStyles()
})
const cacheRef = useRef({ isHovered: initialIsHovered, theme: initialTheme })
const cacheRef = useRef({
isHovered: initialIsHovered,
isPressing: false,
theme: initialTheme,
})
const [springAnimatedStyles, setSpringAnimatedStyles] = useSpring(getStyles)
@@ -75,13 +79,13 @@ export function ColumnOptionsRow(props: ColumnOptionsRowProps) {
)
function getStyles() {
const { isHovered, theme } = cacheRef.current
const { isHovered, isPressing, theme } = cacheRef.current
return {
config: { duration: 100 },
native: true,
backgroundColor:
isHovered || opened
isHovered || isPressing || opened
? theme.backgroundColorLess16
: theme.backgroundColorLess08,
}
@@ -111,13 +115,13 @@ export function ColumnOptionsRow(props: ColumnOptionsRowProps) {
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()
}}
>

View File

@@ -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 && (
<>
<SafeAreaView
collapsable={false}
style={[StyleSheet.absoluteFill, { flexDirection: 'row' }]}
pointerEvents="box-none"
>
<View
ref={leftOverlayRef}
collapsable={false}
style={{ flex: 1 }}
pointerEvents="box-none"
>
@@ -235,6 +233,7 @@ export const Columns = React.memo((props: ColumnsProps) => {
<View
ref={rightOverlayRef}
collapsable={false}
style={{ flex: 1 }}
pointerEvents="box-none"
>

View File

@@ -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)

View File

@@ -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={[

View File

@@ -10,7 +10,7 @@ export function CenterGuide(props: CenterGuideProps) {
const { color = 'red', size = 1 } = props
return (
<View style={StyleSheet.absoluteFill}>
<View collapsable={false} style={StyleSheet.absoluteFill}>
<View
style={{
position: 'absolute',

View File

@@ -126,7 +126,10 @@ export function SpringAnimatedCheckbox(props: SpringAnimatedCheckboxProps) {
},
]}
>
<View style={[StyleSheet.absoluteFill, styles.center, { zIndex: 1 }]}>
<View
collapsable={false}
style={[StyleSheet.absoluteFill, styles.center, { zIndex: 1 }]}
>
<SpringAnimatedView
style={{
width: isIndeterminateState ? '80%' : '100%',
@@ -140,7 +143,10 @@ export function SpringAnimatedCheckbox(props: SpringAnimatedCheckboxProps) {
/>
</View>
<View style={[StyleSheet.absoluteFill, styles.center, { zIndex: 2 }]}>
<View
collapsable={false}
style={[StyleSheet.absoluteFill, styles.center, { zIndex: 2 }]}
>
<SpringAnimatedIcon
color={checkedForegroundColor}
name="check"

View File

@@ -71,6 +71,7 @@ export function FAB(props: FABProps) {
shadowOpacity: 0.2,
shadowRadius: 6,
zIndex: 1,
overflow: 'hidden',
},
style,
]}
@@ -84,6 +85,7 @@ export function FAB(props: FABProps) {
width: fabSize,
height: fabSize,
borderRadius: fabSize / 2,
overflow: 'hidden',
},
!!(isHovered || isPressing) && {
backgroundColor: useBrandColor

View File

@@ -86,6 +86,7 @@ class GradientLayerOverlay extends React.Component<GradientLayerOverlayProps> {
return (
<SpringAnimatedLinearGradient
collapsable={false}
colors={[rgba(color, 0), color]}
pointerEvents="box-none"
style={[

View File

@@ -69,6 +69,7 @@ class GradientLayerOverlay extends React.Component<GradientLayerOverlayProps> {
return (
<SpringAnimatedView
collapsable={false}
pointerEvents="box-none"
style={[
getStyle(color, to, size, spacing),

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { Image, ImageProps } from 'react-native'
import { Platform } from '../../libs/platform'
import { SpringAnimatedImage } from '../animated/spring/SpringAnimatedImage'
export interface ImageWithLoadingProps extends ImageProps {
@@ -25,17 +26,22 @@ export const ImageWithLoading = React.memo((props: ImageWithLoadingProps) => {
onLoad,
onLoadEnd,
onLoadStart,
style,
...otherProps
} = props
const [error, setError] = useState(false)
const [loading, setLoading] = useState(false)
const imageRef = useRef<Image>(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 (
<ImageComponent
{...otherProps}
ref={imageRef}
onError={handleError}
onLoad={handleLoad}
onLoadEnd={handleLoadEnd}
onLoadStart={handleLoadStart}
style={[
style,
{
backgroundColor: error
? backgroundColorFailed
: loading
? backgroundColorLoading
: backgroundColorLoaded,
},
]}
/>
)
})

View File

@@ -130,19 +130,15 @@ export const ScrollViewWithOverlay = React.forwardRef(
return (
<View
style={[
{
position: 'relative',
flex: 1,
},
containerStyle,
]}
collapsable={false}
style={[{ position: 'relative', flex: 1 }, containerStyle]}
>
<ScrollViewComponent
ref={ref}
horizontal={horizontal}
scrollEventThrottle={3}
{...restProps}
collapsable={false}
onContentSizeChange={onContentSizeChange}
onLayout={onLayout}
onScroll={onScroll}

View File

@@ -29,6 +29,7 @@ export const AnimatedTransparentTextOverlay = React.memo(
return (
<SpringAnimatedView
ref={ref}
collapsable={false}
pointerEvents="box-none"
style={[
{ flex: 1, alignSelf: 'stretch', flexBasis: 'auto' },

View File

@@ -57,7 +57,7 @@ export function FABRenderer() {
const iconStyle = undefined
return (
<View style={fabPositionStyle}>
<View collapsable={false} style={fabPositionStyle}>
<FAB
key="fab"
analyticsLabel="add_column"

View File

@@ -17,6 +17,7 @@ import { SpringAnimatedText } from '../animated/spring/SpringAnimatedText'
import { SpringAnimatedTouchableOpacity } from '../animated/spring/SpringAnimatedTouchableOpacity'
import { ColumnHeaderItem } from '../columns/ColumnHeaderItem'
import { ModalColumn } from '../columns/ModalColumn'
import { separatorSize } from '../common/Separator'
import { useColumnWidth } from '../context/ColumnWidthContext'
import { useTheme } from '../context/ThemeContext'
@@ -92,12 +93,16 @@ function AddColumnModalItem({
})
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(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

View File

@@ -125,6 +125,7 @@ export function ModalRenderer(props: ModalRendererProps) {
<>
{!!overlayTransition && (
<SpringAnimatedView
collapsable={false}
style={{
...StyleSheet.absoluteFillObject,
...overlayTransition.props,
@@ -148,6 +149,7 @@ export function ModalRenderer(props: ModalRendererProps) {
{!!modalTransitions.length && (
<SpringAnimatedView
collapsable={false}
style={{
position: 'absolute',
top: 0,
@@ -162,6 +164,7 @@ export function ModalRenderer(props: ModalRendererProps) {
({ key, item, props: { width, ...animatedStyle } }) => (
<SpringAnimatedView
key={key}
collapsable={false}
style={{
position: 'absolute',
top: 0,
@@ -176,6 +179,7 @@ export function ModalRenderer(props: ModalRendererProps) {
{!!renderSeparator && (
<View
collapsable={false}
style={{
position: 'absolute',
top: 0,

View File

@@ -10,17 +10,16 @@ patch-package
// CSS filters
var filters = [];
@@ -307,6 +309,9 @@ var Image = function (_Component) {
@@ -307,6 +309,8 @@ var Image = function (_Component) {
// these styles are not supported on View
delete flatStyle.overlayColor;
delete flatStyle.resizeMode;
+ // these styles will be moved to the inner View (#1228)
+ delete flatStyle.backgroundColor;
+ delete flatStyle.borderRadius;
// Accessibility image allows users to trigger the browser's image context menu
var hiddenImage = displayImageUri ? createElement('img', {
@@ -327,7 +332,7 @@ var Image = function (_Component) {
@@ -327,7 +331,7 @@ var Image = function (_Component) {
testID: testID
}),
React.createElement(View, {