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

Implement Push Notifications #47

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open

Conversation

kikichan513
Copy link
Contributor

@kikichan513 kikichan513 commented Jul 31, 2020

Summary:
Added push notification using expo v.37. sdk push notifications. App is now connected to firebase to store the tokens.
Tokens are stored using timestamps for anonymity, and are not stored in the store for added security measures.

Backend flask app would send push notification to expo's frontend when admin clicks 'approve' 🍰

User Flow:

  1. User clicks 'approve' in flask app.

  2. Notification shows up in mobile:

IMG_8283

🌵 Message me for firebase secrets! 🐑

@kikichan513 kikichan513 changed the title Kiki/push notifications Implement Push Notifications Jul 31, 2020
.gitignore Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
Copy link
Contributor

@JacobJaffe JacobJaffe left a comment

Choose a reason for hiding this comment

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

We can't register a firebase session from secure keys on any user facing app. For a non-authenticated push notification flow, you have to set up an open endpoint that accepts the token, and have throttling mechanisms for preventing missuse

firebase.js Outdated Show resolved Hide resolved
App.js Outdated
const googleFontsLoaded = useFonts({
Roboto_400Regular,
Roboto_500Medium,
Roboto_700Bold,
Roboto_900Black,
});

useEffect(() => {
registerForPushNotificationsAsync();
Copy link
Contributor

Choose a reason for hiding this comment

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

This shouldn't happen on app mount, as this will trigger the push notification dialog immediately in app

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm.. i'll use a callback so it only renders initially

firebase.js Outdated Show resolved Hide resolved
finalStatus = status;
}
if (finalStatus !== 'granted') {
Alert.alert('Failed to get push token for push notification!');
Copy link
Contributor

Choose a reason for hiding this comment

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

Please localize all strings. Also, this will trigger every app mount, so this is only accounting for the happy path right now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, useTranslations() is a hook and this isn't a functional component, it's just a function that doesn't need to return anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how can i bypass?

Copy link
Contributor

Choose a reason for hiding this comment

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

you can access the i18n instance directly instead (config/i18n).

Copy link
Contributor

@JacobJaffe JacobJaffe left a comment

Choose a reason for hiding this comment

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

Looks l like this is still based on app mount logic -- we should make all notification logic based on notification UX states (threading through the on boarding permissions, so we only prompt the dialogue after users have accepted the first time, in our UI)

import * as Permissions from 'expo-permissions';
import Constants from 'expo-constants';

export const sendTokenToBackend = (_token) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
export const sendTokenToBackend = (_token) => {
const sendTokenToBackend = (_token) => {

token: _token,
}),
})
.then((response) => response.json())
Copy link
Contributor

Choose a reason for hiding this comment

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

These console logs should be removed / we should gracefully handle the failure case

// send post request to store token in backend
sendTokenToBackend(token);
} else {
Alert.alert('Must use physical device for Push Notifications');
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this the fallback? is there a specific case for this? Can this be guaranteed to be case exhaustive?

useEffect(() => {
if (!isInitialRender) {
setIsInitialRender(true);
RegisterForPushNotificationsAsync();
Copy link
Contributor

Choose a reason for hiding this comment

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

This probably shouldn't occur on app mount. This will force the push notification permission on first app launch, where we want this to occur after an explanation in onboarding.

Also:

  • if you're trying to make an effect happen once on mount, just remove the dependency chain ([] empty array for it)
  • if you want an effect to happen once, but conditionally, use a memo, so that this state logic doesn't affect renders
  • set properties from useState hook do not need to be included in dependency chains as the references are guaranteed to not mutate

finalStatus = status;
}
if (finalStatus !== 'granted') {
Alert.alert('Failed to get push token for push notification!');
Copy link
Contributor

Choose a reason for hiding this comment

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

you can access the i18n instance directly instead (config/i18n).

Comment on lines +60 to +67
// eslint-disable-next-line no-unused-vars
export const handleNotification = (_notification) => {
Vibration.vibrate();
};

export const notificationSubscription = Notifications.addListener(
handleNotification
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// eslint-disable-next-line no-unused-vars
export const handleNotification = (_notification) => {
Vibration.vibrate();
};
export const notificationSubscription = Notifications.addListener(
handleNotification
);
export const initNotificationSubscription = () => Notifications.addListener(Vibration.vibrate);

And initialize this at the same place as other initialization steps (like sentry, iin app.js), rather than having this occur implicitly by the file being loaded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants