diff --git a/.gitignore b/.gitignore index 8c499494b2c35bca18796c6332712af01789ae32..ca81c11f6f7463ea67276360e262747dfa2899df 100644 --- a/.gitignore +++ b/.gitignore @@ -189,4 +189,8 @@ package-lock.json .history android/.idea android/build + +# Typescript build +lib/dist/ + android/.gradle \ No newline at end of file diff --git a/example/index.ios.js b/example/index.ios.js index 009f41b24aee8ca75d9bd48f91a64cdf40f3dac0..a80704c7a6c6458776f621c53153250f40c9fff7 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -7,24 +7,24 @@ import { } from 'react-native'; import React, {Component} from 'react'; -import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications'; - -let upvoteAction = new NotificationAction({ - activationMode: 'background', - title: String.fromCodePoint(0x1F44D), - identifier: 'UPVOTE_ACTION' -}); - -let replyAction = new NotificationAction({ - activationMode: 'background', - title: 'Reply', - authenticationRequired: true, - textInput: { - buttonTitle: 'Reply now', - placeholder: 'Insert message' - }, - identifier: 'REPLY_ACTION' -}); +import { Notifications } from '../lib/dist/index'; + +// let upvoteAction = new NotificationAction({ +// activationMode: 'background', +// title: String.fromCodePoint(0x1F44D), +// identifier: 'UPVOTE_ACTION' +// }); + +// let replyAction = new NotificationAction({ +// activationMode: 'background', +// title: 'Reply', +// authenticationRequired: true, +// textInput: { +// buttonTitle: 'Reply now', +// placeholder: 'Insert message' +// }, +// identifier: 'REPLY_ACTION' +// }); class NotificationsExampleApp extends Component { @@ -34,23 +34,25 @@ class NotificationsExampleApp extends Component { notifications: [] }; - NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); - NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegisteredFailed.bind(this)); + Notifications.events().registerNotificationsReceived((notification) => { + alert(JSON.stringify(notification)); + }) + // NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); + // NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegisteredFailed.bind(this)); - NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); - NotificationsIOS.registerPushKit(); + // NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); + // NotificationsIOS.registerPushKit(); - NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); - NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); - NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this)); + // NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); + // NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); + // NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this)); } async componentDidMount() { - const initialNotification = await NotificationsIOS.getInitialNotification(); + const initialNotification = await Notifications.getInitialNotification(); if (initialNotification) { this.setState({notifications: [initialNotification.getData().link, ...this.state.notifications]}); } - } onPushRegistered(deviceToken) { @@ -109,15 +111,15 @@ class NotificationsExampleApp extends Component { } requestPermissions() { - let cat = new NotificationCategory({ - identifier: 'SOME_CATEGORY', - actions: [upvoteAction, replyAction] - }); - NotificationsIOS.requestPermissions([cat]); + // let cat = new NotificationCategory({ + // identifier: 'SOME_CATEGORY', + // actions: [upvoteAction, replyAction] + // }); + Notifications.requestPermissions(/*[cat]*/); } sendLocalNotification() { - NotificationsIOS.localNotification({ + Notifications.localNotification({ body: 'Local notificiation!', title: 'Local Notification Title', sound: 'chime.aiff', @@ -127,14 +129,14 @@ class NotificationsExampleApp extends Component { } removeAllDeliveredNotifications() { - NotificationsIOS.removeAllDeliveredNotifications(); + // NotificationsIOS.removeAllDeliveredNotifications(); } componentWillUnmount() { - NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); - NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this)); - NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); - NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); + // NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); + // NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this)); + // NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); + // NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); } } diff --git a/lib/src/Notifications.ts b/lib/src/Notifications.ts new file mode 100644 index 0000000000000000000000000000000000000000..6400ee85206fc1bfb8487c609c52ae940a4ea6ae --- /dev/null +++ b/lib/src/Notifications.ts @@ -0,0 +1,49 @@ +import { NativeCommandsSender } from './adapters/NativeCommandsSender'; +import { NativeEventsReceiver } from './adapters/NativeEventsReceiver'; +import { Commands } from './commands/Commands'; +import { EventsRegistry } from './events/EventsRegistry'; +import { Notification } from './interfaces/Notification'; + +export class NotificationsRoot { + private readonly nativeEventsReceiver: NativeEventsReceiver; + private readonly nativeCommandsSender: NativeCommandsSender; + private readonly commands: Commands; + private readonly eventsRegistry: EventsRegistry; + + constructor() { + this.nativeEventsReceiver = new NativeEventsReceiver(); + this.nativeCommandsSender = new NativeCommandsSender(); + this.commands = new Commands( + this.nativeCommandsSender + ); + this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver); + } + + /** + * Request permissions to send remote notifications - iOS only + */ + public requestPermissions(): Promise { + return this.commands.requestPermissions(); + } + + /** + * Reset the app to a new layout + */ + public localNotification(notification: Notification): Promise { + return this.commands.sendLocalNotification(notification); + } + + /** + * + */ + public getInitialNotification(): Promise { + return this.commands.getInitialNotification(); + } + + /** + * Obtain the events registry instance + */ + public events(): EventsRegistry { + return this.eventsRegistry; + } +} diff --git a/lib/src/adapters/NativeCommandsSender.ts b/lib/src/adapters/NativeCommandsSender.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee4d01e9ab56299e1e178631504c45821dca7fa1 --- /dev/null +++ b/lib/src/adapters/NativeCommandsSender.ts @@ -0,0 +1,27 @@ +import { NativeModules } from 'react-native'; +import { Notification } from '../interfaces/Notification'; + +interface NativeCommandsModule { + getInitialNotification(): Promise; + localNotification(notification: Notification, id: string): Promise; + requestPermissionsWithCategories(categories: any): Promise; +} + +export class NativeCommandsSender { + private readonly nativeCommandsModule: NativeCommandsModule; + constructor() { + this.nativeCommandsModule = NativeModules.RNBridgeModule; + } + + sendLocalNotification(notification: Notification, id: string) { + return this.nativeCommandsModule.localNotification(notification, id); + } + + getInitialNotification() { + return this.nativeCommandsModule.getInitialNotification(); + } + + requestPermissions() { + return this.nativeCommandsModule.requestPermissionsWithCategories([]); + } +} diff --git a/lib/src/adapters/NativeEventsReceiver.ts b/lib/src/adapters/NativeEventsReceiver.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7f8236b957c19df1d59e269572ba935092897a5 --- /dev/null +++ b/lib/src/adapters/NativeEventsReceiver.ts @@ -0,0 +1,19 @@ +import { NativeModules, NativeEventEmitter, EventEmitter, EmitterSubscription } from 'react-native'; +import { + NotificationRegisteredEvent, NotificationReceived +} from '../interfaces/NotificationEvents'; + +export class NativeEventsReceiver { + private emitter: EventEmitter; + constructor() { + this.emitter = new NativeEventEmitter(NativeModules.RNEventEmitter); + } + + public registerRemoteNotificationsRegistered(callback: (event: NotificationRegisteredEvent) => void): EmitterSubscription { + return this.emitter.addListener('remoteNotificationsRegistered', callback); + } + + public registerRemoteNotificationsReceived(callback: (event: NotificationReceived) => void): EmitterSubscription { + return this.emitter.addListener('notificationReceivedForeground', callback); + } +} diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ab6c702fd8796adfb8efd78cfaaf47867d40b29 --- /dev/null +++ b/lib/src/commands/Commands.ts @@ -0,0 +1,25 @@ +import * as _ from 'lodash'; +import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; +import { Notification } from '../interfaces/Notification'; + +export class Commands { + constructor( + private readonly nativeCommandsSender: NativeCommandsSender + ) {} + + public sendLocalNotification(notification: Notification) { + const notificationId = 'id'; + const result = this.nativeCommandsSender.sendLocalNotification(notification, notificationId); + return result; + } + + public getInitialNotification() { + const result = this.nativeCommandsSender.getInitialNotification(); + return result; + } + + public requestPermissions() { + const result = this.nativeCommandsSender.requestPermissions(); + return result; + } +} diff --git a/lib/src/events/EventsRegistry.ts b/lib/src/events/EventsRegistry.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe0998525509099c9f59b782880e6d3db2000164 --- /dev/null +++ b/lib/src/events/EventsRegistry.ts @@ -0,0 +1,18 @@ +import { EmitterSubscription } from 'react-native'; +import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver'; +import { + NotificationRegisteredEvent, + NotificationReceived +} from '../interfaces/NotificationEvents'; + +export class EventsRegistry { + constructor(private nativeEventsReceiver: NativeEventsReceiver) { } + + public registerRemoteNotificationsRegistered(callback: (event: NotificationRegisteredEvent) => void): EmitterSubscription { + return this.nativeEventsReceiver.registerRemoteNotificationsRegistered(callback); + } + + public registerNotificationsReceived(callback: (event: NotificationReceived) => void): EmitterSubscription { + return this.nativeEventsReceiver.registerRemoteNotificationsReceived(callback); + } +} diff --git a/lib/src/index.ts b/lib/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0826f6b25c526b6f4485eef3fc3c3bf4792ec02c --- /dev/null +++ b/lib/src/index.ts @@ -0,0 +1,8 @@ +import { NotificationsRoot } from './Notifications'; + +const notificationsSingleton = new NotificationsRoot(); + +export const Notifications = notificationsSingleton; +export * from './interfaces/EventSubscription'; +export * from './interfaces/Notification'; +export * from './interfaces/NotificationEvents'; diff --git a/lib/src/interfaces/EventSubscription.ts b/lib/src/interfaces/EventSubscription.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d8b49dbd53922d413c00c229528054aa52c52d3 --- /dev/null +++ b/lib/src/interfaces/EventSubscription.ts @@ -0,0 +1,3 @@ +export interface EventSubscription { + remove(): void; +} diff --git a/lib/src/interfaces/Notification.ts b/lib/src/interfaces/Notification.ts new file mode 100644 index 0000000000000000000000000000000000000000..45ee724c49cebb52bf717db5f3f0e451f05c73db --- /dev/null +++ b/lib/src/interfaces/Notification.ts @@ -0,0 +1,8 @@ +export interface Notification { + data: any; + alert: string + sound: string; + badge: number; + type: string; + thread: string; +} diff --git a/lib/src/interfaces/NotificationEvents.ts b/lib/src/interfaces/NotificationEvents.ts new file mode 100644 index 0000000000000000000000000000000000000000..53ce2ea55a3ac238097f5e717a7656495a8a555a --- /dev/null +++ b/lib/src/interfaces/NotificationEvents.ts @@ -0,0 +1,9 @@ +import { Notification } from './Notification'; + +export interface NotificationRegisteredEvent { + deviceToken: string; +} + +export interface NotificationReceived { + notification: Notification; +} diff --git a/metro.config.js b/metro.config.js index bf0faa48a6bc56386ed30d1b685e81670c83562b..91efffdd8d5f35b24c703d98b4ef86da0687a1a6 100644 --- a/metro.config.js +++ b/metro.config.js @@ -3,7 +3,11 @@ module.exports = { watchFolders: [ __dirname, ], + resolver: { + sourceExts: ['ts', 'tsx', 'js'] + }, transformer: { + babelTransformerPath: require.resolve('react-native-typescript-transformer'), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, diff --git a/package.json b/package.json index fcf5f62eb3a4eeea0d3b0104840ce68fb3689f97..5f4fa48a057316f692defc992458827aa098a4e9 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,11 @@ "actionable-notifications", "interactive-notifications" ], - "main": "lib/src/index", + "main": "lib/dist/index.js", + "typings": "lib/dist/index.d.ts", "scripts": { + "build": "rm -rf ./lib/dist && tsc", + "prestart": "npm run build", "pretest": "./node_modules/.bin/eslint *.js test", "test": "node scripts/test", "start": "node ./scripts/start", @@ -45,11 +48,13 @@ "@types/react-test-renderer": "16.x.x", "@babel/plugin-proposal-export-default-from": "7.2.0", "@babel/plugin-proposal-export-namespace-from": "7.2.0", + "@types/jest": "23.x.x", + "@types/lodash": "4.x.x", "typescript": "3.2.2", "babel-eslint": "9.0.0", "chai": "^3.5.0", "chokidar-cli": "^1.2.0", - "eslint": "6.0.1", + "tslint": "5.x.x", "mocha": "^2.5.3", "proxyquire": "^1.7.4", "sinon": "^1.17.3", @@ -60,7 +65,8 @@ "detox": "13.x.x", "jsc-android": "236355.x.x", "jest": "24.8.0", - "metro-react-native-babel-preset": "0.55.x" + "metro-react-native-babel-preset": "0.55.x", + "react-native-typescript-transformer": "1.2.12" }, "publishConfig": { "registry": "https://registry.npmjs.org/" diff --git a/tsconfig-strict.json b/tsconfig-strict.json new file mode 100644 index 0000000000000000000000000000000000000000..6e54709df9e37b2fa0b7fd6c601d4c0693ba8535 --- /dev/null +++ b/tsconfig-strict.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": false, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "diagnostics": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "strict": true + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..6a8f0eedd78dfd36b7895c56fe8cfc6e18d12f3b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig-strict.json", + "compilerOptions": { + "outDir": "./lib/dist", + "allowJs": false, + "target": "esnext", + "module": "commonjs", + "jsx": "react-native", + "declaration": true, + "skipLibCheck": true, + "types": [ + "jest", + "lodash", + "react", + "react-native", + "react-test-renderer" + ] + }, + "include": [ + "./lib/src/**/*" + ] +}