expo-notifications
Version:
Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.
201 lines (200 loc) • 10.5 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.withNotificationsAndroid = exports.withNotificationSounds = exports.withNotificationManifest = exports.withNotificationIconColor = exports.withNotificationIcons = exports.NOTIFICATION_ICON_COLOR_RESOURCE = exports.NOTIFICATION_ICON_COLOR = exports.NOTIFICATION_ICON_RESOURCE = exports.NOTIFICATION_ICON = exports.META_DATA_LOCAL_NOTIFICATION_ICON_COLOR = exports.META_DATA_LOCAL_NOTIFICATION_ICON = exports.META_DATA_FCM_NOTIFICATION_DEFAULT_CHANNEL_ID = exports.META_DATA_FCM_NOTIFICATION_ICON_COLOR = exports.META_DATA_FCM_NOTIFICATION_ICON = exports.dpiValues = exports.ANDROID_RES_PATH = void 0;
exports.getNotificationIcon = getNotificationIcon;
exports.getNotificationColor = getNotificationColor;
exports.setNotificationIconColor = setNotificationIconColor;
exports.setNotificationIconAsync = setNotificationIconAsync;
exports.setNotificationSounds = setNotificationSounds;
const image_utils_1 = require("@expo/image-utils");
const config_plugins_1 = require("expo/config-plugins");
const fs_1 = require("fs");
const path_1 = require("path");
const { Colors } = config_plugins_1.AndroidConfig;
exports.ANDROID_RES_PATH = 'android/app/src/main/res/';
exports.dpiValues = {
mdpi: { folderName: 'mipmap-mdpi', scale: 1 },
hdpi: { folderName: 'mipmap-hdpi', scale: 1.5 },
xhdpi: { folderName: 'mipmap-xhdpi', scale: 2 },
xxhdpi: { folderName: 'mipmap-xxhdpi', scale: 3 },
xxxhdpi: { folderName: 'mipmap-xxxhdpi', scale: 4 },
};
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow, removeMetaDataItemFromMainApplication, } = config_plugins_1.AndroidConfig.Manifest;
const BASELINE_PIXEL_SIZE = 24;
const ERROR_MSG_PREFIX = 'An error occurred while configuring Android notifications. ';
exports.META_DATA_FCM_NOTIFICATION_ICON = 'com.google.firebase.messaging.default_notification_icon';
exports.META_DATA_FCM_NOTIFICATION_ICON_COLOR = 'com.google.firebase.messaging.default_notification_color';
exports.META_DATA_FCM_NOTIFICATION_DEFAULT_CHANNEL_ID = 'com.google.firebase.messaging.default_notification_channel_id';
exports.META_DATA_LOCAL_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon';
exports.META_DATA_LOCAL_NOTIFICATION_ICON_COLOR = 'expo.modules.notifications.default_notification_color';
// TODO @vonovak add config for local notification large icon
// expo.modules.notifications.large_notification_icon
exports.NOTIFICATION_ICON = 'notification_icon';
exports.NOTIFICATION_ICON_RESOURCE = `@drawable/${exports.NOTIFICATION_ICON}`;
exports.NOTIFICATION_ICON_COLOR = 'notification_icon_color';
exports.NOTIFICATION_ICON_COLOR_RESOURCE = `@color/${exports.NOTIFICATION_ICON_COLOR}`;
const withNotificationIcons = (config, { icon }) => {
// If no icon provided in the config plugin props, fallback to value from app.json
icon = icon || getNotificationIcon(config);
return (0, config_plugins_1.withDangerousMod)(config, [
'android',
async (config) => {
await setNotificationIconAsync(config.modRequest.projectRoot, icon);
return config;
},
]);
};
exports.withNotificationIcons = withNotificationIcons;
const withNotificationIconColor = (config, { color }) => {
// If no color provided in the config plugin props, fallback to value from app.json
return (0, config_plugins_1.withAndroidColors)(config, (config) => {
color = color || getNotificationColor(config);
config.modResults = setNotificationIconColor(color, config.modResults);
return config;
});
};
exports.withNotificationIconColor = withNotificationIconColor;
const withNotificationManifest = (config, { icon, color, defaultChannel }) => {
// If no icon or color provided in the config plugin props, fallback to value from app.json
icon = icon || getNotificationIcon(config);
color = color || getNotificationColor(config);
defaultChannel = defaultChannel || null;
return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
config.modResults = setNotificationConfig({ icon, color, defaultChannel }, config.modResults);
return config;
});
};
exports.withNotificationManifest = withNotificationManifest;
const withNotificationSounds = (config, { sounds }) => {
return (0, config_plugins_1.withDangerousMod)(config, [
'android',
(config) => {
setNotificationSounds(config.modRequest.projectRoot, sounds);
return config;
},
]);
};
exports.withNotificationSounds = withNotificationSounds;
function getNotificationIcon(config) {
return config.notification?.icon || null;
}
function getNotificationColor(config) {
return config.notification?.color || null;
}
function setNotificationIconColor(color, colors) {
return Colors.assignColorValue(colors, {
name: exports.NOTIFICATION_ICON_COLOR,
value: color,
});
}
/**
* Applies notification icon configuration for expo-notifications
*/
async function setNotificationIconAsync(projectRoot, icon) {
if (icon) {
await writeNotificationIconImageFilesAsync(icon, projectRoot);
}
else {
removeNotificationIconImageFiles(projectRoot);
}
}
function setNotificationConfig(props, manifest) {
const mainApplication = getMainApplicationOrThrow(manifest);
if (props.icon) {
addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource');
addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_LOCAL_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource');
}
else {
removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_ICON);
removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_LOCAL_NOTIFICATION_ICON);
}
if (props.color) {
addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource');
addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_LOCAL_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource');
}
else {
removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_ICON_COLOR);
removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_LOCAL_NOTIFICATION_ICON_COLOR);
}
if (props.defaultChannel) {
addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_DEFAULT_CHANNEL_ID, props.defaultChannel, 'value');
}
else {
removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_FCM_NOTIFICATION_DEFAULT_CHANNEL_ID);
}
return manifest;
}
async function writeNotificationIconImageFilesAsync(icon, projectRoot) {
await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName, scale }) => {
const drawableFolderName = folderName.replace('mipmap', 'drawable');
const dpiFolderPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName);
if (!(0, fs_1.existsSync)(dpiFolderPath)) {
(0, fs_1.mkdirSync)(dpiFolderPath, { recursive: true });
}
const iconSizePx = BASELINE_PIXEL_SIZE * scale;
try {
const resizedIcon = (await (0, image_utils_1.generateImageAsync)({ projectRoot, cacheType: 'android-notification' }, {
src: icon,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
})).source;
(0, fs_1.writeFileSync)((0, path_1.resolve)(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'), resizedIcon);
}
catch (e) {
throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue resizing Android notification icon: ' + e);
}
}));
}
function removeNotificationIconImageFiles(projectRoot) {
Object.values(exports.dpiValues).forEach(async ({ folderName }) => {
const drawableFolderName = folderName.replace('mipmap', 'drawable');
const dpiFolderPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, drawableFolderName);
const iconFile = (0, path_1.resolve)(dpiFolderPath, exports.NOTIFICATION_ICON + '.png');
if ((0, fs_1.existsSync)(iconFile)) {
(0, fs_1.unlinkSync)(iconFile);
}
});
}
/**
* Save sound files to `<project-root>/android/app/src/main/res/raw`
*/
function setNotificationSounds(projectRoot, sounds) {
if (!Array.isArray(sounds)) {
throw new Error(ERROR_MSG_PREFIX +
`Must provide an array of sound files in your app config, found ${typeof sounds}.`);
}
for (const soundFileRelativePath of sounds) {
writeNotificationSoundFile(soundFileRelativePath, projectRoot);
}
}
/**
* Copies the input file to the `<project-root>/android/app/src/main/res/raw` directory if
* there isn't already an existing file under that name.
*/
function writeNotificationSoundFile(soundFileRelativePath, projectRoot) {
const rawResourcesPath = (0, path_1.resolve)(projectRoot, exports.ANDROID_RES_PATH, 'raw');
const inputFilename = (0, path_1.basename)(soundFileRelativePath);
if (inputFilename) {
try {
const sourceFilepath = (0, path_1.resolve)(projectRoot, soundFileRelativePath);
const destinationFilepath = (0, path_1.resolve)(rawResourcesPath, inputFilename);
if (!(0, fs_1.existsSync)(rawResourcesPath)) {
(0, fs_1.mkdirSync)(rawResourcesPath, { recursive: true });
}
(0, fs_1.copyFileSync)(sourceFilepath, destinationFilepath);
}
catch (e) {
throw new Error(ERROR_MSG_PREFIX + 'Encountered an issue copying Android notification sounds: ' + e);
}
}
}
const withNotificationsAndroid = (config, { icon = null, color = null, sounds = [], defaultChannel = null }) => {
config = (0, exports.withNotificationIconColor)(config, { color });
config = (0, exports.withNotificationIcons)(config, { icon });
config = (0, exports.withNotificationManifest)(config, { icon, color, defaultChannel });
config = (0, exports.withNotificationSounds)(config, { sounds });
return config;
};
exports.withNotificationsAndroid = withNotificationsAndroid;
;