Skip to content

Commit

Permalink
feat: setup notifications (#377)
Browse files Browse the repository at this point in the history
* feat: setup a notifications library

* feat: setup a notifications library
  • Loading branch information
tsyirvo authored Apr 16, 2024
1 parent c933f48 commit 416d75b
Show file tree
Hide file tree
Showing 21 changed files with 270 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
EAS_PROJECT_ID=""
APP_NAME="Dev"
EXPO_APPLE_TEAM_ID=""

API_URL=""

Expand All @@ -9,3 +10,4 @@ SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""
FLAGSMITH_KEY=""
MIXPANEL_TOKEN=""
ONE_SIGNAL_APP_ID=""
2 changes: 2 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
EAS_PROJECT_ID=""
APP_NAME="RN Starter"
EXPO_APPLE_TEAM_ID=""

API_URL=""

Expand All @@ -9,3 +10,4 @@ SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""
FLAGSMITH_KEY=""
MIXPANEL_TOKEN=""
ONE_SIGNAL_APP_ID=""
2 changes: 2 additions & 0 deletions .env.staging
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
EAS_PROJECT_ID=""
APP_NAME="Staging"
EXPO_APPLE_TEAM_ID=""

API_URL=""

Expand All @@ -9,3 +10,4 @@ SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""
FLAGSMITH_KEY=""
MIXPANEL_TOKEN=""
ONE_SIGNAL_APP_ID=""
1 change: 1 addition & 0 deletions __mocks__/expo-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
sentryDsn: undefined,
mixpanelToken: undefined,
flagsmithKey: undefined,
oneSignalAppId: undefined,
},
},
},
Expand Down
3 changes: 3 additions & 0 deletions __mocks__/expo-linking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
openSettings: jest.fn(),
};
3 changes: 3 additions & 0 deletions __mocks__/react-native-onesignal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
initialize: jest.fn(),
};
8 changes: 8 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { ExpoConfig, ConfigContext } from '@expo/config';
import { ClientEnv, Env } from './env';

const isProductionEnv = Env.APP_ENV === 'production';
const isDevelopmentEnv = Env.APP_ENV === 'development';

const plugins: ExpoConfig['plugins'] = [
[
Expand Down Expand Up @@ -59,6 +60,13 @@ const plugins: ExpoConfig['plugins'] = [
},
],
'expo-secure-store',
[
'onesignal-expo-plugin',
{
mode: isDevelopmentEnv ? 'development' : 'production',
devTeam: Env.EXPO_APPLE_TEAM_ID,
},
],
];

// eslint-disable-next-line import/no-default-export
Expand Down
6 changes: 6 additions & 0 deletions eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
"EXPO_NO_DOTENV": "1"
}
},
"development-device": {
"extends": "development",
"ios": {
"simulator": false
}
},
"test:debug": {
"extends": "development",
"android": {
Expand Down
4 changes: 4 additions & 0 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const client = z.object({
FLAGSMITH_KEY: z.string(),
MIXPANEL_TOKEN: z.string(),
SENTRY_DSN: z.string(),
ONE_SIGNAL_APP_ID: z.string(),
});

const buildTime = z.object({
Expand All @@ -51,6 +52,7 @@ const buildTime = z.object({
SENTRY_ORG: z.string(),
SENTRY_PROJECT: z.string(),
SENTRY_AUTH_TOKEN: z.string(),
EXPO_APPLE_TEAM_ID: z.string(),
});

// Environment variables config
Expand All @@ -67,6 +69,7 @@ const _clientEnv = {
FLAGSMITH_KEY: process.env.FLAGSMITH_KEY,
MIXPANEL_TOKEN: process.env.MIXPANEL_TOKEN,
SENTRY_DSN: process.env.SENTRY_DSN,
ONE_SIGNAL_APP_ID: process.env.ONE_SIGNAL_APP_ID,
};

const _buildTimeEnv = {
Expand All @@ -77,6 +80,7 @@ const _buildTimeEnv = {
SENTRY_ORG: process.env.SENTRY_ORG,
SENTRY_PROJECT: process.env.SENTRY_PROJECT,
SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN,
EXPO_APPLE_TEAM_ID: process.env.EXPO_APPLE_TEAM_ID,
};

const _env = {
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"//// BUILDS ////": "",
"build:dev:ios": "cross-env EXPO_NO_DOTENV=1 eas build --profile development --platform ios --local",
"build:dev:android": "cross-env EXPO_NO_DOTENV=1 eas build --profile development --platform android --local",
"build:dev-device:ios": "cross-env EXPO_NO_DOTENV=1 eas build --profile development-device --platform ios --local",
"build:dev-device:android": "cross-env EXPO_NO_DOTENV=1 eas build --profile development-device --platform android --local",
"build:staging:ios": "cross-env APP_ENV=staging eas build --profile staging --platform ios --local",
"build:staging:android": "cross-env APP_ENV=staging eas build --profile staging --platform android --local",
"build:production:ios": "cross-env APP_ENV=production eas build --profile production --platform ios --local",
Expand Down Expand Up @@ -90,6 +92,7 @@
"expo-device": "~5.9.3",
"expo-font": "~11.10.3",
"expo-image": "~1.10.6",
"expo-linking": "~6.2.2",
"expo-localization": "~14.8.3",
"expo-network": "~5.8.0",
"expo-secure-store": "~12.8.1",
Expand All @@ -103,6 +106,7 @@
"jwt-decode": "4.0.0",
"lodash": "4.17.21",
"mixpanel-react-native": "2.4.0",
"onesignal-expo-plugin": "2.0.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "4.0.13",
Expand All @@ -114,6 +118,7 @@
"react-native-gesture-handler": "~2.14.0",
"react-native-keyboard-controller": "1.11.6",
"react-native-mmkv": "2.11.0",
"react-native-onesignal": "5.1.1",
"react-native-reanimated": "~3.6.2",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
Expand Down
10 changes: 7 additions & 3 deletions src/core/bootstrapExternalSdks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Analytics } from './analytics';
import { initDateLocale } from './date';
import { getSupportedDateLocale } from './i18n';
import { ErrorMonitoring } from './monitoring';
import { Notifications } from './notifications';

const initAnalytics = () => {
Analytics.init().catch((error: unknown) => {
Expand All @@ -14,16 +15,19 @@ const initAnalytics = () => {
});
};

const initNotifications = () => {
Notifications.init();
};

const initDateLib = () => {
const localeToUse = getSupportedDateLocale();

initDateLocale(localeToUse);
};

export const bootstrapExternalSdks = () => {
initAnalytics();

ErrorMonitoring.init();

initAnalytics();
initNotifications();
initDateLib();
};
2 changes: 2 additions & 0 deletions src/core/constants/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const apiURL = Env.API_URL;
const sentryDsn = Env.SENTRY_DSN;
const mixpanelToken = Env.MIXPANEL_TOKEN;
const flagsmithKey = Env.FLAGSMITH_KEY;
const oneSignalAppId = Env.ONE_SIGNAL_APP_ID;

export const config = {
defaultLocale: 'en',
Expand All @@ -36,4 +37,5 @@ export const config = {
sentryDsn,
mixpanelToken,
flagsmithKey,
oneSignalAppId,
};
4 changes: 4 additions & 0 deletions src/core/i18n/resources/en/miscScreens.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,9 @@
"maintenanceMode": {
"description": "It will be available online as soon as possible",
"title": "The app is in maintenance"
},
"notifications": {
"cta": "Request",
"title": "Request Notification permission"
}
}
4 changes: 4 additions & 0 deletions src/core/i18n/resources/fr/miscScreens.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,9 @@
"maintenanceMode": {
"description": "Elle sera de nouveau fonctionnelle au plus vite",
"title": "L'app est en maintenance"
},
"notifications": {
"cta": "Demander",
"title": "Demander les permissions de Notification"
}
}
10 changes: 8 additions & 2 deletions src/core/i18n/utils/languageDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Localization from 'expo-localization';
import type { LanguageDetectorModule } from 'i18next';

import { config, storageKeys } from '$core/constants';
import { Notifications } from '$core/notifications';
import { AppStorage } from '$core/storage';

const getSelectedLocale = () =>
Expand All @@ -24,14 +25,19 @@ const detectLanguageToUse = () => {
const currentlySelectedLocale = getSelectedLocale();

if (currentlySelectedLocale) {
Notifications.setUserLanguage(currentlySelectedLocale);

return currentlySelectedLocale;
}

const phonePrimaryLocale = detectPhonePrimaryLocale();

setPhonePrimaryLocale(phonePrimaryLocale ?? config.defaultLocale);
const selectedLanguage = phonePrimaryLocale ?? config.defaultLocale;

Notifications.setUserLanguage(selectedLanguage);
setPhonePrimaryLocale(selectedLanguage);

return phonePrimaryLocale;
return selectedLanguage;
};

export const languageDetector: LanguageDetectorModule = {
Expand Down
1 change: 1 addition & 0 deletions src/core/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Notifications } from './notifications';
86 changes: 86 additions & 0 deletions src/core/notifications/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { OneSignal } from 'react-native-onesignal';

import { config } from '$core/constants';

class NotificationsClass {
/* ***** ***** Setup ***** ***** */

init() {
OneSignal.initialize(config.oneSignalAppId);

this.watchForNotificationPress();
}

/* ***** ***** User ***** ***** */

setUser(userId: string) {
OneSignal.login(userId);
}

removeUser() {
OneSignal.logout();
}

setUserEmail(email: string) {
OneSignal.User.addEmail(email);
}

removeUserEmail(email: string) {
OneSignal.User.removeEmail(email);
}

setUserLanguage(language: string) {
OneSignal.User.setLanguage(language);
}

addTag(key: string, value: string) {
OneSignal.User.addTag(key, value);
}

removeTag(key: string) {
OneSignal.User.removeTag(key);
}

/* ***** ***** Status ***** ***** */

optOut() {
OneSignal.User.pushSubscription.optOut();
}

optIn() {
OneSignal.User.pushSubscription.optIn();
}

/* ***** ***** Permission ***** ***** */

async checkPermissions() {
const isPermissionGranted =
await OneSignal.Notifications.getPermissionAsync();

return isPermissionGranted;
}

async canRequestPermission() {
const isRequestPossible =
await OneSignal.Notifications.canRequestPermission();

return isRequestPossible;
}

async requestPermissions() {
const isPermissionGranted =
await OneSignal.Notifications.requestPermission(false);

return isPermissionGranted;
}

/* ***** ***** Listeners ***** ***** */

watchForNotificationPress() {
OneSignal.Notifications.addEventListener('click', (event) => {
console.log('OneSignal: notification clicked:', event);
});
}
}

export const Notifications = new NotificationsClass();
52 changes: 52 additions & 0 deletions src/features/notifications/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as Linking from 'expo-linking';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Alert } from 'react-native';

import { Logger } from '$core/logger';
import { Notifications as NotificationsHandler } from '$core/notifications';
import { Button } from '$shared/uiKit/button';
import { Box, Text } from '$shared/uiKit/primitives';

export const Notifications = () => {
const { t } = useTranslation('miscScreens');

const requestNotificationPermission = async () => {
const canRequestPermission =
await NotificationsHandler.canRequestPermission();
const isPermissionAlreadyGranted =
await NotificationsHandler.checkPermissions();

if (!canRequestPermission && !isPermissionAlreadyGranted) {
Linking.openSettings().catch((error: unknown) => {
Logger.error({
error,
level: 'warning',
message: 'Failed to open settings',
});
});

return;
}

if (isPermissionAlreadyGranted) {
Alert.alert('Permission already granted');

return;
}

await NotificationsHandler.requestPermissions();
};

return (
<>
<Text variant="large">{t('notifications.title')}</Text>

<Box alignItems="flex-start" mt="spacing_8">
<Button.Text onPress={requestNotificationPermission}>
{t('notifications.cta')}
</Button.Text>
</Box>
</>
);
};
1 change: 1 addition & 0 deletions src/features/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Notifications } from './Notifications';
Loading

0 comments on commit 416d75b

Please sign in to comment.