I am attempting to build a notification service I can use within React Navigation; how far I have gotten in this:
Now this is a modal, that the card element has been hidden and then the view style has been filled into only 15% at the top of the element, and the element will react if you click out of the white area of the notification.
Here is the code for the NotificationModal:
import React from 'react';
import { View, TouchableHighlight,TouchableWithoutFeedback } from 'react-native';
import { Text } from 'react-native-elements';
export default class NotificationModal extends React.Component {
static navigationOptions = ({ navigation }) => {
const title = navigation.getParam('title','No item found...');
return {
title: title
};
};
constructor(props) {
super(props);
}
componentDidMount() {
console.log(this.props);
}
render() {
const { navigation } = this.props;
let title = navigation.getParam('title', '');
return (
<TouchableWithoutFeedback>
<View style={{ flex: 1 ,flexDirection: 'column', justifyContent: 'flex-start'}}>
<View style={{ height: "15%" ,width: '100%', backgroundColor:"#fff", justifyContent:"center"}}>
<Text style={{fontSize:25}}>{title}xd</Text>
</View>
<TouchableHighlight onPress={() => { this.props.navigation.goBack(); }} style={{backgroundColor: "rgba(0,0,0,0)"}}>
<View style={{ height: "85%" ,width: '100%', backgroundColor: "rgba(0,0,0,0)", justifyContent:"center"}}>
<View>
</View>
</View>
</TouchableHighlight>
</View>
</TouchableWithoutFeedback>
);
}
}
At at my root navigation stack it looks like this:
export default RootStack = createStackNavigator(
{
Main: {
screen: MainStack,
},
QuickStockModal: {
screen: StockModal,
},
NotificationModal: {
screen: NotificationModal,
}
},
{
mode: 'modal',
headerMode: 'none',
cardStyle:{
backgroundColor:"transparent",
opacity:0.99
}
}
);
Finally I call this pseudo notification modal like any other navigation instance:
this.props.navigation.navigate('NotificationModal', {
title: 'test'
});
Problems with this
It wont just act as a quick update, it will overtake the touch area, I just wish to let the user know things like 'search results empty' or 'barcode invalid' etc, without hijacking the touch. (But being able to touch buttons of the notification within the white area thats visible)
When you press the outside area to dismiss it, the 'dismiss area' turns dark as it disappears, this looks bad, I am hoping to change the animation to simply push up instead of down and hopefully avoid this
this is a messy way to do notifications but other manners will complicate my current app design too much (Using Redux for instance, will be alot of work for simply trying to add in-app notifications)
In-app notification libs arent customizable enough and also dont even work properly sadly
Related
I am using a so simple GiftedChat component in react native on expo, the problem here is when i open the first time the keyboard this works fine, but the second time i press in the textInput but the keyboard doesn't appear, i'm using a emulator of android 5 and a device with android 7. Doesn't work in any
import { useState, useLayoutEffect, useCallback } from 'react'
import { TouchableOpacity } from 'react-native'
import { GiftedChat } from 'react-native-gifted-chat'
import {
collection,
addDoc,
orderBy,
query,
onSnapshot
} from 'firebase/firestore'
import { signOut } from 'firebase/auth'
import { auth, database } from '../config/firebase'
import { useNavigation } from '#react-navigation/native'
import { AntDesign } from '#expo/vector-icons'
import colors from '../colors'
export default function Chat() {
const [messages, setMessages] = useState([])
const navigation = useNavigation()
const onSignOut = () => {
signOut(auth)
.catch(err => console.log('Error logging out: ', err.message))
}
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
style={{
marginRight: 10
}}
onPress={onSignOut}
>
<AntDesign
name='logout'
size={24}
color={colors.gray}
style={{
marginRight: 10
}}
/>
</TouchableOpacity>
)
})
}, [navigation])
useLayoutEffect(() => {
const collectionRef = collection(database, 'chats')
const q = query(collectionRef, orderBy('createdAt', 'desc'));
const unsubscribe = onSnapshot(q, snapshot => {
setMessages(
snapshot.docs.map(doc => ({
_id: doc.data()._id,
createdAt: doc.data().createdAt.toDate(),
user: doc.data().user,
text: doc.data().text
}))
)
})
return unsubscribe
}, [])
const onSend = useCallback((messages = []) => {
setMessages(previewMessages => GiftedChat.append(previewMessages, messages))
const { _id, createdAt, text, user } = messages[0]
addDoc(collection(database, 'chats'), {
_id,
createdAt,
text,
user,
})
}, [])
return (
<>
<GiftedChat
messages={messages}
showAvatarForEveryMessage={false}
showUserAvatar={true}
onSend={message => onSend(message)}
messagesContainerStyle={{
backgroundColor: '#fff'
}}
user={{
_id: auth?.currentUser?.email,
avatar: 'https://placeimg.com/140/140/any'
}}
/>
</>
)
}
gif example of the error
If you can help me with the reason why this happens i will thank you so much...
The problem is that the TextInput of GiftedChat (of TextInput in general actually) never looses focus if you dismiss the keyboard using the virtual Android button. Thus, the keyboard is not opened again if you simply retouch into the TextInput. If you touch outside it and then inside again, you will notice that the keyboard opens again. This should be the normal behavior of any TextInput and might be even expected by the user (we can open the keyboard again by pressing the same button).
However, you could try to handle this programmatically as well. The GiftedChat component provides a function to allow you to refocus the TextInput again programmatically. It is called focusTextInput() and it
Open(s) the keyboard and focus the text input box
const ref = useRef(null)
<GiftedChat
…
ref={ref}
/>
Then, call ref.focusTextInput(). We could then wrap the GiftedChat component inside a Pressable and fire this in the onPress function.
const ref = useRef(null)
<Pressable onPress={() => ref.focusTextInput()}
<GiftedChat
…
ref={ref}
/>
</Pressable>
I have added react-navigation-drawer for implementing drawer navigation in my app. I have created a file named PrimaryNav.js and added all navigation code in it.
import Login from './components/Login';
import Employee from './pages/Employee';
import { createAppContainer,SafeAreaView, } from 'react-navigation'
import { createDrawerNavigator, DrawerItems } from 'react-navigation-drawer';
import React from 'react';
const Primary_Nav = createDrawerNavigator({
Login: {
screen: Login,
navigationOptions: {
drawerLabel: () => null
}
},
Home_kitchen: {
screen: Home_kitchen,
navigationOptions: {
drawerLabel: "Home"
}
},
Employee: {
screen: Employee,
navigationOptions:{
drawerLabel:"Employee",
}
},
},{
initialRouteName:'Login',
drawerPosition: 'left',
drawerType: "slide",
}
});
const PrimaryNav = createAppContainer(Primary_Nav);
export default PrimaryNav;
Something like above. I have called this file in the App.js, the issue I am facing is I need to set a drawer item based on the role which the user has. So if the user role is cashier he should not be able to see all the menu.
All the pages are coming properly in the drawer menu but the question is how should I want to manage menu role wise in my app and changed the menu based on the roles of the user?
hi I saw your issue and I am trying to helping you.
I have make a custom design for drawer components .
-firstly you can create a extra file for drawer Design like DrawerComponent.js
and import in your code where you are create a drawer navigator
import DrawerComponent from "./DrawerComponent";
const Primary_Nav = createDrawerNavigator(
{
Login: {
screen: Login,
navigationOptions: {
drawerLabel: () => null
}
},
Home_kitchen: {
screen: Home_kitchen,
navigationOptions: {
drawerLabel: "Home"
}
},
Employee: {
screen: Employee,
navigationOptions: {
drawerLabel: "Employee"
}
}
},
{
initialRouteName: "Login",
drawerPosition: "left",
drawerType: "slide",
contentComponent: DrawerComponent // i added this DrawerComponent
}
);
const PrimaryNav = createAppContainer(Primary_Nav);
export default PrimaryNav;
now in the DrawerComponent.js
import React, { Component } from "react";
import { Text, View, TouchableOpacity } from "react-native";
export default class DrawerComponent extends Component {
constructor(props) {
super(props);
this.state = {
role: 1 // i used 1 for cashier and 0 for chef
};
}
render() {
const { role } = this.state;
const { navigation } = this.props;
return (
<View style={{ flex: 1, paddingVertical: 40, paddingHorizontal: 20 }}>
<TouchableOpacity
style={{ margin: 20 }}
onPress={() => navigation.navigate("Home_kitchen")}
>
<Text>Home</Text>
</TouchableOpacity>
{role ? (
<TouchableOpacity
style={{ margin: 20 }}
onPress={() => navigation.navigate("Employee")}
>
<Text>Employee</Text>
</TouchableOpacity>
) : null}
</View>
);
}
}
if you are change the role to 0 then the Employee tab is disable in Drawer Navigator I have user the ternary operator for conditions. you can modify is as you can want. hope it will helpful for you.
I would like to know how I would go about implementing a welcome/getting started screen using react navigation v3.
My confusion would be where the welcome/getting started screen would go?
should the screen be in the Appstack or Authstack?
I want to display this to new users only. When a user logs out and re-authenticate I want it to be popped out of the stack because they are not new users and take them directly to the main app.
I think this piece of logic should take place in the Authloadingscreen, am just not sure how or what technique to use.
This is an example Appfrom https://snack.expo.io/#react-navigation/auth-flow-v3
Any help would be appreciated, thanks.
import React from 'react';
import {
ActivityIndicator,
AsyncStorage,
Button,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import { createStackNavigator, createSwitchNavigator, createAppContainer } from 'react-navigation';
class SignInScreen extends React.Component {
static navigationOptions = {
title: 'Please sign in',
};
render() {
return (
<View style={styles.container}>
<Button title="Sign in!" onPress={this._signInAsync} />
</View>
);
}
_signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('App');
};
}
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome to the app!',
};
render() {
return (
<View style={styles.container}>
<Button title="Show me more of the app" onPress={this._showMoreApp} />
<Button title="Actually, sign me out :)" onPress={this._signOutAsync} />
</View>
);
}
_showMoreApp = () => {
this.props.navigation.navigate('Other');
};
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class OtherScreen extends React.Component {
static navigationOptions = {
title: 'Lots of features here',
};
render() {
return (
<View style={styles.container}>
<Button title="I'm done, sign me out" onPress={this._signOutAsync} />
<StatusBar barStyle="default" />
</View>
);
}
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class AuthLoadingScreen extends React.Component {
constructor() {
super();
this._bootstrapAsync();
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View style={styles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
));
I would put this in the AppStack, since it is part of your app content and not part of your authentication flow.
Additionally, you need a way to determine if it's a new user or a returning user. So either you store this information server side, or locally using AsyncStorage. The best approach would be to store this information server side, since a user can always get a new phone. So during loading (if authenticated) or authenticating you make sure you fetch that data and display/hide the welcome screen accordingly.
I am designing a mobile app to work with a site we already have up and running. There are a few things we would like to take advantage of on the phone, however. For example, using the camera to upload photographs to the site.
Currently, I have this code
export default class App extends Component<Props> {
render() {
return (
<WebView
source={{uri: "https://example.com"}}
/>
);
}
}
Is there some way to add a button that lets users open a menu of options that exists on the app alone (and separate from the site in the webview).
I'v tried something like
export default class App extends Component<Props> {
render() {
return (
<View>
<Text>Test</Text>
<WebView
source={{uri: "https://example.com"}}
/>
</View>
);
}
}
just as a test to see if I can add other elements outside the webview, but my app never loads (I just see a blank white screen) when I try this.
The reason that Test and WewbView won't show up in your example code it's because View doesn't have a defined height, you can use flex:1 to give it the entire screen.
See this working sample:
https://snack.expo.io/#cgomezmendez/vengeful-tortillas
import * as React from 'react';
import { Text, View, StyleSheet, WebView } from 'react-native';
import { Constants } from 'expo';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Test</Text>
<WebView
source={{uri: "https://google.com"}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
You might have a look at this: react-native-webview-bridge
So basically i'm in the process of learning react native. I'm using the react navigation package and I just want to display a simple header title on my stack navigator but the title cuts off. Stack nav title
This is my App.js
import React from 'react';
import { View, Text } from 'react-native';
import { createStackNavigator, createAppContainer} from 'react-navigation'
import HomeScreen from './screens/HomeScreen'
import DetailsScreen from './screens/DetailsScreen'
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Details: DetailsScreen,
},
{
initialRouteName: 'Home'
}
);
const AppContainer = createAppContainer(RootStack)
export default class App extends React.Component {
render(){
return<AppContainer />
}
}
and this is my HomeScreen.js
import React from 'react'
import {Button, View, Text } from 'react-native'
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screeeen</Text>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/>
</View>
)
}
}
It doesnt look like this on my friends phones. I'm using a OnePlus 6 with android 9. They are on older versions of android could that be causing something?
Turns out this is a specific problem affecting OnePlus user who has chosen to use the font OnePlus Slate instead of for exmaple robot. Changing the font in the phone fixes the problem, alternatively you force the usage of a font in the app and it should work as well
I got the same problem running on a OnePlus phone and solved it without the font loader but style with the navigation option in headerTitleStyle width like so:
import { Dimensions } from 'react-native';
const WIDTH = Dimensions.get('window').width;
export const MyStackNav = createStackNavigator(
{
Tab1: {
screen: Tab1,
navigationOptions: ({ navigation }) => ({
headerTitle: `${navigation.state.routeName} page`,
headerTitleStyle: {
width: WIDTH - 75,
},
}),
},
...more code
}
A similar issue also occurs in various components like button in one plus devices. One way to solve it by giving some minimum width to the label style, so that you don't have to switch the font family, helps if you are using some custom fonts
Solution: width: '100%' or some fixed value like minWidth: 100
Code example in drawer navigation
<DrawerItem
labelStyle={{ minWidth: 100 }}
label={`Orders`}
onPress={() => {}}
/>