mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-06-18 20:50:54 -04:00
Cache user icon to resolve flicker (#1255)
* Use FastImage to load & cache user icon * Fetch user icon before leaving login screen
This commit is contained in:
committed by
GitHub
parent
9588c577aa
commit
d79306ffee
@@ -17,6 +17,18 @@ PODS:
|
||||
- hermes-engine/Pre-built (= 0.71.16)
|
||||
- hermes-engine/Pre-built (0.71.16)
|
||||
- libevent (2.1.12)
|
||||
- libwebp (1.3.2):
|
||||
- libwebp/demux (= 1.3.2)
|
||||
- libwebp/mux (= 1.3.2)
|
||||
- libwebp/sharpyuv (= 1.3.2)
|
||||
- libwebp/webp (= 1.3.2)
|
||||
- libwebp/demux (1.3.2):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.3.2):
|
||||
- libwebp/demux
|
||||
- libwebp/sharpyuv (1.3.2)
|
||||
- libwebp/webp (1.3.2):
|
||||
- libwebp/sharpyuv
|
||||
- RCT-Folly (2021.07.22.00):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -409,6 +421,10 @@ PODS:
|
||||
- React-Core
|
||||
- RNDeviceInfo (10.12.0):
|
||||
- React-Core
|
||||
- RNFastImage (8.6.3):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.11.1)
|
||||
- SDWebImageWebPCoder (~> 0.8.4)
|
||||
- RNFlashList (1.6.3):
|
||||
- React-Core
|
||||
- RNFS (2.20.0):
|
||||
@@ -457,6 +473,12 @@ PODS:
|
||||
- React-Core
|
||||
- RNVectorIcons (9.2.0):
|
||||
- React-Core
|
||||
- SDWebImage (5.11.1):
|
||||
- SDWebImage/Core (= 5.11.1)
|
||||
- SDWebImage/Core (5.11.1)
|
||||
- SDWebImageWebPCoder (0.8.5):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.10)
|
||||
- VisionCamera (2.15.6):
|
||||
- React
|
||||
- React-callinvoker
|
||||
@@ -528,6 +550,7 @@ DEPENDENCIES:
|
||||
- "RNCPicker (from `../node_modules/@react-native-picker/picker`)"
|
||||
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
||||
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
|
||||
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
||||
- "RNFlashList (from `../node_modules/@shopify/flash-list`)"
|
||||
- RNFS (from `../node_modules/react-native-fs`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
@@ -547,6 +570,9 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- fmt
|
||||
- libevent
|
||||
- libwebp
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@@ -669,6 +695,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@react-native-community/datetimepicker"
|
||||
RNDeviceInfo:
|
||||
:path: "../node_modules/react-native-device-info"
|
||||
RNFastImage:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
RNFlashList:
|
||||
:path: "../node_modules/@shopify/flash-list"
|
||||
RNFS:
|
||||
@@ -708,6 +736,7 @@ SPEC CHECKSUMS:
|
||||
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
|
||||
hermes-engine: 2382506846564caf4152c45390dc24f08fce7057
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||
RCTRequired: 44a3cda52ccac0be738fbf43fef90f3546a48c52
|
||||
RCTTypeSafety: da7fbf9826fc898ca8b10dc840f2685562039a64
|
||||
@@ -761,6 +790,7 @@ SPEC CHECKSUMS:
|
||||
RNCPicker: 32ca102146bc7d34a8b93a998d9938d9f9ec7898
|
||||
RNDateTimePicker: ccd988deb223cbb2e669e157ec576c2c6217128c
|
||||
RNDeviceInfo: db5c64a060e66e5db3102d041ebe3ef307a85120
|
||||
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
||||
RNFlashList: 4b4b6b093afc0df60ae08f9cbf6ccd4c836c667a
|
||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||
RNGestureHandler: 6e4dc6b7ab3a385386d4e36228bd065e5a611394
|
||||
@@ -772,6 +802,8 @@ SPEC CHECKSUMS:
|
||||
RNStoreReview: 31dbfd0dac2eea9675f0b84f1dd3261c2110c337
|
||||
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
|
||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||
VisionCamera: 523b49054bee9dace64189ab6631cb41e8b83fe0
|
||||
VisionCameraPluginInatVision: 7e09a4ca0b34dd81afd4b68aa26a27eff5bb8fd4
|
||||
Yoga: e29645ec5a66fb00934fad85338742d1c247d4cb
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -60,6 +60,7 @@
|
||||
"react-native-event-listeners": "^1.0.7",
|
||||
"react-native-exception-handler": "^2.10.10",
|
||||
"react-native-exif-reader": "github:inaturalist/react-native-exif-reader",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-geocoder-reborn": "^0.9.0",
|
||||
"react-native-gesture-handler": "^2.13.0",
|
||||
@@ -22801,6 +22802,15 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-fast-image": {
|
||||
"version": "8.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz",
|
||||
"integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==",
|
||||
"peerDependencies": {
|
||||
"react": "^17 || ^18",
|
||||
"react-native": ">=0.60.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-fs": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz",
|
||||
@@ -42885,6 +42895,12 @@
|
||||
"from": "react-native-exif-reader@github:inaturalist/react-native-exif-reader",
|
||||
"requires": {}
|
||||
},
|
||||
"react-native-fast-image": {
|
||||
"version": "8.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz",
|
||||
"integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-native-fs": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz",
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
"react-native-event-listeners": "^1.0.7",
|
||||
"react-native-exception-handler": "^2.10.10",
|
||||
"react-native-exif-reader": "github:inaturalist/react-native-exif-reader",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-geocoder-reborn": "^0.9.0",
|
||||
"react-native-gesture-handler": "^2.13.0",
|
||||
|
||||
@@ -42,6 +42,7 @@ type Props = {
|
||||
const App = ( { children }: Props ): Node => {
|
||||
const realm = useRealm( );
|
||||
const currentUser = useCurrentUser( );
|
||||
|
||||
useIconicTaxa( { reload: true } );
|
||||
useReactQueryRefetch( );
|
||||
useFreshInstall( currentUser );
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import { getUserAgent } from "api/userAgent";
|
||||
import { fetchUserMe } from "api/users";
|
||||
import { create } from "apisauce";
|
||||
import axios from "axios";
|
||||
import i18next from "i18next";
|
||||
@@ -348,9 +349,23 @@ const authenticateUser = async (
|
||||
|
||||
// Save userId to local, encrypted storage
|
||||
const currentUser = { id: userId, login: remoteUsername, signedIn: true };
|
||||
logger.debug( "writing current user to realm: ", currentUser );
|
||||
|
||||
// try to fetch user data (especially for loading user icon) from userMe
|
||||
const apiToken = await getJWT( );
|
||||
const options = {
|
||||
api_token: apiToken
|
||||
};
|
||||
const remoteUser = await fetchUserMe( { }, options );
|
||||
const localUser = remoteUser
|
||||
? {
|
||||
...remoteUser,
|
||||
signedIn: true
|
||||
}
|
||||
: currentUser;
|
||||
|
||||
logger.debug( "writing current user to realm: ", localUser );
|
||||
safeRealmWrite( realm, ( ) => {
|
||||
realm.create( "User", currentUser, "modified" );
|
||||
realm.create( "User", localUser, "modified" );
|
||||
}, "saving current user in AuthenticationService" );
|
||||
const currentRealmUser = User.currentUser( realm );
|
||||
logger.debug( "Signed in", currentRealmUser.login, currentRealmUser.id, currentRealmUser );
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import classNames from "classnames";
|
||||
import { Image } from "components/styledComponents";
|
||||
import * as React from "react";
|
||||
import { FastImage as Image } from "components/styledComponents";
|
||||
import type { Node } from "react";
|
||||
import React from "react";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
type Props = {
|
||||
@@ -15,7 +16,7 @@ type Props = {
|
||||
|
||||
const UserIcon = ( {
|
||||
uri, small, active, large, medium
|
||||
}: Props ): React.Node => {
|
||||
}: Props ): Node => {
|
||||
const getSize = ( ) => {
|
||||
if ( small ) {
|
||||
return "w-[22px] h-[22px]";
|
||||
@@ -34,6 +35,7 @@ const UserIcon = ( {
|
||||
"rounded-full",
|
||||
size
|
||||
);
|
||||
|
||||
// For unknown reasons, the green border doesn't show up on Android using nativewind classNames
|
||||
// but it works with style, might warrant further investigation or an issue in nativewind
|
||||
const style = { borderColor: colors.inatGreen, borderWidth: 3 };
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
TextInput as UntyledTextInput,
|
||||
View as UnstyledView
|
||||
} from "react-native";
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import UnstyledFastImage from "react-native-fast-image";
|
||||
import UnstyledLinearGradient from "react-native-linear-gradient";
|
||||
|
||||
// $FlowIgnore
|
||||
@@ -68,8 +70,12 @@ const fontMonoClass: string = ( Platform.OS === "ios"
|
||||
// $FlowIgnore
|
||||
const LinearGradient = styled( UnstyledLinearGradient );
|
||||
|
||||
// $FlowIgnore
|
||||
const FastImage = styled( UnstyledFastImage );
|
||||
|
||||
export {
|
||||
BottomSheetTextInput,
|
||||
FastImage,
|
||||
fontMonoClass,
|
||||
Image,
|
||||
ImageBackground,
|
||||
|
||||
@@ -13,11 +13,10 @@ const OBS_LIST_SCREEN_ID = "ObservationsStackNavigator";
|
||||
const NOTIFICATIONS_SCREEN_ID = "Notifications";
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
isOnline: boolean
|
||||
navigation: Object
|
||||
};
|
||||
|
||||
const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
const CustomTabBarContainer = ( { navigation }: Props ): Node => {
|
||||
const { t } = useTranslation( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const [activeTab, setActiveTab] = useState( OBS_LIST_SCREEN_ID );
|
||||
@@ -29,8 +28,6 @@ const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
testID: DRAWER_ID,
|
||||
accessibilityLabel: t( "Open-drawer" ),
|
||||
accessibilityHint: t( "Opens-the-side-drawer-menu" ),
|
||||
width: 44,
|
||||
height: 44,
|
||||
size: 32,
|
||||
onPress: ( ) => {
|
||||
navigation.openDrawer( );
|
||||
@@ -43,8 +40,6 @@ const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
testID: EXPLORE_SCREEN_ID,
|
||||
accessibilityLabel: t( "Explore" ),
|
||||
accessibilityHint: t( "Navigates-to-explore" ),
|
||||
width: 44,
|
||||
height: 44,
|
||||
size: 40,
|
||||
onPress: ( ) => {
|
||||
navigation.navigate( "ObservationsStackNavigator", {
|
||||
@@ -56,16 +51,10 @@ const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
},
|
||||
{
|
||||
icon: "person",
|
||||
userIconUri: isOnline
|
||||
? User.uri( currentUser )
|
||||
: null,
|
||||
testID: User.uri( currentUser ) && isOnline
|
||||
? "NavButton.avatar"
|
||||
: "NavButton.personIcon",
|
||||
userIconUri: User.uri( currentUser ),
|
||||
testID: "NavButton.personIcon",
|
||||
accessibilityLabel: t( "Observations" ),
|
||||
accessibilityHint: t( "Navigates-to-observations" ),
|
||||
width: 44,
|
||||
height: 44,
|
||||
size: 40,
|
||||
onPress: ( ) => {
|
||||
navigation.navigate( "ObservationsStackNavigator", {
|
||||
@@ -80,8 +69,6 @@ const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
testID: NOTIFICATIONS_SCREEN_ID,
|
||||
accessibilityLabel: t( "Notifications" ),
|
||||
accessibilityHint: t( "Navigates-to-notifications" ),
|
||||
width: 44,
|
||||
height: 44,
|
||||
size: 32,
|
||||
onPress: ( ) => {
|
||||
navigation.reset( {
|
||||
@@ -94,7 +81,6 @@ const CustomTabBarContainer = ( { navigation, isOnline }: Props ): Node => {
|
||||
}
|
||||
] ), [
|
||||
activeTab,
|
||||
isOnline,
|
||||
currentUser,
|
||||
isDrawerOpen,
|
||||
navigation,
|
||||
|
||||
@@ -29,8 +29,8 @@ const NavButton = ( {
|
||||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
accessibilityRole = "tab",
|
||||
width,
|
||||
height
|
||||
width = 44,
|
||||
height = 44
|
||||
}: Props ): React.Node => {
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
const sharedProps = {
|
||||
|
||||
@@ -17,7 +17,6 @@ import DeveloperStackNavigator from "navigation/StackNavigators/DeveloperStackNa
|
||||
import ObservationsStackNavigator from "navigation/StackNavigators/ObservationsStackNavigator";
|
||||
import ProjectsStackNavigator from "navigation/StackNavigators/ProjectsStackNavigator";
|
||||
import React from "react";
|
||||
import { useIsConnected } from "sharedHooks";
|
||||
|
||||
import CustomTabBarContainer from "./CustomTabBarContainer";
|
||||
|
||||
@@ -28,8 +27,7 @@ const OBS_LIST_SCREEN_ID = "ObservationsStackNavigator";
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
const BottomTabs = ( ) => {
|
||||
const isOnline = useIsConnected( );
|
||||
const renderTabBar = props => <CustomTabBarContainer {...props} isOnline={isOnline} />;
|
||||
const renderTabBar = props => <CustomTabBarContainer {...props} />;
|
||||
|
||||
const aboutTitle = () => <Heading4>{t( "ABOUT-INATURALIST" )}</Heading4>;
|
||||
const donateTitle = () => <Heading4>{t( "DONATE" )}</Heading4>;
|
||||
|
||||
@@ -3,7 +3,11 @@ import { fetchUserMe } from "api/users";
|
||||
import { RealmContext } from "providers/contexts";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import safeRealmWrite from "sharedHelpers/safeRealmWrite";
|
||||
import { useAuthenticatedQuery, useCurrentUser, useIsConnected } from "sharedHooks";
|
||||
import {
|
||||
useAuthenticatedQuery,
|
||||
useCurrentUser,
|
||||
useIsConnected
|
||||
} from "sharedHooks";
|
||||
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
|
||||
@@ -378,3 +378,11 @@ inatjs.announcements.search.mockResolvedValue( makeResponse( ) );
|
||||
inatjs.observations.updates.mockResolvedValue( makeResponse( ) );
|
||||
|
||||
jest.mock( "react-native-audio-recorder-player", ( ) => MockAudioRecorderPlayer );
|
||||
|
||||
jest.mock( "react-native-fast-image", ( ) => {
|
||||
const actualNav = jest.requireActual( "react-native-fast-image" );
|
||||
return {
|
||||
...actualNav,
|
||||
preload: jest.fn( )
|
||||
};
|
||||
} );
|
||||
|
||||
@@ -1,98 +1,155 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`UserIcon displays active user image correctly 1`] = `
|
||||
<Image
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 40,
|
||||
},
|
||||
{
|
||||
"height": 40,
|
||||
},
|
||||
{
|
||||
"borderColor": "#74AC00",
|
||||
"borderWidth": 3,
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 40,
|
||||
},
|
||||
{
|
||||
"height": 40,
|
||||
},
|
||||
{
|
||||
"borderColor": "#74AC00",
|
||||
"borderWidth": 3,
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
>
|
||||
<FastImageView
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`UserIcon displays small user image correctly 1`] = `
|
||||
<Image
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 22,
|
||||
},
|
||||
{
|
||||
"height": 22,
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 22,
|
||||
},
|
||||
{
|
||||
"height": 22,
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
>
|
||||
<FastImageView
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`UserIcon displays user image correctly 1`] = `
|
||||
<Image
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 40,
|
||||
},
|
||||
{
|
||||
"height": 40,
|
||||
},
|
||||
[
|
||||
{
|
||||
"borderBottomLeftRadius": 9999,
|
||||
"borderBottomRightRadius": 9999,
|
||||
"borderTopLeftRadius": 9999,
|
||||
"borderTopRightRadius": 9999,
|
||||
},
|
||||
{
|
||||
"width": 40,
|
||||
},
|
||||
{
|
||||
"height": 40,
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
>
|
||||
<FastImageView
|
||||
accessibilityIgnoresInvertColors={true}
|
||||
accessibilityRole="image"
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "some_uri",
|
||||
}
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
testID="UserIcon.photo"
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user