Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(celo-news): skeleton placeholder while loading #3274

Merged
merged 6 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ PODS:
- Adjust/Core (4.29.7)
- Analytics (4.1.6)
- boost (1.76.0)
- BVLinearGradient (2.6.2):
- React-Core
- CleverTap-iOS-SDK (4.0.0):
- SDWebImage (~> 5.1)
- clevertap-react-native (0.8.1):
Expand Down Expand Up @@ -676,6 +678,7 @@ PODS:

DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- clevertap-react-native (from `../node_modules/clevertap-react-native`)
- CTNotificationService
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
Expand Down Expand Up @@ -832,6 +835,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
BVLinearGradient:
:path: "../node_modules/react-native-linear-gradient"
clevertap-react-native:
:path: "../node_modules/clevertap-react-native"
DoubleConversion:
Expand Down Expand Up @@ -997,6 +1002,7 @@ SPEC CHECKSUMS:
Adjust: 91a06a01e4bb35b432e26b5d5bb8995b95fc381c
Analytics: eefe524436f904b8bb3f8c8c3425280e43b34efc
boost: a7c83b31436843459a1961bfd74b96033dc77234
BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44
CleverTap-iOS-SDK: fa4737b7cede85e55d49354ce79ef8d08762ddca
clevertap-react-native: 97ccd6512b94fe034c126340b8eb677f28e5baa1
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
Expand Down
2 changes: 2 additions & 0 deletions ios/celo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@
"${PODS_ROOT}/Target Support Files/Pods-celo/Pods-celo-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Adjust/Adjust.framework",
"${BUILT_PRODUCTS_DIR}/Analytics/Segment.framework",
"${BUILT_PRODUCTS_DIR}/BVLinearGradient/BVLinearGradient.framework",
"${BUILT_PRODUCTS_DIR}/CleverTap-iOS-SDK/CleverTapSDK.framework",
"${BUILT_PRODUCTS_DIR}/CocoaAsyncSocket/CocoaAsyncSocket.framework",
"${BUILT_PRODUCTS_DIR}/DoubleConversion/DoubleConversion.framework",
Expand Down Expand Up @@ -715,6 +716,7 @@
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Adjust.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Segment.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BVLinearGradient.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CleverTapSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaAsyncSocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DoubleConversion.framework",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"react-native-image-crop-picker": "^0.35.1",
"react-native-keep-awake": "^4.0.0",
"react-native-keychain": "8.0.0",
"react-native-linear-gradient": "^2.6.2",
"react-native-localize": "^2.1.7",
"react-native-mail": "^6.1.1",
"react-native-modal": "^13.0.1",
Expand All @@ -169,6 +170,7 @@
"react-native-shake": "~3.5.0",
"react-native-share": "^7.3.2",
"react-native-simple-toast": "^1.1.3",
"react-native-skeleton-placeholder": "^5.2.4",
"react-native-sms-retriever": "^1.1.1",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^12.3.0",
Expand Down
39 changes: 39 additions & 0 deletions patches/react-native-skeleton-placeholder+5.2.4.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
diff --git a/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.d.ts b/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.d.ts
index a3dc594..3a58d21 100644
--- a/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.d.ts
+++ b/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.d.ts
@@ -34,6 +34,8 @@ declare type SkeletonPlaceholderProps = {
* Determines width of the highlighted area
*/
shimmerWidth?: number;
+ // TODO: remove this patch once a release is published with https://github.com/chramos/react-native-skeleton-placeholder/pull/71
+ testID?: string;
};
declare type SkeletonPlaceholderItemProps = ViewStyle & {
style?: StyleProp<ViewStyle>;
diff --git a/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.js b/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.js
index 353abb6..5ff4824 100644
--- a/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.js
+++ b/node_modules/react-native-skeleton-placeholder/lib/skeleton-placeholder.js
@@ -39,7 +39,7 @@ const react_native_1 = require("react-native");
const react_native_linear_gradient_1 = __importDefault(require("react-native-linear-gradient"));
const WINDOW_WIDTH = react_native_1.Dimensions.get('window').width;
const logEnabled = false;
-const SkeletonPlaceholder = ({ children, enabled = true, backgroundColor = '#E1E9EE', highlightColor = '#F2F8FC', speed = 800, direction = 'right', borderRadius, shimmerWidth, }) => {
+const SkeletonPlaceholder = ({ children, enabled = true, backgroundColor = '#E1E9EE', highlightColor = '#F2F8FC', speed = 800, direction = 'right', borderRadius, shimmerWidth, testID, }) => {
const [layout, setLayout] = React.useState();
const animatedValueRef = React.useRef(new react_native_1.Animated.Value(0));
const isAnimationReady = Boolean(speed && (layout === null || layout === void 0 ? void 0 : layout.width) && (layout === null || layout === void 0 ? void 0 : layout.height));
@@ -79,10 +79,10 @@ const SkeletonPlaceholder = ({ children, enabled = true, backgroundColor = '#E1E
if (!enabled || !placeholders)
return children;
if (!(layout === null || layout === void 0 ? void 0 : layout.width) || !layout.height)
- return <react_native_1.View onLayout={(event) => setLayout(event.nativeEvent.layout)}>{placeholders}</react_native_1.View>;
+ return <react_native_1.View onLayout={(event) => setLayout(event.nativeEvent.layout)} testID={testID}>{placeholders}</react_native_1.View>;
// https://github.com/react-native-linear-gradient/react-native-linear-gradient/issues/358
// to make transparent gradient we need to use original color with alpha
- return (<masked_view_1.default style={{ height: layout.height, width: layout.width }} maskElement={placeholders}>
+ return (<masked_view_1.default style={{ height: layout.height, width: layout.width }} maskElement={placeholders} testID={testID}>
<react_native_1.View style={[react_native_1.StyleSheet.absoluteFill, { backgroundColor }]}/>

{isAnimationReady && highlightColor !== undefined && transparentColor !== undefined && (<react_native_1.Animated.View style={animatedGradientStyle}>
17 changes: 17 additions & 0 deletions src/components/SkeletonPlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SkeletonPlaceholder from 'react-native-skeleton-placeholder'

// Just re-export for now
// We may want to customize this component in the future
// But at least this way we can use the same import.
//
// For instance one future improvement would be to synchronize the animation
// across all instances displayed at the same time.
// Right now if the components are not rendered at the same time, you'll see the animation
// be at a different progress for each one.
//
// Important: The children of the component are used as a mask and SkeletonPlaceholder
// tries to determine the width / height of the leaf nodes and use a solid background for them.
// So don't be surprised if it doesn't behave like standard React Native styles.
// You may need to provide explicit width / height props to the leaf nodes to get the desired effect.
// See https://github.com/chramos/react-native-skeleton-placeholder/blob/3c0ebcf3f99f9f0d0708c12f7f8e7fdc8bac843c/src/skeleton-placeholder.tsx#L166
export default SkeletonPlaceholder
12 changes: 6 additions & 6 deletions src/exchange/CeloNewsFeed.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ describe('CeloNewsFeed', () => {

// Check we can see the Celo news header
expect(tree.queryByText('celoNews.headerTitle')).toBeTruthy()
// Check we can see the loading spinner
expect(tree.queryByTestId('CeloNewsFeed/loading')).toBeTruthy()
// Check we can see the skeleton placeholder
expect(tree.queryAllByTestId('CeloNewsFeedItemSkeleton')[0]).toBeTruthy()
// Check we cannot see the error view
expect(tree.queryByText('celoNews.loadingError')).toBeFalsy()
// Check we cannot see the read more button
Expand All @@ -103,8 +103,8 @@ describe('CeloNewsFeed', () => {

// Check we can see a news item
expect(tree.queryByText('Announcing Kuneco Changes & New Celo Block Party')).toBeTruthy()
// Check we cannot see the loading spinner
expect(tree.queryByTestId('CeloNewsFeed/loading')).toBeFalsy()
// Check we cannot see the skeleton placeholder
expect(tree.queryByTestId('CeloNewsFeedItemSkeleton')).toBeFalsy()
// Check we cannot see the error view
expect(tree.queryByText('celoNews.loadingError')).toBeFalsy()

Expand Down Expand Up @@ -143,8 +143,8 @@ describe('CeloNewsFeed', () => {

// Check we cannot see the Celo news header
expect(tree.queryByText('celoNews.headerTitle')).toBeFalsy()
// Check we cannot see the loading spinner
expect(tree.queryByTestId('CeloNewsFeed/loading')).toBeFalsy()
// Check we cannot see the skeleton placeholder
expect(tree.queryByTestId('CeloNewsFeedItemSkeleton')).toBeFalsy()
// Check we can see the error view
expect(tree.queryByText('celoNews.loadingError')).toBeTruthy()
// Check we cannot see the read more button
Expand Down
30 changes: 9 additions & 21 deletions src/exchange/CeloNewsFeed.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import React, { useMemo } from 'react'
import { useAsync } from 'react-async-hook'
import { useTranslation } from 'react-i18next'
import {
ActivityIndicator,
FlatList,
ListRenderItemInfo,
StyleSheet,
Text,
View,
} from 'react-native'
import { FlatList, ListRenderItemInfo, StyleSheet, Text, View } from 'react-native'
import { CeloNewsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { celoNewsConfigSelector } from 'src/app/selectors'
Expand All @@ -28,6 +21,11 @@ import networkConfig from 'src/web3/networkConfig'

const TAG = 'exchange/CeloNewsFeed'

const LOADING_SKELETON_DATA = Array.from({ length: 5 }).map(
// This bypasses the type check, but it's fine because we're just using it for the skeleton
(_, i) => ({ id: i } as CeloNewsArticle)
)

function renderItem({ item: article }: ListRenderItemInfo<CeloNewsArticle>) {
return <CeloNewsFeedItem article={article} />
}
Expand Down Expand Up @@ -64,6 +62,7 @@ export default function CeloNewsFeed() {

const asyncArticles = useFetchArticles()
const { readMoreUrl } = useSelector(celoNewsConfigSelector)
const isLoading = asyncArticles.status === 'loading'

function onPressReadMore() {
const url = readMoreUrl
Expand All @@ -82,12 +81,6 @@ export default function CeloNewsFeed() {

const footer = useMemo(() => {
switch (asyncArticles.status) {
case 'loading':
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.goldUI} testID="CeloNewsFeed/loading" />
</View>
)
case 'success':
if (!readMoreUrl) {
return null
Expand Down Expand Up @@ -121,8 +114,8 @@ export default function CeloNewsFeed() {

return (
<FlatList
data={asyncArticles.result?.articles || []}
renderItem={renderItem}
data={isLoading ? LOADING_SKELETON_DATA : asyncArticles.result?.articles}
renderItem={isLoading ? CeloNewsFeedItem.Skeleton : renderItem}
keyExtractor={keyExtractor}
ItemSeparatorComponent={ItemSeparator}
ListHeaderComponent={
Expand Down Expand Up @@ -159,9 +152,4 @@ const styles = StyleSheet.create({
marginVertical: 32,
marginHorizontal: Spacing.Thick24,
},
loadingContainer: {
alignItems: 'center',
justifyContent: 'center',
minHeight: 200,
},
})
24 changes: 24 additions & 0 deletions src/exchange/CeloNewsFeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react'
import { Image, StyleSheet, Text, View } from 'react-native'
import { CeloNewsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import SkeletonPlaceholder from 'src/components/SkeletonPlaceholder'
import Touchable from 'src/components/Touchable'
import { CeloNewsArticle } from 'src/exchange/types'
import { navigate } from 'src/navigator/NavigationService'
Expand All @@ -25,6 +26,7 @@ export default function CeloNewsFeedItem({ article, testID }: Props) {
navigate(Screens.WebViewScreen, { uri: url })
}

// Be sure to update the skeleton component if this changes significantly
return (
<Touchable onPress={onPress} testID={testID}>
<View style={styles.contentContainer}>
Expand All @@ -51,6 +53,28 @@ export default function CeloNewsFeedItem({ article, testID }: Props) {
)
}

// This is a skeleton placeholder for when the feed is loading
// It's a simplified version of the real component, reusing styles where possible
CeloNewsFeedItem.Skeleton = () => (
<SkeletonPlaceholder
borderRadius={4}
backgroundColor={colors.gray2}
highlightColor={colors.white}
testID="CeloNewsFeedItemSkeleton"
>
<View style={styles.contentContainer}>
<View style={{ ...styles.author, width: 60 }} />
<View style={styles.row}>
<View style={{ flex: 1 }}>
<View style={{ ...styles.title, flex: undefined, marginBottom: 4 }} />
<View style={{ ...styles.title, flex: undefined, width: '60%' }} />
</View>
<View style={styles.image} />
</View>
</View>
Comment on lines +65 to +74
Copy link
Member Author

@jeanregisser jeanregisser Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I broke our rule of not using inline styles here because I thought it made more sense to have them directly there.
But let me know if you think I should really not do this.

</SkeletonPlaceholder>
)

const styles = StyleSheet.create({
contentContainer: {
marginHorizontal: Spacing.Thick24,
Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15624,6 +15624,11 @@ react-native-kill-packager@^1.0.0:
resolved "https://registry.yarnpkg.com/react-native-kill-packager/-/react-native-kill-packager-1.0.0.tgz#8d5dc706429805800298acabca45598d0d39b391"
integrity sha1-jV3HBkKYBYACmKyrykVZjQ05s5E=

react-native-linear-gradient@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.6.2.tgz#56598a76832724b2afa7889747635b5c80948f38"
integrity sha512-Z8Xxvupsex+9BBFoSYS87bilNPWcRfRsGC0cpJk72Nxb5p2nEkGSBv73xZbEHnW2mUFvP+huYxrVvjZkr/gRjQ==

react-native-localize@^2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/react-native-localize/-/react-native-localize-2.1.7.tgz#f1dda0c6fd58ab06b5b119cb4a507ab8800b9823"
Expand Down Expand Up @@ -15775,6 +15780,11 @@ react-native-simple-toast@^1.1.3:
resolved "https://registry.yarnpkg.com/react-native-simple-toast/-/react-native-simple-toast-1.1.3.tgz#4d0835891fe5c9342341fc4a88ed01ebb1a34bd5"
integrity sha512-bkZy25axqlU4L6IoTysSl5KOQA7qdxzivbXz/L/yAw9g9fENxjzYvUUtv5wWtgq7mzOe5HiJ7GCQrOl3MhIevQ==

react-native-skeleton-placeholder@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/react-native-skeleton-placeholder/-/react-native-skeleton-placeholder-5.2.4.tgz#a654fa083ff274a39c763e086f8a850c556d4c95"
integrity sha512-OZntVq1hU1UX33FltxK2ezT2v9vHIhV8YnEbnMWUCvxT0N9OsgD1qxiHm6qb9YRJVgq2o5z3S7dNPsPnDF/jNg==

react-native-sms-retriever@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/react-native-sms-retriever/-/react-native-sms-retriever-1.1.1.tgz#e87225836b394560c63db4c106552a0b9247d9b3"
Expand Down