Hi so I've been experimenting with the expo-notifications library all day and firstly it's really neat however I've run into a bit of an obstacle and that is scheduling a notification that repeats daily but at a random time and shows different data (e.g. fetch quote from an API)
To better explain think of how BeReal send their notifications... daily, random times. I'm tryna mimic that locally.
I can't figure out how to achieve this and would really appreciate some clarification on whether this is even possible.
I've created a basic custom hook to experiment:
import { View, Text, Platform } from "react-native";
import React, { useEffect, useState, useRef } from "react";
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
// import { useNavigation } from "#react-navigation/native";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export default function useNotifications() {
const [expoPushToken, setExpoPushToken] = useState("");
const [givenNotificationPerm, setGivenNotificationPerm] = useState(false);
const [notification, setNotification] = useState(false);
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
// permissions(); - asks for permission
givenPermissions(); // checks if has permissions
getScheduled(); // get current notifications in pipeline
Notifications.cancelAllScheduledNotificationsAsync();
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
console.log("--- notification received ---");
// const show_time = notification?.request?.trigger?.dateComponents;
// console.log(show_time);
console.log(notification);
console.log("------");
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log("--- notification tapped ---");
const screen = response?.notification?.request?.content?.data?.screen;
const show_time =
response?.notification?.request?.content?.data?.show_time; // temp
// navigation.navigate("Root", { screen: screen });
console.log("this showed: " + show_time);
console.log("------");
});
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
async function permissions() {
registerForPushNotificationsAsync().then((token) =>
setExpoPushToken(token)
);
}
async function getScheduled() {
console.log("getting schedfuled notifcation");
const slay = await Notifications.getAllScheduledNotificationsAsync();
console.log("scheduled:");
console.log(slay);
}
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive
}
async function schedulePushNotification() {
console.log("reqested notifcation");
const randomSeconds = getRandomIntInclusive(61, 75);
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
];
const random = Math.floor(Math.random() * months.length);
Notifications.scheduleNotificationAsync({
content: {
title: `${months[random]} is random`,
data: { screen: "screenName", show_time: randomSeconds },
},
trigger: {
seconds: randomSeconds,
repeats: true,
},
});
await Notifications.cancelAllScheduledNotificationsAsync();
getScheduled();
}
async function registerForPushNotificationsAsync() {
let token;
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
alert("Must use physical device for Push Notifications");
}
return token;
}
async function givenPermissions() {
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
if (existingStatus !== "granted") {
setGivenNotificationPerm(false);
}
setGivenNotificationPerm(true);
} else {
alert("Must use physical device for Push Notifications");
}
}
return {
expoPushToken,
notification,
permissions,
schedulePushNotification,
givenNotificationPerm,
};
}
Pastebin code
And then for the App.js screen:
export default function App() {
const { permissions, schedulePushNotification } = useNotifications();
return(
<>
<Button title="Enable notifications - onboarding screen only button" onPress={permissions} />
<Button title="Schedule notification" onPress={schedulePushNotification} />
</>
)
}
Click the button multiple times, multiple notifications scheduled with random time and month for example purposes but how would i put this into practice to create an almost automation desire?
Right now my brain is thinking of just creating an array of 5 notifications and schedule them all in one go and then after X time has passed produce another 5 notifications but to do that i would have to ensure the user comes on the app within that timeframe to schedule more etc.
Bit stuck. Help appreciated
Thanks so much.
Related
I have a markets screen where I am using sockets to update the prices of cryptocurrencies in real time. The screen contains an infinite scroller, so when the user scrolls, more cryptocurrencies load and the coins being observed by the socket changes as well. However I am noticing as the coins list is increasing, the app becomes really slow and I cannot navigate to other screens or click anywhere quickly.
I have seen a few apps achieve this infinite-scroll-live-prices logic such as CoinGecko & CoinMarketCap.
Snippet of the relevant code:
const updatePriceOfCoins = (newPrices = {}, coins = []) => {
const updatedCoins = [...coins];
let wasUpdated = false;
for (let i = 0; i < updatedCoins.length; i++) {
let coin = updatedCoins[i];
if (newPrices[coin.id] !== undefined) {
updatedCoins[i] = { ...coin, priceUsd: newPrices[coin.id] };
wasUpdated = true;
}
}
return { wasUpdated, coins: updatedCoins };
};
const MarketsScreen = ({
markets,
getMarkets,
isLoading,
isLoadingMore,
perPage,
getMoreMarkets,
hasMore,
updateMarkets
}) => {
const socket = useLivePrices(markets);
const marketsRef = useRef(markets);
useEffect(() => {
marketsRef.current = markets;
}, [markets]);
const onNewPrices = (newPrices) => {
const { wasUpdated, coins: updatedMarkets } = updatePriceOfCoins(newPrices, marketsRef.current);
if (wasUpdated) {
updateMarkets(updatedMarkets);
}
};
useEffect(() => {
getMarkets();
}, []);
useEffect(() => {
if (socket !== null) {
socket.on("new prices", onNewPrices);
}
return () => {
if (socket !== null) {
socket.off("new prices");
}
};
}, [socket]);
return (
<FlatList
data={data}
renderItem={renderDataItem}
showsVerticalScrollIndicator={false}
onEndReached={getMoreMarkets}
onEndReachedThreshold={0.5}
/>
);
};
useLivePrices hook
const useLivePrices = (coinsToWatch = []) => {
const [socket, setSocket] = useState(null);
const prevCommaSepCoins = useRef("");
useEffect(() => {
//Only initialize socket once then everytime coinsToWatch is different
//update the coins observed
if (coinsToWatch.length > 0) {
if (socket === null) {
const commaSepCoins = coinsToCommaSepIDs(coinsToWatch);
setSocket(connectToLivePricesSocket(commaSepCoins));
prevCommaSepCoins.current = commaSepCoins;
} else {
const newCommaSepCoins = coinsToCommaSepIDs(coinsToWatch);
if (prevCommaSepCoins.current !== newCommaSepCoins) {
socket.emit("update coins", newCommaSepCoins);
prevCommaSepCoins.current = newCommaSepCoins;
}
}
}
}, [coinsToWatch]);
useEffect(() => {
let unsubFocus = () => {};
let unsubBlur = () => {};
if (socket !== null) {
//pause and resume prices based on if screen is in focus
unsubFocus = navigation.addListener("focus", resumePrices);
unsubBlur = navigation.addListener("blur", pausePrices);
}
return () => {
if (socket !== null) {
socket.disconnect();
unsubFocus();
unsubBlur();
}
};
}, [socket]);
return socket;
};
I want to achieve the infinite-scroll-live-prices but not sure how to optimize the performance anymore.
I tried optimizing the performance by reducing the number of renders when price updates. I have also tried to pause and resume the socket based on if the screen is focused so that state updates are not happening while the screen is not focused.
I am using react-native-google-mobile-ads in my App to display Interstitials and Rewarded Ads. Currently, I am trying to handle the EEA consent correctly.
This is my code:
const interstitial = InterstitialAd.createForAdRequest(adUnitId, {
...ADS_REQUEST_OPTIONS,
requestNonPersonalizedAdsOnly: !userChoices.selectPersonalisedAds,
});
Here is my code:
config.js:
export const TEST_DEVICE_ID = "EMULATOR";
export const INFO_REQUEST_CONFIGURATION = {
tagForUnderAgeOfConsent: true,
testDeviceIdentifiers: [TEST_DEVICE_ID],
// Always ensure debug information is removed for production apps!
...__DEV__ && { debugGeography: AdsConsentDebugGeography.EEA },
};
export const ADS_REQUEST_CONFIGURATION = {
maxAdContentRating: MaxAdContentRating.T,
tagForChildDirectedTreatment: false,
tagForUnderAgeOfConsent: true,
testDeviceIdentifiers: [TEST_DEVICE_ID],
};
export const ADS_REQUEST_OPTIONS = {
requestNonPersonalizedAdsOnly: false,
keywords: [
...
],
};
AdMobContext.jsx:
...
export function AdMobProvider({ children }) {
const requestEEAConsent = async () => {
const consentInfo = await AdsConsent.requestInfoUpdate(INFO_REQUEST_CONFIGURATION);
if (
consentInfo.isConsentFormAvailable &&
consentInfo.status === AdsConsentStatus.REQUIRED
) {
const { status } = await AdsConsent.showForm();
consentInfo.status = status;
}
console.log({ consentInfo });
return consentInfo;
};
const initializeAdmob = async () => {
// Request the respective consent to users in the EEA
await requestEEAConsent();
// Configure the ads requests
await mobileAds().setRequestConfiguration(ADS_REQUEST_CONFIGURATION);
// Initialize the AdMob service
await mobileAds().initialize();
};
useEffect(() => {
initializeAdmob();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
...
}
Will AdMob display ads to users that have not granted the consent?
Do we have to handle this manually or does AdMob manage this for us?
I'm trying to create an anti-crash function, but got confused at the moment that the channel does not return the author. How can I get the author in another way?
I tried to connect to AuditLogEvent, but it didn't work
My code:
const { AuditLogEvent } = requier('discord.js')
const usersMap = new Map();
const LIMIT = 3;
const TIMES = 10000
bot.rest.on('channelDelete', async channel => {
const fetchedLogs = await channel.guild.fetchAuditLogs({
limit: 1,
type: AuditLogEvent.ChannelDelete,
})
const deletionLog = fetchedLogs.entries.first();
const { executor, target } = deletionLog
if(channel.guild.id != "940990129307263046") return
if(usersMap.has(executor.id)) {
const userData = usersMap.get(executor.id);
const { lastDelete, timer } = userData;
let deleteCount = userData.deleteCount;
const tim = channel.createdTimestamp - lastDelete.createdTimestamp
if(tim > TIMES) {
usersMap.delete(executor.id)
} else {
++deleteCount;
if(parseInt(deleteCount) === LIMIT) {
executor.ban()
}
}
}
})
const NetworkToUse = process.env.REACT_APP_NETWORK;
const mnemonicWalletSubprovider = new MnemonicWalletSubprovider({
mnemonic: process.env.REACT_APP_MNEMONIC,
});
const infuraRpcSubprovider = new RPCSubprovider({
rpcUrl: `https://${NetworkToUse}.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
});
const providerEngine = new Web3ProviderEngine();
if (window.ethereum) {
providerEngine.addProvider(new SignerSubprovider(window.ethereum));
}
// providerEngine.addProvider(mnemonicWalletSubprovider);
providerEngine.addProvider(infuraRpcSubprovider);
providerEngine.start();
const seaport = new OpenSeaPort(
providerEngine,
{
networkName: NetworkToUse === "mainnet" ? Network.Main : Network.Rinkeby,
apiKey: process.env.REACT_APP_API_KEY,
},
(arg) => {
console.log("From OpenSeaPort CB:");
console.log(arg);
}
);
const placeBidMetaMask = async (order) => {
setIsProcessing(true);
if (typeof window.ethereum === "undefined") {
setError("Please make sure you have MetaMask installed!");
return;
}
if (!bidPrice || bidPrice < asset.price) {
setError("Insufficient Funds!");
return;
}
const { tokenId, tokenAddress } = order.asset;
try {
const [userAccount] = await window.ethereum.request({
method: "eth_requestAccounts",
});
const offer = await seaport.createBuyOrder({
asset: {
tokenId,
tokenAddress,
schemaName: asset.details.assetContract.schemaName,
},
accountAddress: userAccount,
startAmount: bidPrice,
});
console.log(offer);
setMessage("Buy Order Created");
} catch (err) {
setError(err.message);
console.log(err.message);
} finally {
setIsProcessing(false);
}
};
I am using metamask as wellet for bidding
Hi, I am using above code to place bid on opensea It is working but, I am using my personal MNEMONIC
But, in real time i can't get this from users meta mask wallet.
Is there any alternate way to place the bid.
I am using metamask as wellet for bidding
Hi, I am using above code to place bid on opensea It is working but, I am using my personal MNEMONIC
But, in real time i can't get this from users meta mask wallet.
Is there any alternate way to place the bid.
I want to make my push notification to be able to take me to a specific page 'Fingerprint' once its clicked. As of now the push notification does nothing when clicked except opening the app if its in the background. I have searched online, and trial & error for two days now to no avail. Any help would be appreciated.
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Alert, AsyncStorage, Button} from 'react-native';
import firebase from 'react-native-firebase';
type Props = {};
export default class testapp extends Component<Props> {
async componentDidMount() {
this.checkPermission();
this.createNotificationListeners(); //add this line
}
componentWillUnmount() {
this.notificationListener;
this.notificationOpenedListener;
}
//1
async checkPermission() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
this.getToken();
} else {
this.requestPermission();
}
}
//3
async getToken() {
let fcmToken = await AsyncStorage.getItem('fcmToken');
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
// user has a device token
console.log('fcmToken:', fcmToken);
await AsyncStorage.setItem('fcmToken', fcmToken);
}
}
console.log('fcmToken:', fcmToken);
}
//2
async requestPermission() {
try {
await firebase.messaging().requestPermission();
// User has authorised
this.getToken();
} catch (error) {
// User has rejected permissions
console.log('permission rejected');
}
}
async createNotificationListeners() {
/*
* Triggered when a particular notification has been received in foreground
* */
this.notificationListener = firebase.notifications().onNotification((notification) => {
const { title, body } = notification;
console.log('onNotification:');
const localNotification = new firebase.notifications.Notification({
sound: 'sampleaudio',
show_in_foreground: true,
})
.setSound('sampleaudio.wav')
.setNotificationId(notification.notificationId)
.setTitle(notification.title)
.setBody(notification.body)
.android.setChannelId('fcm_FirebaseNotifiction_default_channel') // e.g. the id you chose above
.android.setSmallIcon('#drawable/ic_launcher') // create this icon in Android Studio
.android.setColor('#000000') // you can set a color here
// .setClickAction(()=>alert('test'))
.android.setPriority(firebase.notifications.Android.Priority.High);
firebase.notifications()
.displayNotification(localNotification)
.catch(err => console.error(err));
});
const channel = new firebase.notifications.Android.Channel('fcm_FirebaseNotifiction_default_channel', 'UniFinalApp', firebase.notifications.Android.Importance.High)
.setDescription('Demo app description')
.setSound('sampleaudio.wav');
firebase.notifications().android.createChannel(channel);
/*
Code would probably go in the section below
* If your app is in background, you can listen for when a notification is clicked / tapped / opened as follows:
* */
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
const { title, body } = notificationOpen.notification;
console.log('onNotificationOpened:');
// Alert.alert(title, body)
});
/*
* If your app is closed, you can check if it was opened by a notification being clicked / tapped / opened as follows:
* */
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
const { title, body } = notificationOpen.notification;
console.log('getInitialNotification:');
Alert.alert(title, body)
}
/*
* Triggered for data only payload in foreground
* */
this.messageListener = firebase.messaging().onMessage((message) => {
//process data message
console.log("JSON.stringify:", JSON.stringify(message));
});
}
render() {
// const {navigate}=this.props.navigation;
return (
<View >
</View>
);
}
}
Inside the documentation for React-native-firebase; there's a section on how to handles data from notification. What you can do is to send data inside the notification that will tell the app what to do. For example:
firebase.notifications().getInitialNotification()
.then((notificationOpen: NotificationOpen) => {
if (notificationOpen) {
// App was opened by a notification
const notification: Notification = notificationOpen.notification;
const data = notificationOpen.notification._data;
if (data.action === 'openChat') {
//Code to open Chat screen
}
}
});
Or you can use the data from the notification to set some flags, and then once the user enters a certain screen (like after logged-in) checks the flags to see if certain action need to be done.
I am trying to get the notification like this but its also not working. This is working for background means when an app is close
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
const {_notificationId} = notificationOpen.notification.data
if (_notificationId) {
redirect('chatform', {id: _notificationId})
}
}