I have an app that is currently live in the store. After clicking a button 3 times in the application, an advertisement is shown. There has been a serious decrease in advertising revenues in the last two days. The reason for this was that on some devices, it was necessary to press more than 3 times for the ad to appear. On my friend's phone, normally it was necessary to click 3 times for the ad to appear, but now after 13 clicks, the ad appeared. How can I solve this issue?
Splash screen
import { View } from 'react-native'
import React, { useEffect } from 'react'
import mobileAds from 'react-native-google-mobile-ads';
export default Splash = ({ navigation }) => {
useEffect(() => {
mobileAds().initialize()
.then(e => {
navigation.navigate("MainScreen")
})
}, [])
return (
<View />
)
}
MainScreen:
import { Button } from 'react-native'
import React, { useState, useEffect } from 'react'
import { InterstitialAd, TestIds, AdEventType } from 'react-native-google-mobile-ads';
const adUnitId = __DEV__ ? TestIds.INTERSTITIAL : 'ca-app-pub-XXXX';
const interstitialAd = InterstitialAd.createForAdRequest(adUnitId);
export default MainScreen = ({ navigation }) => {
const [request, setRequest] = useState(0)
useEffect(() => {
interstitialAd.addAdEventsListener(({ type }) => {
if (type === AdEventType.LOADED) {
interstitialAd.show();
}
});
}, [])
useEffect(() => {
if (countOfRequests % 3 == 0) {
interstitialAd.load();
}
}, [request])
return (
<Button
title='press'
onPress={() => setRequest(prev => prev + 1)}
/>
)
}
Related
I've been battling a bug in my code for the last 4 days and would appreciate some pointers to get me going in the right directions. Component is working fine as long as there is internet connection, but if there is no internet connection, audios and videos are not playing, only thumbnail present.
I'm using netInfo's NetInfo.fetch() to check for connection. If there is connection, I'm refetching data to check for any updates to student assignments.
I'm using expo-av for playing audio/video files (v10.2.1). I'm also using useQuery hook from react-query to fetch data about audio and video files (like url etc.) My video player component is something like this:
Video Player:
import React, {
forwardRef,
ForwardRefRenderFunction,
useCallback,
useImperativeHandle,
useRef
} from 'react';
import { Platform } from 'react-native';
import Orientation from 'react-native-orientation-locker';
import { Audio, Video, VideoFullscreenUpdateEvent, VideoProps } from 'expo-av';
const Player: ForwardRefRenderFunction<
Video | undefined,
VideoProps
> = (props, ref) => {
const innerRef = useRef<Video>(null);
const orientation = useCallback<
(event: VideoFullscreenUpdateEvent) => void
>(
(event) => {
if (Platform.OS === 'android') {
if (
event.fullscreenUpdate === Video.FULLSCREEN_UPDATE_PLAYER_DID_PRESENT
) {
Orientation.unlockAllOrientations();
} else if (
event.fullscreenUpdate === Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS
) {
Orientation.lockToPortrait();
}
}
props.onFullscreenUpdate?.(event);
},
[props]
);
useImperativeHandle(ref, () => {
if (innerRef.current) {
return innerRef.current;
}
return undefined;
});
return (
<Video
resizeMode="contain"
useNativeControls
ref={innerRef}
onLoad={loading}
{...props}
onFullscreenUpdate={orientation}
/>
);
};
export const VideoPlayer = forwardRef(Player);
Custom Hook:
For async state management, I'm using a custom react-query hook, that looks something like this (non-relevant imports and code removed):
import { useFocusEffect } from '#react-navigation/core';
import { useCallback } from 'react';
import NetInfo from '#react-native-community/netinfo';
export const useStudentAssignment = (
assignmentId: Assignment['id']
): UseQueryResult<Assignment, Error> => {
const listKey = studentAssignmentKeys.list({ assignedToIdEq: studentData?.id });
const queryClient = useQueryClient();
const data = useQuery<Assignment, Error>(
studentAssignmentKeys.detail(assignmentId),
async () => {
const { data: assignment } = await SystemAPI.fetchAssignment(assignmentId);
return Assignment.deserialize({
...assignment,
});
},
{
staleTime: 1000 * 60 * 30,
initialData: () => {
const cache= queryClient.getQueryData<Assignment[]>(listKey);
return cache?.find((assignment) => assignment.id === assignmentId);
},
initialDataUpdatedAt: queryClient.getQueryState(listKey)?.dataUpdatedAt,
}
);
useFocusEffect(
useCallback(() => {
NetInfo.fetch().then((state) => {
if (state.isConnected) {
data.refetch();
}
});
}, [data])
);
return data;
};
Component:
import React, { FC, useCallback, useEffect, useMemo, useRef } from 'react';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StackScreenProps } from '#react-navigation/stack';
import { ROUTES } from 'enums/SMSRoutes';
import { StoreType } from 'enums/SMSStoreType';
import { useStudentAssignment } from 'hooks/Assignments/useStudentAssignment';
import { RootStackParamList } from 'navigators';
import { AssignmentViewer } from 'screens/AssignmentViewer';
type NavProps = StackScreenProps<
RootStackParamList,
ROUTES.ASSIGNMENT_VIEW
>;
export const AssignmentView: FC<NavProps> = ({
navigation,
route: {
params: { assignmentId }
}
}) => {
const assignmentQuery = useStudentAssignment(assignmentId);
const assignmentTracker = useStore(StoreType.AssignmentTracker);
const isDoneRef = useRef<boolean>(false);
const questions = assignmentQuery.data?.questions || [];
const activeQuestion = useMemo(() => {
return questions.filter((question) => question.active);
}, [questions]);
const onDone = useCallback(() => {
isDoneRef.current = true;
navigation.push(ROUTES.ASSIGNMENT_COMPLETED);
}, [navigation]);
useEffect(() => {
assignmentTracker.start(assignmentId);
return () => {
assignmentTracker.finish(isDoneRef.current);
};
}, []);
return (
<SafeAreaView>
<AssignmentViewer
questions={activeQuestion}
onDone={onDone}
isLoading={assignmentQuery.isLoading}
/>
</SafeAreaView>
);
};
What I'm trying to do here is that if internet connection is connected and the user navigates to the current view (which is used to view assignments), I'd like to refetch the data. Per the requirements, I can't use the staleTime property or any other interval based refetching.
Component is working fine if I don't refetch, or if internet connection is present. If connection isn't there, it doesn't play the cache'd audio/video.
If I take out the check for internet connection (remove netInfo), component display videos both offline and online. However, refetching fails due to no connectivity.
What should I change to make sure that data is refetched when connected and videos are played even if not connected to Internet?
using this package for wheel selector, and works fine, I just need to select value programmatically with animation(as happen user did).
Here is source code, which works cool, just need prop to select programmatically.
import React, { useRef, useMemo, useEffect, useState } from "react";
import { LinearGradient } from "expo-linear-gradient";
import WheelPickerExpo from "react-native-wheel-picker-expo";
const CITIES = "Jakarta,Bandung,Sumbawa,Taliwang,Lombok,Bima".split(",");
const ActionSheet = () => {
const [selectedRate, setSelectedRate] = useState(0);
useEffect(() => {
setTimeout(() => {
setSelectedRate(2);//changing initial select doesnt animate.
}, 4000);
}, []);
return (
<WheelPickerExpo
height={300}
width="100%"
initialSelectedIndex={selectedRate}
items={CITIES.map(name => ({ label: name, value: "" }))}
onChange={({ item }) => {
console.log(`item`, item);
}}
/>
);
};
export default ActionSheet;
Trying to import .ttf for font in expo cli.
I also have splash screen. I wanna show the splash screen until the font loads.
Font: Josefin Sans.
"expo": "~45.0.0"
I took reference from following links but nothing works:
Using Custom Fonts: https://docs.expo.dev/guides/using-custom-fonts/
Splash Screen: https://docs.expo.dev/versions/latest/sdk/splash-screen/
Code (App.js)
import { useState, useEffect, useCallback } from "react";
import { Text } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import Header from "./components/Header.component";
import styles from "./styles/appStyle";
import * as Font from "expo-font";
import * as SplashScreen from "expo-splash-screen";
const App = () => {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
// Pre-load fonts
await Font.loadAsync({
"JosefinSans-Regular": require("./assets/fonts/JosefinSans-Regular.ttf"),
});
// Artificially delay for two seconds to simulate a slow loading
// experience. Please remove this if you copy and paste the code!
await new Promise((resolve) => setTimeout(resolve, 2000));
} catch (e) {
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
// This tells the splash screen to hide immediately! If we call this after
// `setAppIsReady`, then we may see a blank screen while the app is
// loading its initial state and rendering its first pixels. So instead,
// we hide the splash screen once we know the root view has already
// performed layout.
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<>
<SafeAreaView style={styles.container} onLayout={onLayoutRootView}>
<Header />
<Text>Hello World!</Text>
<StatusBar style="light" backgroundColor="#05060B" />
</SafeAreaView>
</>
);
};
export default App;
Error
Android Bundling failed 12ms
Unable to resolve module ./assets/fonts/JosefinSans-Regular.ttf from C:\Users\user\Desktop\app\App.js:
None of these files exist:
* JosefinSans-Regular.ttf
* assets\fonts\JosefinSans-Regular.ttf\index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
18 | // Pre-load fonts
19 | await Font.loadAsync({
> 20 | "JosefinSans-Regular": require("./assets/fonts/JosefinSans-Regular.ttf"),
| ^
21 | });
22 | // Artificially delay for two seconds to simulate a slow loading
23 | // experience. Please remove this if you copy and paste the code!
File Structure:
Snap.png
Answer
expo install #expo-google-fonts/josefin-sans expo-font
And the code looks like this.
import { useState, useEffect, useCallback } from "react";
import { Text } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import Header from "./components/Header.component";
import styles from "./styles/appStyle";
import * as Font from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import {
useFonts,
JosefinSans_100Thin,
JosefinSans_200ExtraLight,
JosefinSans_300Light,
JosefinSans_400Regular,
JosefinSans_500Medium,
JosefinSans_600SemiBold,
JosefinSans_700Bold,
JosefinSans_100Thin_Italic,
JosefinSans_200ExtraLight_Italic,
JosefinSans_300Light_Italic,
JosefinSans_400Regular_Italic,
JosefinSans_500Medium_Italic,
JosefinSans_600SemiBold_Italic,
JosefinSans_700Bold_Italic,
} from "#expo-google-fonts/josefin-sans";
const App = () => {
const [appIsReady, setAppIsReady] = useState(false);
let [fontsLoaded] = useFonts({
JosefinSans_100Thin,
JosefinSans_200ExtraLight,
JosefinSans_300Light,
JosefinSans_400Regular,
JosefinSans_500Medium,
JosefinSans_600SemiBold,
JosefinSans_700Bold,
JosefinSans_100Thin_Italic,
JosefinSans_200ExtraLight_Italic,
JosefinSans_300Light_Italic,
JosefinSans_400Regular_Italic,
JosefinSans_500Medium_Italic,
JosefinSans_600SemiBold_Italic,
JosefinSans_700Bold_Italic,
});
const prepare = async () => {
try {
// Pre-load fonts
await Font.loadAsync(fontsLoaded)
.then(() => {
setAppIsReady(true);
})
.catch((err) => {});
// Artificially delay for two seconds to simulate a slow loading
// experience. Please remove this if you copy and paste the code!
// await new Promise((resolve) => setTimeout(resolve, 2000));
} catch (e) {}
};
useEffect(() => {
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
// This tells the splash screen to hide immediately! If we call this after
// `setAppIsReady`, then we may see a blank screen while the app is
// loading its initial state and rendering its first pixels. So instead,
// we hide the splash screen once we know the root view has already
// performed layout.
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<>
<SafeAreaView style={styles.container} onLayout={onLayoutRootView}>
<Header />
<Text>Hello World!</Text>
<StatusBar style="light" backgroundColor="#05060B" />
</SafeAreaView>
</>
);
};
export default App;
What I need to do is to render the react native routes based on the users auth status. Right now I am doing this the wrong way, by having an interval running to check for auth status change:
import React, { useState, useEffect } from 'react';
import { AppLoading } from 'expo';
import { checkAuth } from './auth';
import { LoggedInRoutes, LoggedOutRoutes } from './router';
export default () => {
const [isReady, setReady] = useState(false);
const [loggedIn, setLoggedIn] = useState(false);
useEffect(() => {
setInterval(() => {
checkAuth()
.then((res) => { setLoggedIn(res); setReady(true); console.log('checked..') })
.catch((err) => alert(err));
}, 1500);
}, [loggedIn]);
if (!isReady) {
return (
<AppLoading
onFinish={() => setReady(true)}
/>
);
}
return (
loggedIn ? <LoggedInRoutes /> : <LoggedOutRoutes />
);
}
But obviously that is quite bad. I am using async storage to save the user when he authenticates and remove him from storage when he clicks the logout button.
Is there a way to check for changes in async storage and re-render the routes? or run a function that changes loggedIn state when user click login/logout button?
I would recommend to use switchNavigator in react navigation
reactnavigation.org/docs/4.x/auth-flow – mr-nobody 40 secs ago Edit Delete
this approach will works like a charm.
import React, {useState, useEffect} from 'react';
import OnBoardingRoutes from './onBoarding.routes';
import AppRoutes from './app.routes';
import checkFirstUsage from "./checkFirstUsage/path";
const Routes: React.FC = () => {
const [loading, setLoading] = useState(true)
const [firstUsage,setFirstUsage] =useState(null);
useEffect(() => {
async function check() {
const fU = await checkFirstUsage()
setFirstUsage(fU)
setLoading(false)
}
check()
},[])
if (loading) return null // or any better component
return firstUsage ? <OnBoardingRoutes /> : <AppRoutes />;
};
export default Routes;
I want to show apple button if platform is ios. The button show properly in ios emulator. But I am confused to test the platfrom.
I have try to mock the platform but the platform will be ios by default in the first time (you can see the image).
This is my component.
import React, { useState, useEffect } from 'react'
import { ScrollView, StatusBar, Platform, View, Text, Linking, SafeAreaView } from 'react-native'
import Header from './components/Header'
import Form from './components/Form'
import ButtonFull from '../../../__global__/button/buttonFull'
import styles from './styles/StyleLogin'
import color from '../../../__global__/styles/themes/colorThemes'
const LoginScreen = () => {
const showAppleButton = () => {
console.log('Platform ', Platform.OS)
if (Platform.OS === 'ios') {
console.log('Platform OS ', Platform.OS)
console.log('Platform Version ', Platform.Version)
const version = Platform.Version ? Platform.Version.split('.')[0] : 0
if (version >= 13) {
return (
<View style={styles.containerButton}>
<ButtonFull
accessibilityLabel={'appleButton'}
isDisabled={false}
buttonColor={color.black}
onPress={() => loginWithApple()}
title={'Apple ID'}
/>
</View>
)
} else {
return false
}
} else {
return false
}
}
return (
<ScrollView>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<Header />
<Form
phoneNumber={phoneNumber}
changePhoneNumber={(value) => changePhoneNumber(value)}
focus={focus}
setFocus={(value) => setFocus(value)}
loginSubmit={() => loginSubmit()}
error={error}
fullFilled={fullFilled}
submited={submited}
setSubmited={(value) => setSubmited(value)}
sendOtp={() => sendOtp()} />
{showAppleButton()}
</ScrollView>
)
}
export default LoginScreen
Test File
import React from 'react'
import { configure, shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import LoginScreen from '../index'
import renderer from 'react-test-renderer'
import { fireEvent, render, waitFor } from 'react-native-testing-library'
import '#testing-library/jest-native/extend-expect'
import { Provider } from 'react-redux'
import reducers from '../../../../redux/store'
import { createStore } from 'redux'
import { Platform } from 'react-native'
jest.mock('#react-navigation/native', () => ({
useNavigation: component => component,
}))
describe('Login screen', () => {
const mockPlatform = (OS, Version) => {
jest.resetModules()
jest.doMock('react-native/Libraries/Utilities/Platform', () => ({
OS,
select: config => config[OS],
Version,
}))
};
const store = createStore(reducers)
configure({ adapter: new Adapter() })
const wrapper = shallow(<Provider store={store}><LoginScreen /></Provider>)
const rendered = renderer.create(<Provider store={store}><LoginScreen /></Provider>)
it('renders correctly', () => {
expect(rendered.toJSON()).toBeTruthy()
})
it('should render the header component', () => {
expect(wrapper.find('Header').exists())
})
it('should render the form component', () => {
expect(wrapper.find('Form').exists())
})
it('should render the button social media', () => {
mockPlatform('android', '15.0.1')
console.log('Apple Button ', wrapper.find('[accessibilityLabel="appleButton"]').exists())
})
})
In this image, platform will be ios in the first time. I dont know why.
It's not safe to rely on module internals like react-native/Libraries/Utilities/Platform. Platform is imported from react-native and should be preferably mocked on this module.
jest.doMock doesn't affect modules that were imported on top level. In order for a mock to take effect, the whole hierarchy that depends on mocked module needs to re-imported locally with require.
In this case this isn't needed because Platform is referred as an object, so its properties can be mocked instead. The mock needs to be aware which properties can be mocked as functions:
let originalOS;
beforeEach(() => {
originalOS = Platform.OS;
});
afterEach(() => {
Platform.OS = originalOS;
jest.restoreAllMocks();
});
it('should mock Platform', () => {
jest.spyOn(Platform, 'select').mockReturnValue('android');
jest.spyOn(Platform, 'Version', 'get').mockReturnValue('15.0.1')
Platform.OS = 'android';
...
});