Commit 8ef5e0d2 authored by Bess's avatar Bess Committed by GitHub

Merge pull request #1 from wix/v3

V3 merge
parents 77f672cd 6fe705ff
*/node_modules
*.log
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"parser": "babel-eslint", "parser": "babel-eslint",
"env": { "env": {
"node": true, "node": true,
"mocha": true, "jest": true,
"es6": true "es6": true
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"no-trailing-spaces": "error", "no-trailing-spaces": "error",
"quotes": [ "quotes": [
"error", "error",
"double" "single"
], ],
"semi": [ "semi": [
"error", "error",
......
workflow "Verify labels" {
on = "pull_request"
resolves = "Enforce PR label"
}
action "Enforce PR label" {
uses = "yogevbd/enforce-label-action@1.0.0"
secrets = ["GITHUB_TOKEN"]
env = {
VALID_LABELS = "bug,enhancement,feature,skip-changelog"
}
}
\ No newline at end of file
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- "type: accepted/enhancement"
- "user: looking for contributors"
- "📌 pinned"
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: "🏚 stale"
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
The issue has been closed for inactivity.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
pulls:
daysUntilStale: 45
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# issues:
# exemptLabels:
# - confirmed
# Configuration for support-requests - https://github.com/dessant/support-requests
# Label used to mark issues as support requests
supportLabel: "type: question/stack overflow"
# Comment to post on issues marked as support requests. Add a link
# to a support page, or set to `false` to disable
supportComment: >
We use the issue tracker exclusively for bug reports and feature requests.
This issue appears to be a general usage or support question.
Instead, please ask a question on Stack Overflow with the `react-native-notifications` tag.
# Whether to close issues marked as support requests
close: true
# Whether to lock issues marked as support requests
lock: false
...@@ -15,8 +15,12 @@ npm-debug.log ...@@ -15,8 +15,12 @@ npm-debug.log
##### #####
# Android # Android
android/local.properties lib/android/local.properties
android/*.iml lib/android/*.iml
lib/android/.idea
lib/android/build
lib/android/.gradle
*.gradle
##### #####
# OS X temporary files that should never be committed # OS X temporary files that should never be committed
...@@ -178,3 +182,15 @@ jspm_packages ...@@ -178,3 +182,15 @@ jspm_packages
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Intellij
*.iml
coverage/
package-lock.json
.history
# Typescript build
lib/dist/
module.exports = {
template: {
commit: ({message, url, author, name}) => `- [${message}](${url}) - ${author ? `@${author}` : name}`,
issue: "- {{name}} [{{text}}]({{url}})",
label: "[**{{label}}**]",
noLabel: "closed",
group: "\n#### {{heading}}\n",
changelogTitle: "# Changelog\n\n",
release: "## {{release}} ({{date}})\n{{body}}",
releaseSeparator: "\n---\n\n"
},
groupBy: {
"Enhancements:": ["enhancement", "internal"],
"Bug Fixes:": ["bug"],
"Features": ["feature"]
},
ignoreIssuesWith: [
"skip-changelog"
],
ignoreTagsWith: [
"snapshot"
],
dataSource: "prs",
changelogFilename: "CHANGELOG.gren.md",
tags: "all",
override: true,
generate: true
}
\ No newline at end of file
example/ example/
test/ test/
RNNotifications/DerivedData
node_modules/ node_modules/
website/
docs/
docs_old/
scripts/
e2e/
.eslintrc .eslintrc
*.yml *.yml
coverage/
.history
android/.idea
android/build/
.idea
.history/
.github/
\ No newline at end of file
language: node_js language: node_js
osx_image: xcode10.1
node_js: node_js:
- "7" - "10"
# Changelog
## 3.0.0-beta.1 (07/10/2019)
*No changelog for this release.*
---
## 3.0.0-beta.0 (02/10/2019)
#### Bug Fixes:
- Remove vibrate permissions maxSdkVersion, fix example project on android [#388](https://github.com/wix/react-native-notifications/pull/388)
---
## 2.1.3 (22/09/2019)
#### Enhancements:
- Autogenerate changelog using github-release-notes [#382](https://github.com/wix/react-native-notifications/pull/382)
#### Bug Fixes:
- Fixing white square icon and annotation error [#379](https://github.com/wix/react-native-notifications/pull/379)
---
## 3.0.0-alpha.0 (15/09/2019)
*No changelog for this release.*
---
## 2.1.0 (14/09/2019)
#### Enhancements:
- React Native 0.60 Support [#375](https://github.com/wix/react-native-notifications/pull/375)
- New api changes ios [#342](https://github.com/wix/react-native-notifications/pull/342)
---
## v1.4.0 (18/04/2019)
*No changelog for this release.*
---
## v1.1.17 (26/10/2017)
*No changelog for this release.*
---
## v1.1.12 (13/07/2017)
*No changelog for this release.*
# Changelog
# 2.1.0
## Added
* react-native 0.60 Support
### Breaking Change
This version requires an additional installation step in order to identify the correct build flavor on android, as described in our [Installation doc](https://github.com/wix/react-native-notifications/blob/master/docs/installation.md#step-5-rnnotifications-and-react-native-version).
# 2.0.6
## Fixed
### Android
* Resolve intent by extra key and not by `google.message_id` string, Addresses #296. [#5056657](https://github.com/wix/react-native-notifications/pull/358/commits/5056657a6b3041b0c272357afcded42e59b83433) by [yogevbd](https://github.com/yogevbd)
# 2.0.5
## Fixed
### Android
* Reverted #349, This caused our e2e tests to fail, therefor we reverted this PR and will resolve this issue in the next version [#0b70828](https://github.com/wix/react-native-notifications/pull/357/commits/0b70828ca3e1f4e00817a32d6327381b4605c75c) by [yogevbd](https://github.com/yogevbd)
# 2.0.4
## Fixed
* Fix missing badge in silent notification payload with no aps.alert [#21c684d](https://github.com/wix/react-native-notifications/commit/21c684dbb7f632644747fa884c1b3f2bfd87f0a5) by [yogevbd](https://github.com/yogevbd)
Moved our builds to CircleCI and added iOS unit and e2e tests coverage.
# 2.0.3
## Fixed
### Android
* Remove verify notification [#57190f7](https://github.com/wix/react-native-notifications/commit/57190f7ed239022da28f62cb6e4d04e5fd5d48d1) by [yogevbd](https://github.com/yogevbd)
* Bring native unit tests back to life [#11f370b](https://github.com/wix/react-native-notifications/commit/11f370b380c4f9dd0365cc4866114722fa70a393) by [yogevbd](https://github.com/yogevbd)
## Fixed
### iOS
* Fix iOS build on RN0.59 [#b90ea92](https://github.com/wix/react-native-notifications/commit/b90ea920b195a80bc218e15f58222af1701bf79f) by [lionerez1](https://github.com/lionerez1)
### Breaking change
* Updated the android [installation setup guide](https://github.com/wix/react-native-notifications/blob/master/docs/installation.md).
Make sure settings gradle imports from `'../node_modules/react-native-notifications/android/app'` and not `'../node_modules/react-native-notifications/android'`
```gradle
include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
```
* @yogevbd
FROM node:8.11.4
WORKDIR /app/website
EXPOSE 3000 35729
COPY ./docs /app/docs
COPY ./website /app/website
RUN yarn install
CMD ["yarn", "start"]
# React Native Notifications [![Build Status](https://travis-ci.org/wix/react-native-notifications.svg)](https://travis-ci.org/wix/react-native-notifications) # React Native Notifications
![npm](https://img.shields.io/npm/dw/react-native-notifications.svg)
[![Build Status](https://img.shields.io/jenkins/s/http/jenkins-oss.wixpress.com:8080/job/multi-react-native-notifications-master.svg)](https://jenkins-oss.wixpress.com/job/multi-react-native-notifications-master/)
[![npm (tag)](https://img.shields.io/npm/v/react-native-notifications/snapshot.svg)](https://github.com/wix/react-native-navigation/tree/master)
Handle all the aspects of push notifications for your app, including remote and local notifications, interactive notifications, silent notifications, and more. Handle all the aspects of push notifications for your app, including remote and local notifications, interactive notifications, silent notifications, and more.
...@@ -27,14 +30,8 @@ _For information regarding proper integration with [react-native-navigation](htt ...@@ -27,14 +30,8 @@ _For information regarding proper integration with [react-native-navigation](htt
_Upcoming: local notifications, background-state Rx queue (iOS equivalent)_ _Upcoming: local notifications, background-state Rx queue (iOS equivalent)_
# Table of Content # Quick Links
- [Documentation](https://wix.github.io/react-native-notifications/)
- [Installation and setup](./docs/installation.md) - Setting up the library in your app
- [Subscription](./docs/subscription.md) - Signing in to push notifications vendors (e.g. GCM)
- [Notification Events (notfications core)](./docs/notificationsEvents.md) - Handling push notification arrival, notification opening by users
- [Local notifications](./docs/localNotifications.md) - Manually triggering notifications (i.e. not via push)
- [Advanced iOS topics](./docs/advancedIos.md) - e.g. managed notifications, PushKit API, Notifications actions
- [Notifications layout control - Android (wiki page)](https://github.com/wix/react-native-notifications/wiki/Android:-Layout-Customization) - Learn how to fully customize your notifications layout on Android!
# License # License
The MIT License. The MIT License.
......
@import UIKit;
#import <React/RCTBridgeModule.h>
#import <PushKit/PushKit.h>
@interface RNNotifications : NSObject <RCTBridgeModule>
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken;
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type;
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification;
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification;
+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler;
+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler;
@end
#import <UIKit/UIKit.h>
#import <PushKit/PushKit.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import "RNNotifications.h"
#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
#import "RNNotificationsBridgeQueue.h"
#import <UserNotifications/UserNotifications.h>
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
NSString* const RNNotificationCreateAction = @"CREATE";
NSString* const RNNotificationClearAction = @"CLEAR";
NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered";
NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed";
NSString* const RNPushKitRegistered = @"RNPushKitRegistered";
NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground";
NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground";
NSString* const RNNotificationOpened = @"RNNotificationOpened";
NSString* const RNNotificationActionTriggered = @"RNNotificationActionTriggered";
/*
* Converters for Interactive Notifications
*/
@implementation RCTConvert (UIUserNotificationActivationMode)
RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
@"foreground": @(UIUserNotificationActivationModeForeground),
@"background": @(UIUserNotificationActivationModeBackground)
}), UIUserNotificationActivationModeForeground, integerValue)
@end
@implementation RCTConvert (UIUserNotificationActionContext)
RCT_ENUM_CONVERTER(UIUserNotificationActionContext, (@{
@"default": @(UIUserNotificationActionContextDefault),
@"minimal": @(UIUserNotificationActionContextMinimal)
}), UIUserNotificationActionContextDefault, integerValue)
@end
@implementation RCTConvert (UIUserNotificationActionBehavior)
/* iOS 9 only */
RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
@"default": @(UIUserNotificationActionBehaviorDefault),
@"textInput": @(UIUserNotificationActionBehaviorTextInput)
}), UIUserNotificationActionBehaviorDefault, integerValue)
@end
@implementation RCTConvert (UIMutableUserNotificationAction)
+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new];
action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]];
action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]];
action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]];
action.destructive = [RCTConvert BOOL:details[@"destructive"]];
action.title = [RCTConvert NSString:details[@"title"]];
action.identifier = [RCTConvert NSString:details[@"identifier"]];
return action;
}
@end
@implementation RCTConvert (UIMutableUserNotificationCategory)
+ (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)json
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new];
category.identifier = details[@"identifier"];
// category actions
NSMutableArray* actions = [NSMutableArray new];
for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) {
[actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]];
}
[category setActions:actions forContext:[RCTConvert UIUserNotificationActionContext:details[@"context"]]];
return category;
}
@end
@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UILocalNotification* notification = [UILocalNotification new];
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]];
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]];
notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
if ([RCTConvert BOOL:details[@"silent"]]) {
notification.soundName = nil;
}
notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
notification.category = [RCTConvert NSString:details[@"category"]];
return notification;
}
@end
@implementation RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
content.body = [RCTConvert NSString:details[@"alertBody"]];
content.title = [RCTConvert NSString:details[@"alertTitle"]];
content.sound = [RCTConvert NSString:details[@"soundName"]]
? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]]
: [UNNotificationSound defaultSound];
if ([RCTConvert BOOL:details[@"silent"]]) {
content.sound = nil;
}
content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
UNCalendarNotificationTrigger *trigger = nil;
if (triggerDate != nil) {
NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
components:NSCalendarUnitYear +
NSCalendarUnitMonth + NSCalendarUnitDay +
NSCalendarUnitHour + NSCalendarUnitMinute +
NSCalendarUnitSecond + NSCalendarUnitTimeZone
fromDate:triggerDate];
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
repeats:NO];
}
return [UNNotificationRequest requestWithIdentifier:notificationId
content:content trigger:trigger];
}
@end
static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
{
NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
UNNotificationContent *content = notification.request.content;
formattedNotification[@"identifier"] = notification.request.identifier;
if (notification.date) {
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
NSString *dateString = [formatter stringFromDate:notification.date];
formattedNotification[@"fireDate"] = dateString;
}
formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title);
formattedNotification[@"alertBody"] = RCTNullIfNil(content.body);
formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier);
formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo));
return formattedNotification;
}
@implementation RNNotifications
RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationsRegistered:)
name:RNNotificationsRegistered
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationsRegistrationFailed:)
name:RNNotificationsRegistrationFailed
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handlePushKitRegistered:)
name:RNPushKitRegistered
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationReceivedForeground:)
name:RNNotificationReceivedForeground
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationReceivedBackground:)
name:RNNotificationReceivedBackground
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationOpened:)
name:RNNotificationOpened
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationActionTriggered:)
name:RNNotificationActionTriggered
object:nil];
[RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
[RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil;
}
/*
* Public Methods
*/
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) {
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken
{
NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken];
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered
object:self
userInfo:@{@"deviceToken": tokenRepresentation}];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed
object:self
userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
}
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
{
UIApplicationState state = [UIApplication sharedApplication].applicationState;
if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
// JS thread is ready, push the notification to the bridge
if (state == UIApplicationStateActive) {
// Notification received foreground
[self didReceiveNotificationOnForegroundState:notification];
} else if (state == UIApplicationStateInactive) {
// Notification opened
[self didNotificationOpen:notification];
} else {
// Notification received background
[self didReceiveNotificationOnBackgroundState:notification];
}
} else {
// JS thread is not ready - store it in the native notifications queue
[[RNNotificationsBridgeQueue sharedInstance] postNotification:notification];
}
}
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
{
UIApplicationState state = [UIApplication sharedApplication].applicationState;
NSMutableDictionary* newUserInfo = notification.userInfo.mutableCopy;
[newUserInfo removeObjectForKey:@"__id"];
notification.userInfo = newUserInfo;
if (state == UIApplicationStateActive) {
[self didReceiveNotificationOnForegroundState:notification.userInfo];
} else if (state == UIApplicationStateInactive) {
NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"];
if (notificationId) {
[self clearNotificationFromNotificationsCenter:notificationId];
}
[self didNotificationOpen:notification.userInfo];
}
}
+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler];
}
+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler];
}
/*
* Notification handlers
*/
+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground
object:self
userInfo:notification];
}
+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification
{
NSDictionary* managedAps = [notification objectForKey:@"managedAps"];
NSDictionary* alert = [managedAps objectForKey:@"alert"];
NSString* action = [managedAps objectForKey:@"action"];
NSString* notificationId = [managedAps objectForKey:@"notificationId"];
if (action) {
// create or delete notification
if ([action isEqualToString: RNNotificationCreateAction]
&& notificationId
&& alert) {
[self dispatchLocalNotificationFromNotification:notification];
} else if ([action isEqualToString: RNNotificationClearAction] && notificationId) {
[self clearNotificationFromNotificationsCenter:notificationId];
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground
object:self
userInfo:notification];
}
+ (void)didNotificationOpen:(NSDictionary *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened
object:self
userInfo:notification];
}
/*
* Helper methods
*/
+ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification
{
NSDictionary* managedAps = [notification objectForKey:@"managedAps"];
NSDictionary* alert = [managedAps objectForKey:@"alert"];
NSString* action = [managedAps objectForKey:@"action"];
NSString* notificationId = [managedAps objectForKey:@"notificationId"];
if ([action isEqualToString: RNNotificationCreateAction]
&& notificationId
&& alert) {
// trigger new client push notification
UILocalNotification* note = [UILocalNotification new];
note.alertTitle = [alert objectForKey:@"title"];
note.alertBody = [alert objectForKey:@"body"];
note.userInfo = notification;
note.soundName = [managedAps objectForKey:@"sound"];
note.category = [managedAps objectForKey:@"category"];
[[UIApplication sharedApplication] presentLocalNotificationNow:note];
// Serialize it and store so we can delete it later
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:note];
NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:notificationKey];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"Local notification was triggered: %@", notificationKey);
}
}
+ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId
{
NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey];
if (data) {
UILocalNotification* notification = [NSKeyedUnarchiver unarchiveObjectWithData: data];
// delete the notification
[[UIApplication sharedApplication] cancelLocalNotification:notification];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey];
NSLog(@"Local notification removed: %@", notificationKey);
return;
}
}
+ (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId
{
return [NSString stringWithFormat:@"%@.%@", [[NSBundle mainBundle] bundleIdentifier], notificationId];
}
+ (NSString *)deviceTokenToString:(NSData *)deviceToken
{
NSMutableString *result = [NSMutableString string];
NSUInteger deviceTokenLength = deviceToken.length;
const unsigned char *bytes = deviceToken.bytes;
for (NSUInteger i = 0; i < deviceTokenLength; i++) {
[result appendFormat:@"%02x", bytes[i]];
}
return [result copy];
}
+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories
{
UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler
{
NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]];
NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }];
// add text
NSString* text = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey];
if (text != NULL) {
info[@"text"] = text;
}
// add notification custom data
if (userInfo != NULL) {
info[@"notification"] = userInfo;
}
// Emit event to the queue (in order to store the completion handler). if JS thread is ready, post it also to the notification center (to the bridge).
[[RNNotificationsBridgeQueue sharedInstance] postAction:info withCompletionKey:completionKey andCompletionHandler:completionHandler];
if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
object:self
userInfo:info];
}
}
+ (void)registerPushKit
{
// Create a push registry object
PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
// Set the registry delegate to app delegate
pushKitRegistry.delegate = [[UIApplication sharedApplication] delegate];
pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
[[NSNotificationCenter defaultCenter] postNotificationName:RNPushKitRegistered
object:self
userInfo:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
[RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}
/*
* Javascript events
*/
- (void)handleNotificationsRegistered:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
}
- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo];
}
- (void)handlePushKitRegistered:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo];
}
- (void)handleNotificationReceivedForeground:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedForeground" body:notification.userInfo];
}
- (void)handleNotificationReceivedBackground:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedBackground" body:notification.userInfo];
}
- (void)handleNotificationOpened:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"notificationOpened" body:notification.userInfo];
}
- (void)handleNotificationActionTriggered:(NSNotification *)notification
{
[_bridge.eventDispatcher sendAppEventWithName:@"notificationActionReceived" body:notification.userInfo];
}
/*
* React Native exported methods
*/
RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json)
{
NSMutableSet* categories = nil;
if ([json count] > 0) {
categories = [NSMutableSet new];
for (NSDictionary* categoryJson in json) {
[categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]];
}
}
[RNNotifications requestPermissionsWithCategories:categories];
}
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
NSDictionary * notification = nil;
notification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification ?
[RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification :
[RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
[RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
[RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
resolve(notification);
}
RCT_EXPORT_METHOD(log:(NSString *)message)
{
NSLog(message);
}
RCT_EXPORT_METHOD(completionHandler:(NSString *)completionKey)
{
[[RNNotificationsBridgeQueue sharedInstance] completeAction:completionKey];
}
RCT_EXPORT_METHOD(abandonPermissions)
{
[[UIApplication sharedApplication] unregisterForRemoteNotifications];
}
RCT_EXPORT_METHOD(registerPushKit)
{
[RNNotifications registerPushKit];
}
RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback)
{
NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber;
callback(@[ [NSNumber numberWithInteger:count] ]);
}
RCT_EXPORT_METHOD(setBadgesCount:(int)count)
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback)
{
NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining;
callback(@[ [NSNumber numberWithDouble:remainingTime] ]);
}
RCT_EXPORT_METHOD(consumeBackgroundQueue)
{
// Mark JS Thread as ready
[RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES;
// Push actions to JS
[[RNNotificationsBridgeQueue sharedInstance] consumeActionsQueue:^(NSDictionary* action) {
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
object:self
userInfo:action];
}];
// Push background notifications to JS
[[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) {
[RNNotifications didReceiveNotificationOnBackgroundState:notification];
}];
// Push opened local notifications
NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
if (openedLocalNotification) {
[RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
[RNNotifications didNotificationOpen:openedLocalNotification];
}
// Push opened remote notifications
NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification;
if (openedRemoteNotification) {
[RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
[RNNotifications didNotificationOpen:openedRemoteNotification];
}
}
RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId)
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
} else {
UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
[userInfo setValue:notificationId forKey:@"__id"];
localNotification.userInfo = userInfo;
if ([notification objectForKey:@"fireDate"] != nil) {
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
} else {
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
}
}
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId)
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
} else {
for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
NSDictionary* notificationInfo = notification.userInfo;
if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
}
RCT_EXPORT_METHOD(cancelAllLocalNotifications)
{
[RCTSharedApplication() cancelAllLocalNotifications];
}
RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
BOOL ans;
if (TARGET_IPHONE_SIMULATOR) {
ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0;
}
else {
ans = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
}
resolve(@(ans));
}
RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve
reject:(RCTPromiseRejectBlock) reject) {
UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
resolve(@{
@"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0),
@"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0),
@"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0),
});
}
#if !TARGET_OS_TV
RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeAllDeliveredNotifications];
}
}
RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeDeliveredNotificationsWithIdentifiers:identifiers];
}
}
RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];
for (UNNotification *notification in notifications) {
[formattedNotifications addObject:RCTFormatUNNotification(notification)];
}
callback(@[formattedNotifications]);
}];
}
}
#endif !TARGET_OS_TV
@end
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */; };
D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; };
D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsBridgeQueue.m; sourceTree = "<group>"; };
D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsBridgeQueue.h; sourceTree = "<group>"; };
D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotifications.m; sourceTree = "<group>"; };
D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotifications.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRNNotifications.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */,
D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */,
D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */,
D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RNNotifications */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNNotifications;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRNNotifications.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RNNotifications */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */,
D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../example/node_modules/react-native/React/**",
"$(SRCROOT)/../../react-native/React/**",
"$(SRCROOT)/../../../React/**",
);
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNNotifications;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../example/node_modules/react-native/React/**",
"$(SRCROOT)/../../react-native/React/**",
"$(SRCROOT)/../../../React/**",
);
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNNotifications;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
#import <Foundation/Foundation.h>
@interface RNNotificationsBridgeQueue : NSObject
@property BOOL jsIsReady;
@property NSDictionary* openedRemoteNotification;
@property NSDictionary* openedLocalNotification;
+ (nonnull instancetype)sharedInstance;
- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler;
- (void)postNotification:(NSDictionary *)notification;
- (void)consumeActionsQueue:(void (^)(NSDictionary *))block;
- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block;
- (void)completeAction:(NSString *)completionKey;
@end
\ No newline at end of file
#import "RNNotificationsBridgeQueue.h"
@implementation RNNotificationsBridgeQueue
NSMutableArray<NSDictionary *>* actionsQueue;
NSMutableArray<NSDictionary *>* notificationsQueue;
NSMutableDictionary* actionCompletionHandlers;
+ (nonnull instancetype)sharedInstance {
static RNNotificationsBridgeQueue* sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}
- (instancetype)init
{
actionsQueue = [NSMutableArray new];
notificationsQueue = [NSMutableArray new];
actionCompletionHandlers = [NSMutableDictionary new];
self.jsIsReady = NO;
return self;
}
- (void)postNotification:(NSDictionary *)notification
{
if (!notificationsQueue) return;
[notificationsQueue insertObject:notification atIndex:0];
}
- (NSDictionary *)dequeueSingleNotification
{
if (!notificationsQueue || notificationsQueue.count == 0) return nil;
NSDictionary* notification = [notificationsQueue lastObject];
[notificationsQueue removeLastObject];
return notification;
}
- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block
{
NSDictionary* notification;
while ((notification = [self dequeueSingleNotification]) != nil) {
block(notification);
}
notificationsQueue = nil;
}
- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler
{
// store completion handler
actionCompletionHandlers[completionKey] = completionHandler;
if (!actionsQueue) return;
[actionsQueue insertObject:action atIndex:0];
}
- (NSDictionary *)dequeueSingleAction
{
if (!actionsQueue || actionsQueue.count == 0) return nil;
NSDictionary* action = [actionsQueue lastObject];
[actionsQueue removeLastObject];
return action;
}
- (void)consumeActionsQueue:(void (^)(NSDictionary *))block
{
NSDictionary* lastActionInfo;
while ((lastActionInfo = [self dequeueSingleAction]) != nil) {
block(lastActionInfo);
}
actionsQueue = nil;
}
- (void)completeAction:(NSString *)completionKey
{
void (^completionHandler)() = (void (^)())[actionCompletionHandlers valueForKey:completionKey];
if (completionHandler) {
completionHandler();
[actionCompletionHandlers removeObjectForKey:completionKey];
}
}
@end
\ No newline at end of file
package com.wix.reactnativenotifications;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}
package com.wix.reactnativenotifications;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
// Google's GCM.
compile 'com.google.android.gms:play-services-gcm:15.0.1'
compile 'com.facebook.react:react-native:+'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.+'
testCompile 'org.robolectric:robolectric:3.1.4'
}
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":reactnativenotification" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":reactnativenotification" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
<option name="LIBRARY_PROJECT" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotations" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/24.0.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/drawee/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/fbcore/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/fresco/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/imagepipeline-base/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/imagepipeline-okhttp3/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.fresco/imagepipeline/0.11.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.react/react-native/0.34.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.facebook.soloader/soloader/0.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-base/9.6.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-basement/9.6.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-gcm/9.6.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-iid/9.6.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-tasks/9.6.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/org.webkit/android-jsc/r174650/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/typedefs.txt" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="okio-1.9.0" level="project" />
<orderEntry type="library" exported="" name="play-services-base-9.6.1" level="project" />
<orderEntry type="library" exported="" name="fresco-0.11.0" level="project" />
<orderEntry type="library" exported="" name="drawee-0.11.0" level="project" />
<orderEntry type="library" exported="" name="jsr305-3.0.0" level="project" />
<orderEntry type="library" exported="" name="support-v4-24.0.0" level="project" />
<orderEntry type="library" exported="" name="bolts-tasks-1.4.0" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-23.0.1" level="project" />
<orderEntry type="library" exported="" name="okhttp-urlconnection-3.4.1" level="project" />
<orderEntry type="library" exported="" name="android-jsc-r174650" level="project" />
<orderEntry type="library" exported="" name="react-native-0.34.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.0.1" level="project" />
<orderEntry type="library" exported="" name="fbcore-0.11.0" level="project" />
<orderEntry type="library" exported="" name="imagepipeline-0.11.0" level="project" />
<orderEntry type="library" exported="" name="library-2.4.0" level="project" />
<orderEntry type="library" exported="" name="play-services-iid-9.6.1" level="project" />
<orderEntry type="library" exported="" name="imagepipeline-base-0.11.0" level="project" />
<orderEntry type="library" exported="" name="soloader-0.1.0" level="project" />
<orderEntry type="library" exported="" name="javax.inject-1" level="project" />
<orderEntry type="library" exported="" name="jackson-core-2.2.3" level="project" />
<orderEntry type="library" exported="" name="support-annotations-24.0.0" level="project" />
<orderEntry type="library" exported="" name="play-services-tasks-9.6.1" level="project" />
<orderEntry type="library" exported="" name="play-services-basement-9.6.1" level="project" />
<orderEntry type="library" exported="" name="okhttp-ws-3.4.1" level="project" />
<orderEntry type="library" exported="" name="okhttp-3.4.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
<orderEntry type="library" exported="" name="imagepipeline-okhttp3-0.11.0" level="project" />
<orderEntry type="library" exported="" name="play-services-gcm-9.6.1" level="project" />
</component>
</module>
\ No newline at end of file
package com.wix.reactnativenotifications;
import android.app.Application;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class RNNotificationsPackage implements ReactPackage {
private final Application mApplication;
public RNNotificationsPackage(Application application) {
mApplication = application;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new RNNotificationsModule(mApplication, reactContext));
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
package com.wix.reactnativenotifications.gcm;
import android.content.Intent;
import com.google.android.gms.iid.InstanceIDListenerService;
/**
* Instance-ID + token refreshing handling service. Contacts the GCM to fetch the updated token.
*
* @author amitd
*/
public class GcmInstanceIdListenerService extends InstanceIDListenerService {
@Override
public void onTokenRefresh() {
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
// Google recommends running this from an intent service.
Intent intent = new Intent(this, GcmInstanceIdRefreshHandlerService.class);
startService(intent);
}
}
package com.wix.reactnativenotifications.gcm;
import android.content.Context;
public interface INotificationsGcmApplication {
IGcmToken getGcmToken(Context context);
}
module.exports = function (api) {
api && api.cache(false);
return {
presets: [
'module:metro-react-native-babel-preset'
],
plugins: [
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-export-default-from'
]
};
};
version: "3"
services:
docusaurus:
build: .
ports:
- 3000:3000
- 35729:35729
volumes:
- ./docs:/app/docs
- ./website/blog:/app/website/blog
- ./website/core:/app/website/core
- ./website/i18n:/app/website/i18n
- ./website/pages:/app/website/pages
- ./website/static:/app/website/static
- ./website/sidebars.json:/app/website/sidebars.json
- ./website/siteConfig.js:/app/website/siteConfig.js
working_dir: /app/website
---
id: advanced-ios
title: iOS Advanced API
sidebar_label: iOS
---
## PushKit API
The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/).
### Register to PushKit
[Prepare your app to receive VoIP push notifications](https://developer.apple.com/library/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html)
### Listen to PushKit notifications
On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload.
```js
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(payload));
});
```
In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
```javascript
constructor() {
Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log("PushKit Token Received: " + event.pushKitToken);
});
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log('PushKit notification Received: ' + JSON.stringify(payload));
});
Notifications.ios.registerPushKit();
}
```
> 1. Notice that PushKit device token and regular notifications device token are different, so you must handle two different tokens in the server side in order to support this feature.
> 2. PushKit will not request permissions from the user for push notifications.
---
## Interactive / Actionable Notifications
> This section provides description for iOS. For notifications customization on Android, refer to [our wiki](https://github.com/wix/react-native-notifications/wiki/Android-Customizations#customizing-notifications-layout).
Interactive notifications allow you to reply to a message right from the notification banner or take action right from the lock screen.
On the Lock screen and within Notification Center, you swipe from right to left
to reveal actions. Destructive actions, like trashing an email, are color-coded red. Relatively neutral actions, like dismissing an alert or declining an invitation, are color-coded gray.
For banners, you pull down to reveal actions as buttons. For popups, the actions are immediately visible — the buttons are right there.
You can find more info about interactive notifications [here](http://www.imore.com/interactive-notifications-ios-8-explained).
![Interactive Notifications](http://i.imgur.com/XrVzy9w.gif)
Notification **actions** allow the user to interact with a given notification.
Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself.
Follow the basic workflow of adding interactive notifications to your app:
1. Config the actions.
2. Group actions together into categories.
3. Register to push notifications with the configured categories.
4. Push a notification (or trigger a [local](#triggering-local-notifications) one) with the configured category name.
### Example
#### Config the Actions
We will config two actions: upvote and reply.
```javascript
import { Notifications, NotificationAction, NotificationCategory } from 'react-native-notifications';
let upvoteAction = new NotificationAction({
activationMode: "background",
title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION",
textInput: {
buttonTitle: 'title',
placeholder: 'placeholder text'
}
});
let replyAction = new NotificationAction({
activationMode: "background",
title: "Reply",
authenticationRequired: true,
identifier: "REPLY_ACTION"
});
```
#### Config the Category
We will group `upvote` action and `reply` action into a single category: `EXAMPLE_CATEGORY `. If the notification contains `EXAMPLE_CATEGORY ` under `category` field, those actions will appear.
```javascript
let exampleCategory = new NotificationCategory({
identifier: "EXAMPLE_CATEGORY",
actions: [upvoteAction, replyAction]
});
```
#### Register to Push Notifications
Instead of basic registration like we've done before, we will register the device to push notifications with the category we've just created.
```javascript
Notifications.setCategories([exampleCategory]);
```
#### Push an Interactive Notification
Notification payload should look like this:
```javascript
{
aps: {
// ... (alert, sound, badge, etc)
category: "EXAMPLE_CATEGORY"
}
}
```
The [example app](https://github.com/wix/react-native-notifications/tree/master/example) contains this interactive notification example, you can follow there.
### `NotificationAction` Payload
- `title` - Action button title.
- `identifier` - Action identifier (must be unique).
- `activationMode` - Indicating whether the app should activate to the foreground or background.
- `foreground` (default) - Activate the app and put it in the foreground.
- `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground.
- `textInput` - `TextInput` payload, when supplied, the system will present text input in this action.
- `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive.
- `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed.
### `NotificationCategory` Payload
- `identifier` - The name of the action group (must be unique).
- `actions` - An array of `NotificationAction` objects, which related to this category.
### `TextInput` Payload
- `buttonTitle` - Title of the `send` button.
- `placeholder` - Placeholder for the `textInput`.
#### Get and set application icon badges count (iOS only)
Get the current number:
```javascript
Notifications.ios.getBadgesCount((count) => console.log(count));
```
Set to specific number:
```javascript
Notifications.ios.setBadgesCount(2);
```
Clear badges icon:
```javascript
Notifications.ios.setBadgesCount(0);
```
---
id: android-api
title: Android Specific Commands
sidebar_label: Android specific
---
## refreshToken()
Request a new token for sending push notifications.
```js
Notifications.android.refreshToken();
```
---
id: general-api
title: General Commands
sidebar_label: General
---
## registerRemoteNotifications()
Requests remote notification permissions, prompting the user's dialog box on iOS and request a token on Android.
If the user accept the remote notifications permissions, `RemoteNotificationsRegistered` event will get called with the device token.
```js
Notifications.registerRemoteNotifications();
```
## getInitialNotification()
This method returns a promise. If the app was launched by a push notification, this promise resolves to an object of type [Notification](notification-object). Otherwise, it resolves to undefined.
```js
const notification: Notification = await Notifications.getInitialNotification();
```
## postLocalNotification(notification, id?)
Posts local notification to the device notification center.
```js
Notifications.postLocalNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
link: 'localNotificationLink',
fireDate: new Date()
}, id);
```
## cancelLocalNotification(id)
Relevant for notifications sent with `fireDate`.
```js
Notifications.cancelLocalNotification(id);
```
## isRegisteredForRemoteNotifications()
Check if the app has permissions to send remote notifications.
```js
const hasPermissions: boolean = await Notifications.isRegisteredForRemoteNotifications();
```
---
id: general-events
title: General
sidebar_label: General
---
## registerRemoteNotificationsRegistered()
Fired when the user registers for remote notifications. The handler will be invoked with an event holding the hex string representing the `deviceToken`
```js
Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
console.log(event.deviceToken);
});
```
## registerNotificationReceived()
Fired when a remote notification is received in foreground state. The handler will be invoked with an instance of [Notification](notification-object).
Should call completion function on iOS, will be ignored on Android.
```js
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
console.log(JSON.stringify(notification.data));
// Calling completion on iOS with `alert: true` will present the native iOS inApp notification.
completion({alert: true, sound: true, badge: false});
});
```
## registerRemoteNotificationOpened()
Fired when a remote notification is opened from dead or background state. The handler will be invoked with an instance of [Notification](notification-object).
Should call completion function on iOS, will be ignored on Android.
```js
Notifications.events().registerRemoteNotificationOpened((notification: Notification, completion: () => void) => {
console.log(JSON.stringify(notification.data));
completion();
});
```
## registerRemoteNotificationsRegistrationFailed()
Fired when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. The handler will be invoked with {localizedDescription: string, code: string, domain: string}.
```js
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
console.log(event.code, event.localizedDescription, event.domain);
});
```
\ No newline at end of file
---
id: installation-android
title: Android Installation
sidebar_label: Android Installation
---
Add the library to your application class (e.g. `MainApplication.java`):
```java
import com.wix.reactnativenotifications.RNNotificationsPackage;
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
// ...
// Add this line:
new RNNotificationsPackage(MainApplication.this)
);
```
### Receiving push notifications
> Note: This section is only necessary in case you wish to be able to **receive** push notifications in your React-Native app.
Push notifications on Android are managed and dispatched using [Google's FCM service](https://firebase.google.com/docs/cloud-messaging). The following installation steps are a TL;DR of [Google's FCM setup guide](https://firebase.google.com/docs/cloud-messaging/android/client). You can follow them to get FCM integrated quickly, but we recommend that you will in the very least have a peek at the guide's overview.
#### Step #1: Subscribe to Google's FCM
To set FCM in your app, you must first create a google-services.json file. If you have no existing API project yet, the easiest way to go about in creating one is using [this step-by-step installation process](https://firebase.google.com/docs/android/setup);
#### Step #2: Copy google-services.json
After creating google-services.json, copy it into your project's android/app folder.
#### Step #3: Add google-services package to Project/build.gradle
```gradle
buildscript {
...
dependencies {
...
classpath 'com.google.gms:google-services:4.0.0'
}
}
```
#### Step #4: Add firebase-core package and apply google-services plugin in Project/app/build.gradle
```gradle
dependencies {
...
implementation 'com.google.firebase:firebase-core:16.0.0'
}
apply plugin: 'com.google.gms.google-services'
```
---
id: installation-ios
title: iOS Installation
sidebar_label: iOS Installation
---
As with any React Native project, the first step is to add the project as an npm dependency.
The 2nd is to do some platform specific setup so as to be able to work with Apple and Google's services for push notifications.
Start by running this:
```
$ npm install react-native-notifications --save
```
Then, [Manually link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) the library to your Xcode project.
To enable notifications support add the following line at the top of your `AppDelegate.m`
```objective-c
#import "RNNotifications.h"
```
Start monitor notifications in: `application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`
```objective-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[RNNotifications startMonitorNotifications]; // -> Add this line
return YES;
}
```
And add the following methods to support registration:
```objective-c
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
}
\ No newline at end of file
---
id: ios-api
title: iOS Specific Commands
sidebar_label: iOS specific
---
## requestPermissions()
Requests notification permissions from iOS, prompting the user's dialog box.
```js
Notifications.ios.requestPermissions();
```
## checkPermissions()
See what push permissions are currently enabled.
```js
Notifications.ios.checkPermissions();
```
## abandonPermissions()
Unregister for all remote notifications received via Apple Push Notification service.
You should call this method in rare circumstances only, such as when a new version of the app removes support for all types of remote notifications. Users can temporarily prevent apps from receiving remote notifications through the Notifications section of the Settings app. Apps unregistered through this method can always re-register.
```js
Notifications.ios.abandonPermissions();
```
## registerPushKit()
Register for PushKit notifications
```js
Notifications.ios.registerPushKit();
```
## cancelAllLocalNotifications()
Cancels all scheduled localNotifications
```js
Notifications.ios.cancelAllLocalNotifications();
```
## getDeliveredNotifications()
Provides you with a list of the app’s notifications that are still displayed in Notification Center
```js
Notifications.ios.getDeliveredNotifications();
```
## removeAllDeliveredNotifications()
Remove all delivered notifications from Notification Center
```js
Notifications.ios.removeAllDeliveredNotifications();
```
## removeDeliveredNotifications()
Removes the specified notifications from Notification Center
```js
Notifications.ios.removeDeliveredNotifications(identifiers);
```
## getBadgeCount()
Gets the badge count number from the aps object
```js
Notifications.ios.getBadgeCount();
```
## setBadgeCount()
Sets the badge number for the app icon on the home screen
```js
Notifications.ios.setBadgeCount(1);
```
\ No newline at end of file
---
id: ios-events
title: iOS
sidebar_label: iOS specific
---
## registerPushKitRegistered()
Fired when the user registers for PushKit notifications. The handler will be invoked with an event holding the hex string representing the `pushKitToken`
```js
Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log(event.pushKitToken);
});
```
## registerPushKitNotificationReceived()
Fired when a PushKit notification is received. The handler will be invoked with the notification object.
```js
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(payload));
});
```
---
id: localNotifications
title: Local Notifications
sidebar_label: Local Notifications
---
# Local Notifications <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
You can manually trigger local notifications in your JS code, to be posted immediately or in the future. You can manually trigger local notifications in your JS code, to be posted immediately or in the future.
Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library. Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library.
...@@ -9,10 +12,10 @@ Triggering local notifications is fully compatible with React Native `PushNotifi ...@@ -9,10 +12,10 @@ Triggering local notifications is fully compatible with React Native `PushNotifi
Example: Example:
```javascript ```javascript
let localNotification = NotificationsIOS.localNotification({ let localNotification = Notifications.postLocalNotification({
alertBody: "Local notificiation!", body: "Local notificiation!",
alertTitle: "Local Notification Title", title: "Local Notification Title",
soundName: "chime.aiff", sound: "chime.aiff",
silent: false, silent: false,
category: "SOME_CATEGORY", category: "SOME_CATEGORY",
userInfo: { } userInfo: { }
...@@ -22,46 +25,46 @@ let localNotification = NotificationsIOS.localNotification({ ...@@ -22,46 +25,46 @@ let localNotification = NotificationsIOS.localNotification({
Notification object contains: Notification object contains:
- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch). - **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch).
- `alertBody`- The message displayed in the notification alert. - `body`- The message displayed in the notification alert.
- `alertTitle`- The title of the notification, displayed in the notifications center. - `title`- The title of the notification, displayed in the notifications center.
- `alertAction`- The "action" displayed beneath an actionable notification on the lockscreen (e.g. "Slide to **open**"). Note that Apple no longer shows this in iOS 10. - `alertAction`- The "action" displayed beneath an actionable notification on the lockscreen (e.g. "Slide to **open**"). Note that Apple no longer shows this in iOS 10.
- `soundName`- The sound played when the notification is fired (optional -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4. - `sound`- The sound played when the notification is fired (optional -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4.
- `silent`- Whether the notification sound should be suppressed (optional). - `silent`- Whether the notification sound should be suppressed (optional).
- `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional). - `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
- `userInfo`- An optional object containing additional notification data. - `userInfo`- An optional object containing additional notification data.
### Cancel Scheduled Local Notifications ### Cancel Scheduled Local Notifications
The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`. The `Notifications.postLocalNotification()` method return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `Notifications.cancelLocalNotification(notificationId)`.
Example: Example:
```javascript ```javascript
let someLocalNotification = NotificationsIOS.localNotification({ let someLocalNotification = Notifications.postLocalNotification({
alertBody: "Local notificiation!", body: "Local notificiation!",
alertTitle: "Local Notification Title", title: "Local Notification Title",
soundName: "chime.aiff", sound: "chime.aiff",
category: "SOME_CATEGORY", category: "SOME_CATEGORY",
userInfo: { } userInfo: { }
}); });
NotificationsIOS.cancelLocalNotification(someLocalNotification); Notifications.cancelLocalNotification(someLocalNotification);
``` ```
To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`: To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`:
```javascript ```javascript
NotificationsIOS.cancelAllLocalNotifications(); Notifications.ios.cancelAllLocalNotifications();
``` ```
#### Cancel Delivered Local Notifications (iOS 10+ only) #### Cancel Delivered Local Notifications (iOS 10+ only)
To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`: To dismiss notifications from the notification center that have already been shown to the user, call `Notifications.ios.removeDeliveredNotifications([notificationId])`:
```javascript ```javascript
let someLocalNotification = NotificationsIOS.localNotification({...}); let someLocalNotification = Notifications.postLocalNotification({...});
NotificationsIOS.removeDeliveredNotifications([someLocalNotification]); Notifications.ios.removeDeliveredNotifications([someLocalNotification]);
``` ```
Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
...@@ -69,12 +72,12 @@ Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications ...@@ -69,12 +72,12 @@ Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
notifications). notifications).
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications: Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications:
```javascript ```javascript
NotificationsAndroid.localNotification({ Notifications.postLocalNotification({
title: "Local notification", title: "Local notification",
body: "This notification was generated by the app!", body: "This notification was generated by the app!",
extra: "data" extra: "data"
......
---
id: notification-object
title: Notification object
sidebar_label: Notification
---
Contains the payload data.
- **`message`**- returns the notification's main message string.
- **`sound`**- returns the sound string from the `aps` object.
- **`badge`**- returns the badge count number from the `aps` object.
- **`category`**- returns the category from the `aps` object (related to interactive notifications).
- **`data`**- returns the data payload (additional info) of the notification.
Example:
```js
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
// Prints the notification payload
console.log(JSON.stringify(notification.data));
completion({alert: false, sound: false, badge: false});
});
```
\ No newline at end of file
---
id: notifications-events
title: Handling Notification Events
sidebar_label: Events
---
When a push notification is received by the device, the application can be in one of the following states:
1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceived` event will be fired, do not forget to invoke `completion()` callback.
Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired, here as well you need to remember invoking `completion()` callback.
Example:
```javascript
constructor() {
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
console.log("Notification Received - Foreground", notification.data);
// Calling completion on iOS with `alert: true` will present the native iOS inApp notification.
completion({alert: true, sound: true, badge: false});
});
Notifications.events().registerRemoteNotificationOpened((notification: Notification, completion: () => void, action: NotificationActionResponse) => {
console.log("Notification opened by device user", notification.data);
console.log(`Notification opened with an action identifier: ${action.identifier} and response text: ${action.text}`);
completion();
});
}
```
### Notification Object
When you receive a push notification, you'll get an instance of [Notification](notification-object) object, contains the following methods:
## Querying initial notification
React-Native's [`PushNotificationsIOS.getInitialNotification()`](https://facebook.github.io/react-native/docs/pushnotificationios.html#getinitialnotification) allows for the async retrieval of the original notification used to open the App on iOS, but it has no equivalent implementation for Android.
```javascript
import {Notifications} from 'react-native-notifications';
Notifications.getInitialNotification()
.then((notification) => {
console.log("Initial notification was:", (notification ? notification.data : 'N/A'));
})
.catch((err) => console.error("getInitialNotifiation() failed", err));
```
> **Note**
>
> Notifications are considered 'initial' under the following terms:
> - User tapped on a notification, _AND_ -
> - App was either not running at all ("dead" state), _OR_ it existed in the background with **no running activities** associated with it.
# Push Notifications Subscription ---
id: subscription
title: Push Notifications Subscription
sidebar_label: Subscription
---
The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. GCM), then publishing the received token to your own push management servers. The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. FCM), then publishing the received token to your own push management servers.
This section is about the first part of the flow. This section is about the first part of the flow.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
In order to handle notifications, you must register the `remoteNotificationsRegistered` event beforehand. In order to handle notifications, you must register the `remoteNotificationsRegistered` event beforehand.
In your React Native app: In your React Native app:
```javascript ```javascript
import NotificationsIOS from 'react-native-notifications'; import {Notifications} from 'react-native-notifications';
class App extends Component { class App extends Component {
constructor() { constructor() {
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
NotificationsIOS.requestPermissions();
}
onPushRegistered(deviceToken) {
// TODO: Send the token to my server so it could send back push notifications... // TODO: Send the token to my server so it could send back push notifications...
console.log("Device Token Received", deviceToken); console.log("Device Token Received", event.deviceToken);
} });
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
console.error(event);
});
onPushRegistrationFailed(error) { Notifications.requestPermissions();
// For example:
//
// error={
// domain: 'NSCocoaErroDomain',
// code: 3010,
// localizedDescription: 'remote notifications are not supported in the simulator'
// }
console.error(error);
}
componentWillUnmount() {
// prevent memory leaks!
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
} }
} }
...@@ -48,30 +34,12 @@ class App extends Component { ...@@ -48,30 +34,12 @@ class App extends Component {
When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.). When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.).
You can check if the user granted permissions by calling `checkPermissions()`: You can check if the user granted permissions on iOS by calling `checkPermissions()`:
```javascript ```javascript
NotificationsIOS.checkPermissions().then((currentPermissions) => { Notifications.ios.checkPermissions().then((currentPermissions) => {
console.log('Badges enabled: ' + !!currentPermissions.badge); console.log('Badges enabled: ' + !!currentPermissions.badge);
console.log('Sounds enabled: ' + !!currentPermissions.sound); console.log('Sounds enabled: ' + !!currentPermissions.sound);
console.log('Alerts enabled: ' + !!currentPermissions.alert); console.log('Alerts enabled: ' + !!currentPermissions.alert);
}); });
``` ```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Android works similarly but using a different API; The equivalent code is:
```javascript
import {NotificationsAndroid} from 'react-native-notifications';
// On Android, we allow for only one (global) listener per each event type.
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
// TODO: Send the token to my server so it could send back push notifications...
console.log('Push-notifications registered!', deviceToken)
});
```
`deviceToken` being the token used to identify the device on the GCM.
...@@ -91,19 +91,12 @@ After [preparing your app to receive VoIP push notifications](https://developer. ...@@ -91,19 +91,12 @@ After [preparing your app to receive VoIP push notifications](https://developer.
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
``` ```
And the following methods: ### Listen to PushKit notifications
On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload.
```objective-c ```objective-c
// PushKit API Support #import "RNNotifications.h"
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type #import <PushKit/PushKit.h>
{
[RNNotifications didUpdatePushCredentials:credentials forType:type];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
[RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}
``` ```
In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`: In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
...@@ -111,6 +104,7 @@ In your ReactNative code, add event handler for `pushKitRegistered` event and ca ...@@ -111,6 +104,7 @@ In your ReactNative code, add event handler for `pushKitRegistered` event and ca
```javascript ```javascript
constructor() { constructor() {
NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this));
NotificationsIOS.registerPushKit(); NotificationsIOS.registerPushKit();
} }
...@@ -118,6 +112,10 @@ onPushKitRegistered(deviceToken) { ...@@ -118,6 +112,10 @@ onPushKitRegistered(deviceToken) {
console.log("PushKit Token Received: " + deviceToken); console.log("PushKit Token Received: " + deviceToken);
} }
onPushKitNotificationReceived(notification) {
console.log('PushKit notification Received: ' + JSON.stringify(notification));
}
componentWillUnmount() { componentWillUnmount() {
// Don't forget to remove the event listeners to prevent memory leaks! // Don't forget to remove the event listeners to prevent memory leaks!
NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this)); NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this));
...@@ -150,22 +148,7 @@ Notification **actions** allow the user to interact with a given notification. ...@@ -150,22 +148,7 @@ Notification **actions** allow the user to interact with a given notification.
Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself. Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself.
In order to support interactive notifications, firstly add the following methods to `appDelegate.m` file: Follow the basic workflow of adding interactive notifications to your app:
```objective-c
// Required for the notification actions.
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler];
}
```
Then, follow the basic workflow of adding interactive notifications to your app:
1. Config the actions. 1. Config the actions.
2. Group actions together into categories. 2. Group actions together into categories.
...@@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac ...@@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac
let upvoteAction = new NotificationAction({ let upvoteAction = new NotificationAction({
activationMode: "background", activationMode: "background",
title: String.fromCodePoint(0x1F44D), title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION" identifier: "UPVOTE_ACTION",
}, (action, completed) => { textInput: {
console.log("ACTION RECEIVED"); buttonTitle: 'title',
console.log(JSON.stringify(action)); placeholder: 'placeholder text'
}
// You must call to completed(), otherwise the action will not be triggered
completed();
}); });
let replyAction = new NotificationAction({ let replyAction = new NotificationAction({
activationMode: "background", activationMode: "background",
title: "Reply", title: "Reply",
behavior: "textInput",
authenticationRequired: true, authenticationRequired: true,
identifier: "REPLY_ACTION" identifier: "REPLY_ACTION"
}, (action, completed) => {
console.log("ACTION RECEIVED");
console.log(action);
completed();
}); });
``` ```
...@@ -212,8 +187,7 @@ We will group `upvote` action and `reply` action into a single category: `EXAMPL ...@@ -212,8 +187,7 @@ We will group `upvote` action and `reply` action into a single category: `EXAMPL
```javascript ```javascript
let exampleCategory = new NotificationCategory({ let exampleCategory = new NotificationCategory({
identifier: "EXAMPLE_CATEGORY", identifier: "EXAMPLE_CATEGORY",
actions: [upvoteAction, replyAction], actions: [upvoteAction, replyAction]
context: "default"
}); });
``` ```
...@@ -245,9 +219,7 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ ...@@ -245,9 +219,7 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/
- `activationMode` - Indicating whether the app should activate to the foreground or background. - `activationMode` - Indicating whether the app should activate to the foreground or background.
- `foreground` (default) - Activate the app and put it in the foreground. - `foreground` (default) - Activate the app and put it in the foreground.
- `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground. - `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground.
- `behavior` - Indicating additional behavior that the action supports. - `textInput` - `TextInput` payload, when supplied, the system will present text input in this action.
- `default` - No additional behavior.
- `textInput` - When button is tapped, the action opens a text input. the text will be delivered to your action callback.
- `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive. - `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive.
- `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed. - `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed.
...@@ -255,9 +227,11 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ ...@@ -255,9 +227,11 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/
- `identifier` - The name of the action group (must be unique). - `identifier` - The name of the action group (must be unique).
- `actions` - An array of `NotificationAction` objects, which related to this category. - `actions` - An array of `NotificationAction` objects, which related to this category.
- `context` - Indicating the amount of space available for displaying actions in a notification.
- `default` (default) - Displayes up to 4 actions (full UI). ### `TextInput` Payload
- `minimal` - Displays up tp 2 actions (minimal UI).
- `buttonTitle` - Title of the `send` button.
- `placeholder` - Placeholder for the `textInput`.
#### Get and set application icon badges count (iOS only) #### Get and set application icon badges count (iOS only)
......
...@@ -7,7 +7,7 @@ The 2nd is to do some platform specific setup so as to be able to work with Appl ...@@ -7,7 +7,7 @@ The 2nd is to do some platform specific setup so as to be able to work with Appl
Start by running this: Start by running this:
``` ```
$ npm install react-native-notifications --save $ npm install react-native-notifications@^2.0.6 --save
``` ```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS ## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
...@@ -20,17 +20,24 @@ Then, to enable notifications support add the following line at the top of your ...@@ -20,17 +20,24 @@ Then, to enable notifications support add the following line at the top of your
#import "RNNotifications.h" #import "RNNotifications.h"
``` ```
And the following methods to support registration and receiving notifications: Start monitor notifications in: `application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`
```objective-c ```objective-c
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{ [RNNotifications startMonitorNotifications]; // -> Add this line
[RNNotifications didRegisterUserNotificationSettings:notificationSettings];
return YES;
} }
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken ```
{
And add the following methods to support registration:
```objective-c
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
} }
...@@ -38,16 +45,6 @@ And the following methods to support registration and receiving notifications: ...@@ -38,16 +45,6 @@ And the following methods to support registration and receiving notifications:
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
} }
// Required for the notification event.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification {
[RNNotifications didReceiveRemoteNotification:notification];
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNNotifications didReceiveLocalNotification:notification];
}
``` ```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android ## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
...@@ -57,7 +54,7 @@ Add a reference to the library's native code in your global `settings.gradle`: ...@@ -57,7 +54,7 @@ Add a reference to the library's native code in your global `settings.gradle`:
```gradle ```gradle
include ':reactnativenotifications' include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/lib/android/app')
``` ```
Declare the library as a dependency in your **app-project's** `build.gradle`: Declare the library as a dependency in your **app-project's** `build.gradle`:
...@@ -91,27 +88,99 @@ import com.wix.reactnativenotifications.RNNotificationsPackage; ...@@ -91,27 +88,99 @@ import com.wix.reactnativenotifications.RNNotificationsPackage;
> Note: This section is only necessary in case you wish to be able to **receive** push notifications in your React-Native app. > Note: This section is only necessary in case you wish to be able to **receive** push notifications in your React-Native app.
Push notifications on Android are managed and dispatched using [Google's GCM service](https://developers.google.com/cloud-messaging/gcm) (now integrated into Firebase). The following installation steps are a TL;DR of [Google's GCM setup guide](https://developers.google.com/cloud-messaging/android/client). You can follow them to get GCM integrated quickly, but we recommend that you will in the very least have a peek at the guide's overview. Push notifications on Android are managed and dispatched using [Google's FCM service](https://firebase.google.com/docs/cloud-messaging). The following installation steps are a TL;DR of [Google's FCM setup guide](https://firebase.google.com/docs/cloud-messaging/android/client). You can follow them to get FCM integrated quickly, but we recommend that you will in the very least have a peek at the guide's overview.
#### Step #1: Subscribe to Google's GCM #### Step #1: Subscribe to Google's FCM
To set GCM in your app, you must first create a Google API-project and obtain a **Sender ID** and a **Server API Key**. If you have no existing API project yet, the easiest way to go about in creating one is using [this step-by-step installation process](https://developers.google.com/mobile/add); Use [this tutorial](https://code.tutsplus.com/tutorials/how-to-get-started-with-push-notifications-on-android--cms-25870) for insturctions. To set FCM in your app, you must first create a google-services.json file. If you have no existing API project yet, the easiest way to go about in creating one is using [this step-by-step installation process](https://firebase.google.com/docs/android/setup);
Alternatively, follow [Google's complete guide](https://developers.google.com/cloud-messaging/android/client#create-an-api-project).
#### Step #2: Add Sender ID to Manifest File #### Step #2: Copy google-services.json
Once obtained, bundle the Sender ID onto your main `manifest.xml` file: After creating google-services.json, copy it into your project's android/app folder.
#### Step #3: Add google-services package to Project/build.gradle
```gradle ```gradle
<manifest> buildscript {
... ...
<application> dependencies {
...
classpath 'com.google.gms:google-services:4.0.0'
}
}
```
#### Step #4: Add firebase-core package and apply google-services plugin in Project/app/build.gradle
```gradle
dependencies {
...
implementation 'com.google.firebase:firebase-core:16.0.0'
}
apply plugin: 'com.google.gms.google-services'
```
#### Step #5: RNNotifications and React Native version
<B>This step is required only for `react-native-notifications` version `2.1.0` and above.</B> <Br>
react-native-notifications supports multiple React Native versions. Target the React Native version required by your project by specifying the RNN build flavor in `android/app/build.gradle`.
```diff
android {
...
defaultConfig {
applicationId "com.yourproject"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
+ missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
versionCode 1
versionName "1.0"
...
}
...
}
```
!>Important note about `missingDimensionStrategy`<Br>
>`reactNative59` - RN 0.59.x and below<Br>
>`reactNative60` - RN 0.60.0 and above
Now we need to instruct gradle how to build that flavor. To do so here two solutions:
#### 5.1 Build app with gradle command
**prefered solution** The RNNotification flavor you would like to build is specified in `app/build.gradle`. Therefore in order to compile only that flavor, instead of building your entire project using `./gradlew assembleDebug`, you should instruct gradle to build the app module: `./gradlew app:assembleDebug`. The easiest way is to add a package.json command to build and install your debug Android APK .
```
"scripts": {
... ...
// Replace '1234567890' with your sender ID. "android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug"
// IMPORTANT: Leave the trailing \0 intact!!! }
<meta-data android:name="com.wix.reactnativenotifications.gcmSenderId" android:value="1234567890\0"/> ```
</application>
</manifest>
Now run `npm run android` to build your application
#### 5.2 Ignore other RNN flavors
If you don't want to run `npm run android` and want to keep the default `react-native run-android` command, you need to specify to graddle to ignore the other flavors RNNotifications provides.
To do so edit `android/build.gradle` and add:
```diff
+subprojects { subproject ->
+ afterEvaluate {
+ if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
+ android {
+ variantFilter { variant ->
+ def names = variant.flavors*.name
+ if (names.contains("reactNative59")) {
+ setIgnore(true)
+ }
+ }
+ }
+ }
+ }
+}
``` ```
**Note**: As more build variants come available in the future, you will need to adjust the list (`names.contains("reactNative59")`). This is why we recommend the first solution.
\ No newline at end of file
# Local Notifications
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
You can manually trigger local notifications in your JS code, to be posted immediately or in the future.
Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library.
Example:
```javascript
let localNotification = NotificationsIOS.localNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
silent: false,
category: "SOME_CATEGORY",
userInfo: { }
});
```
Notification object contains:
- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch).
- `body`- The message displayed in the notification alert.
- `title`- The title of the notification, displayed in the notifications center.
- `alertAction`- The "action" displayed beneath an actionable notification on the lockscreen (e.g. "Slide to **open**"). Note that Apple no longer shows this in iOS 10.
- `sound`- The sound played when the notification is fired (optional -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4.
- `silent`- Whether the notification sound should be suppressed (optional).
- `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
- `userInfo`- An optional object containing additional notification data.
### Cancel Scheduled Local Notifications
The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`.
Example:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
category: "SOME_CATEGORY",
userInfo: { }
});
NotificationsIOS.cancelLocalNotification(someLocalNotification);
```
To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`:
```javascript
NotificationsIOS.cancelAllLocalNotifications();
```
#### Cancel Delivered Local Notifications (iOS 10+ only)
To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({...});
NotificationsIOS.removeDeliveredNotifications([someLocalNotification]);
```
Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
(note that this will dismiss push notifications in addition to local
notifications).
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications:
```javascript
NotificationsAndroid.localNotification({
title: "Local notification",
body: "This notification was generated by the app!",
extra: "data"
});
```
Upon notification opening (tapping by the device user), all data fields will be delivered as-is).
...@@ -5,40 +5,35 @@ ...@@ -5,40 +5,35 @@
When a push notification is received by the device, the application can be in one of the following states: When a push notification is received by the device, the application can be in one of the following states:
1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceivedForeground` event will be fired. 1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceivedForeground` event will be fired, do not forget to invoke `completion()` callback.
2. **Background:** When the app is running in a background state; in this case, a `notificationReceivedBackground` event will be fired.
Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired. Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired, here as well you need to remember invoking `completion()` callback.
Example: Example:
```javascript ```javascript
constructor() { constructor() {
this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this); this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this);
this._boundOnNotificationReceivedBackground = this.onNotificationReceivedBackground.bind(this);
this._boundOnNotificationOpened = this.onNotificationOpened.bind(this); this._boundOnNotificationOpened = this.onNotificationOpened.bind(this);
NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground);
NotificationsIOS.addEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground);
NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened); NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened);
} }
onNotificationReceivedForeground(notification) { onNotificationReceivedForeground(notification, completion) {
completion({alert: true, sound: false, badge: false});
console.log("Notification Received - Foreground", notification); console.log("Notification Received - Foreground", notification);
} }
onNotificationReceivedBackground(notification) { onNotificationOpened(notification, completion, action) {
console.log("Notification Received - Background", notification);
}
onNotificationOpened(notification) {
console.log("Notification opened by device user", notification); console.log("Notification opened by device user", notification);
console.log(`Notification opened with an action identifier: ${action.identifier} and response text: ${action.text}`, notification);
completion();
} }
componentWillUnmount() { componentWillUnmount() {
// Don't forget to remove the event listeners to prevent memory leaks! // Don't forget to remove the event listeners to prevent memory leaks!
NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground);
NotificationsIOS.removeEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground);
NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened); NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened);
} }
``` ```
...@@ -54,12 +49,6 @@ When you receive a push notification, you'll get an instance of `IOSNotification ...@@ -54,12 +49,6 @@ When you receive a push notification, you'll get an instance of `IOSNotification
- **`getData()`**- returns the data payload (additional info) of the notification. - **`getData()`**- returns the data payload (additional info) of the notification.
- **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`. - **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`.
### Background Queue (Important - please read!)
When a push notification is opened but the app is not running, the application will be in a **cold launch** state, until the JS engine is up and ready to handle the notification.
The application will collect the events (notifications, actions, etc.) that happend during the cold launch for you.
When your app is ready (most of the time it's after the call to `requestPermissions()`), just call to `NotificationsIOS.consumeBackgroundQueue();` in order to consume the background queue. For more info see `index.ios.js` in the example app.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android ## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
......
# Push Notifications Subscription
The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. GCM), then publishing the received token to your own push management servers.
This section is about the first part of the flow.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
In order to handle notifications, you must register the `remoteNotificationsRegistered` event beforehand.
In your React Native app:
```javascript
import NotificationsIOS from 'react-native-notifications';
class App extends Component {
constructor() {
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
NotificationsIOS.requestPermissions();
}
onPushRegistered(deviceToken) {
// TODO: Send the token to my server so it could send back push notifications...
console.log("Device Token Received", deviceToken);
}
onPushRegistrationFailed(error) {
// For example:
//
// error={
// domain: 'NSCocoaErroDomain',
// code: 3010,
// localizedDescription: 'remote notifications are not supported in the simulator'
// }
console.error(error);
}
componentWillUnmount() {
// prevent memory leaks!
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
}
}
```
When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.).
You can check if the user granted permissions by calling `checkPermissions()`:
```javascript
NotificationsIOS.checkPermissions().then((currentPermissions) => {
console.log('Badges enabled: ' + !!currentPermissions.badge);
console.log('Sounds enabled: ' + !!currentPermissions.sound);
console.log('Alerts enabled: ' + !!currentPermissions.alert);
});
```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Android works similarly but using a different API; The equivalent code is:
```javascript
import {NotificationsAndroid} from 'react-native-notifications';
// On Android, we allow for only one (global) listener per each event type.
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
// TODO: Send the token to my server so it could send back push notifications...
console.log('Push-notifications registered!', deviceToken)
});
```
`deviceToken` being the token used to identify the device on the GCM.
const Utils = require('./Utils');
const {elementByLabel} = Utils;
describe('Notifications', () => {
beforeEach(async () => {
await device.relaunchApp({delete: true, permissions: {notifications: 'YES'}});
});
describe('Foreground', () => {
it('Should receive notification', async () => {
await device.sendUserNotification(createNotification({link: 'foreground/notification'}));
await linkShouldBeVisible('foreground/notification');
});
it('Should open notification', async () => {
await device.sendUserNotification(createNotification({link: 'foreground/notification/click', showAlert: true}));
await expect(elementByLabel('Notification Clicked: foreground/notification/click')).toBeVisible();
});
});
describe('Background', () => {
it('Should open notification', async () => {
await device.sendToHome();
await expect(elementByLabel('Notification Clicked: background/notification')).toBeNotVisible();
await device.launchApp({newInstance: false, userNotification: createNotification({link: 'background/notification'})});
await expect(elementByLabel('Notification Clicked: background/notification')).toBeVisible();
});
});
describe('Dead state', () => {
it('Should receive notification', async () => {
await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})});
await linkShouldBeVisible('deadState/notification');
});
});
async function linkShouldBeVisible(link) {
return await expect(elementByLabel(`Extra Link Param: ${link}`)).toBeVisible();
}
});
function createNotification({link, showAlert}) {
return {
trigger: {
type: 'push'
},
title: 'From push',
subtitle: 'Subtitle',
body: 'Body',
badge: 1,
payload: {
link,
showAlert
}
};
}
module.exports = {
elementByLabel: (label) => {
return element(by.text(label));
},
elementById: (id) => {
return element(by.id(id));
},
tapBackIos: () => {
try {
return element(by.traits(['button']).and(by.label('Back'))).atIndex(0).tap();
} catch (err) {
return element(by.type('_UIModernBarButton').and(by.label('Back'))).tap();
}
},
sleep: ms => new Promise(res => setTimeout(res, ms))
};
{
"setupTestFrameworkScriptFile" : "./init.js",
"testEnvironment": "node",
"bail": true,
"verbose": true
}
const detox = require('detox');
const config = require('../package.json').detox;
const exec = require('shell-utils').exec;
const adapter = require('detox/runners/jest/adapter');
jest.setTimeout(300000);
jasmine.getEnv().addReporter(adapter);
beforeAll(async () => {
await detox.init(config, {launchApp: false});
disableAndroidEmulatorAnimations();
});
afterAll(async () => {
await adapter.afterAll();
await detox.cleanup();
});
beforeEach(async () => {
await adapter.beforeEach();
});
function disableAndroidEmulatorAnimations() {
if (device.getPlatform() === 'android') {
const deviceId = device._deviceId;
exec.execAsync(`adb -s ${deviceId} shell settings put global window_animation_scale 0.0`);
exec.execAsync(`adb -s ${deviceId} shell settings put global transition_animation_scale 0.0`);
exec.execAsync(`adb -s ${deviceId} shell settings put global animator_duration_scale 0.0`);
}
}
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 26
supportLibVersion = "28.0.3"
}
repositories { repositories {
google() google()
mavenLocal() mavenLocal()
...@@ -8,7 +15,8 @@ buildscript { ...@@ -8,7 +15,8 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.google.gms:google-services:4.0.1'
} }
} }
...@@ -21,8 +29,9 @@ allprojects { ...@@ -21,8 +29,9 @@ allprojects {
maven { maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android" url "$rootDir/../../node_modules/react-native/android"
} }
maven { url "$rootDir/../../node_modules/jsc-android/dist" }
} }
} }
......
...@@ -16,3 +16,6 @@ ...@@ -16,3 +16,6 @@
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true
\ No newline at end of file
...@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME ...@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
/build /build
google-services.json
\ No newline at end of file
apply plugin: 'com.android.application' apply plugin: "com.android.application"
project.ext.react = [
root : "../../../",
entryFile: "index.js",
bundleAssetName: "index.bundle",
bundleInAlpha: true,
bundleInBeta: true
]
apply from: "../../../node_modules/react-native/react.gradle"
android { android {
compileSdkVersion 26 compileSdkVersion 28
buildToolsVersion "28.0.3" buildToolsVersion "28.0.3"
defaultConfig { defaultConfig {
applicationId "com.wix.reactnativenotifications.app" applicationId "com.wix.reactnativenotifications.app"
minSdkVersion 19 minSdkVersion 16
targetSdkVersion 26 targetSdkVersion 28
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
...@@ -21,26 +30,33 @@ android { ...@@ -21,26 +30,33 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
packagingOptions {
pickFirst '**/libjsc.so'
pickFirst '**/libc++_shared.so'
}
} }
configurations.all { configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details -> resolutionStrategy {
def requested = details.requested force 'org.webkit:android-jsc:r236355'
if (requested.group == 'com.android.support') {
if (!requested.name.startsWith("multidex")) {
details.useVersion "26.1.0"
}
}
} }
} }
dependencies { dependencies {
// compile fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.google.firebase:firebase-core:16.0.0'
implementation 'com.android.support:design:26.1.0' implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'com.facebook.react:react-native:+' implementation 'com.facebook.react:react-native:+'
implementation 'org.webkit:android-jsc-intl:+'
implementation project(':react-native-notifications') implementation project(':react-native-notifications')
testImplementation'junit:junit:4.12' testImplementation'junit:junit:4.12'
} }
apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
...@@ -9,11 +9,9 @@ ...@@ -9,11 +9,9 @@
android:name=".MainApplication" android:name=".MainApplication"
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<meta-data
android:name="com.wix.reactnativenotifications.gcmSenderId"
android:value="434691868895\0"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
......
package com.wix.reactnativenotifications.app; package com.wix.reactnativenotifications.app;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Toolbar;
import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivity;
import com.facebook.react.ReactRootView;
import static android.os.Build.VERSION.SDK_INT;
public class MainActivity extends ReactActivity { public class MainActivity extends ReactActivity {
private ReactRootView mReactRootView;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected String getMainComponentName() {
super.onCreate(savedInstanceState); return "NotificationsExampleApp";
ViewGroup layout;
if (SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
layout = (ViewGroup) getLayoutInflater().inflate(R.layout.activity_main, null);
Toolbar toolbar = layout.findViewById(R.id.toolbar);
setActionBar(toolbar);
} else {
layout = (ViewGroup) getLayoutInflater().inflate(R.layout.activity_main_prelollipop, null);
}
mReactRootView = new ReactRootView(this);
layout.addView(mReactRootView);
setContentView(layout);
startReactApplication();
}
private void startReactApplication() {
mReactRootView.startReactApplication(getReactInstanceManager(), "WixRNNotifications", null);
} }
} }
...@@ -6,12 +6,18 @@ import com.facebook.react.ReactApplication; ...@@ -6,12 +6,18 @@ import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage; import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.wix.reactnativenotifications.RNNotificationsPackage; import com.wix.reactnativenotifications.RNNotificationsPackage;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class MainApplication extends Application implements ReactApplication { public class MainApplication extends Application implements ReactApplication {
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, false);
}
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override @Override
...@@ -26,6 +32,11 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -26,6 +32,11 @@ public class MainApplication extends Application implements ReactApplication {
new RNNotificationsPackage(MainApplication.this) new RNNotificationsPackage(MainApplication.this)
); );
} }
@Override
protected String getJSMainModuleName() {
return "index";
}
}; };
@Override @Override
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.wix.reactnativenotifications.app.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
>
<android.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.wix.reactnativenotifications.app.MainActivity">
</android.support.design.widget.CoordinatorLayout>
#!/usr/bin/python
from urllib2 import *
import json
import sys
if len(sys.argv) < 2:
print 'Error: missing token argument'
sys.exit(1)
API_KEY = 'AIzaSyBVtqdO_SgPVhhXnyNGC_VXSbIX-fxk1YY'
TOKEN = sys.argv[1]
data = {
"to": TOKEN,
"data" : {
"body": "SUCCESS! Sent from script :)",
"title": "Wix Example Project"
}
}
dataJson = json.dumps(data)
request = Request(
'https://gcm-http.googleapis.com/gcm/send',
dataJson,
{
"Authorization" : "key="+API_KEY,
"Content-type" : "application/json"
}
)
print "Sending notification..."
print urlopen(request).read()
include ':myapplication' include ':myapplication'
include ':react-native-notifications' include ':react-native-notifications'
project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../../lib/android/app')
\ No newline at end of file
'use strict';
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
let mainScreen;
function onPushRegistered() {
if (mainScreen) {
mainScreen.onPushRegistered();
}
}
function onNotificationOpened(notification) {
if (mainScreen) {
mainScreen.onNotificationOpened(notification)
}
}
function onNotificationReceived(notification) {
if (mainScreen) {
mainScreen.onNotificationReceived(notification)
}
}
// It's highly recommended to keep listeners registration at global scope rather than at screen-scope seeing that
// component mount and unmount lifecycle tends to be asymmetric!
NotificationsAndroid.setRegistrationTokenUpdateListener(onPushRegistered);
NotificationsAndroid.setNotificationOpenedListener(onNotificationOpened);
NotificationsAndroid.setNotificationReceivedListener(onNotificationReceived);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
titleText: {
fontSize: 24,
textAlign: 'center',
margin: 10,
},
bodyText: {
fontSize: 18,
textAlign: 'center',
margin: 10,
},
mainButtonText: {
fontSize: 25,
fontStyle: 'italic',
fontWeight: 'bold',
textAlign: 'center',
margin: 10,
},
plainButtonText: {
fontSize: 18,
fontStyle: 'italic',
textAlign: 'center',
margin: 10,
},
});
class MainComponent extends Component {
constructor(props) {
super(props);
this.onPostNotification = this.onPostNotification.bind(this);
this.onCancelNotification = this.onCancelNotification.bind(this);
this.state = {
elapsed: 0,
lastNotification: undefined
};
console.log('ReactScreen', 'ReactScreen');
mainScreen = this;
setInterval(this.onTick.bind(this), 1000);
}
componentDidMount() {
console.log('ReactScreen', 'componentDidMount');
PendingNotifications.getInitialNotification()
.then((notification) => {console.log("getInitialNotification:", notification); this.setState({initialNotification: (notification ? notification.getData() : undefined)});})
.catch((err) => console.error("getInitialNotifiation failed", err));
}
componentWillUnmount() {
console.log('ReactScreen', 'componentWillUnmount');
}
onTick() {
this.setState({elapsed: this.state.elapsed + 1});
}
onPostNotification() {
this.lastNotificationId = NotificationsAndroid.localNotification({title: "Local notification", body: "This notification was generated by the app!"});
}
onCancelNotification() {
if (this.lastNotificationId) {
NotificationsAndroid.cancelLocalNotification(this.lastNotificationId);
this.lastNotificationId = undefined;
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.titleText}>Wix React Native Notifications</Text>
<Text style={styles.bodyText}>{this.state.initialNotification ? 'Opened from notification' : ''}</Text>
<Text style={styles.bodyText}>Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"}</Text>
<Text style={styles.bodyText}>Time elapsed: {this.state.elapsed}</Text>
<Text>{"\n\n"}</Text>
<TouchableHighlight onPress={() => this.onPostNotification()}>
<Text style={styles.mainButtonText}>Try Me!</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onCancelNotification()}>
<Text style={styles.plainButtonText}>Undo last</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onCheckPermissions()}>
<Text style={styles.plainButtonText}>Check permissions</Text>
</TouchableHighlight>
</View>
)
}
async onCheckPermissions() {
const hasPermissions = await NotificationsAndroid.isRegisteredForRemoteNotifications();
if (hasPermissions) {
alert('Yay! You have permissions');
} else {
alert('Boo! You don\'t have permissions');
}
}
onPushRegistered() {
}
onNotificationOpened(notification) {
console.log("onNotificationOpened: ", notification);
this.setState({lastNotification: notification.getData(), notificationRxTime: this.state.elapsed});
}
onNotificationReceived(notification) {
console.log("onNotificationReceived: ", notification);
}
}
AppRegistry.registerComponent('WixRNNotifications', () => MainComponent);
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import {
AppRegistry,
StyleSheet,
Text,
View
} 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"
}, (action, completed) => {
NotificationsIOS.log("ACTION RECEIVED");
NotificationsIOS.log(JSON.stringify(action));
completed();
});
let replyAction = new NotificationAction({
activationMode: "background",
title: "Reply",
behavior: "textInput",
authenticationRequired: true,
identifier: "REPLY_ACTION"
}, (action, completed) => {
console.log("ACTION RECEIVED");
console.log(action);
completed();
});
let cat = new NotificationCategory({
identifier: "SOME_CATEGORY",
actions: [upvoteAction, replyAction],
context: "default"
});
class NotificationsExampleApp extends Component {
constructor() {
super();
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.requestPermissions([cat]);
NotificationsIOS.consumeBackgroundQueue();
NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
NotificationsIOS.registerPushKit();
NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this));
}
onPushRegistered(deviceToken) {
console.log("Device Token Received: " + deviceToken);
}
onPushKitRegistered(deviceToken) {
console.log("PushKit Token Received: " + deviceToken);
}
onNotificationReceivedForeground(notification) {
console.log("Notification Received Foreground: " + JSON.stringify(notification));
}
onNotificationReceivedBackground(notification) {
NotificationsIOS.log("Notification Received Background: " + JSON.stringify(notification));
let localNotification = NotificationsIOS.localNotification({
alertBody: "Received background notificiation!",
alertTitle: "Local Notification Title",
alertAction: "Click here to open",
soundName: "chime.aiff",
category: "SOME_CATEGORY",
userInfo: notification.getData()
});
// if you want to fire the local notification 10 seconds later,
// add the following line to the notification payload:
// fireDate: new Date(Date.now() + (10 * 1000)).toISOString()
// NotificationsIOS.backgroundTimeRemaining(time => NotificationsIOS.log("remaining background time: " + time));
// NotificationsIOS.cancelLocalNotification(localNotification);
}
onNotificationOpened(notification) {
console.log("Notification Opened: " + JSON.stringify(notification));
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native Notifications Demo App!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
componentWillUnmount() {
NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.removeEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
// NotificationsIOS.resetCategories();
}
_onNotification(notification) {
AlertIOS.alert(
'Notification Received',
'Alert message: ' + notification.getMessage(),
[{
text: 'Dismiss',
onPress: null,
}]
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('NotificationsExampleApp', () => NotificationsExampleApp);
import {
AppRegistry,
StyleSheet,
View,
Text,
Button
} from 'react-native';
import React, {Component} from 'react';
import {Notifications, NotificationAction, NotificationCategory} from 'react-native-notifications';
class NotificationsExampleApp extends Component {
constructor() {
super();
this.state = {
notifications: [],
openedNotifications: [],
};
this.registerNotificationEvents();
this.setCategories();
}
registerNotificationEvents() {
Notifications.events().registerNotificationReceived((notification, completion) => {
this.setState({
notifications: [...this.state.notifications, notification]
});
completion({alert: notification.data.showAlert, sound: false, badge: false});
});
Notifications.events().registerRemoteNotificationOpened((notification, completion) => {
this.setState({
openedNotifications: [...this.state.openedNotifications, notification]
});
completion();
});
}
requestPermissions() {
Notifications.registerRemoteNotifications();
}
setCategories() {
const upvoteAction = new NotificationAction({
activationMode: 'background',
title: String.fromCodePoint(0x1F44D),
identifier: 'UPVOTE_ACTION'
});
const replyAction = new NotificationAction({
activationMode: 'background',
title: 'Reply',
authenticationRequired: true,
textInput: {
buttonTitle: 'Reply now',
placeholder: 'Insert message'
},
identifier: 'REPLY_ACTION'
});
const category = new NotificationCategory({
identifier: 'SOME_CATEGORY',
actions: [upvoteAction, replyAction]
});
Notifications.setCategories([category]);
}
sendLocalNotification() {
Notifications.postLocalNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
link: 'localNotificationLink',
});
}
removeAllDeliveredNotifications() {
Notifications.removeAllDeliveredNotifications();
}
async componentDidMount() {
const initialNotification = await Notifications.getInitialNotification();
if (initialNotification) {
this.setState({notifications: [initialNotification, ...this.state.notifications]});
}
}
renderNotification(notification) {
return (
<View style={{backgroundColor: 'lightgray', margin: 10}}>
<Text>{`Title: ${notification.title}`}</Text>
<Text>{`Body: ${notification.body}`}</Text>
<Text>{`Extra Link Param: ${notification.data.link}`}</Text>
</View>
);
}
renderOpenedNotification(notification) {
return (
<View style={{backgroundColor: 'lightgray', margin: 10}}>
<Text>{`Title: ${notification.title}`}</Text>
<Text>{`Body: ${notification.body}`}</Text>
<Text>{`Notification Clicked: ${notification.data.link}`}</Text>
</View>
);
}
render() {
const notifications = this.state.notifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderNotification(notification)}
</View>
));
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderOpenedNotification(notification)}
</View>
));
return (
<View style={styles.container}>
<Button title={'Request permissions'} onPress={this.requestPermissions} testID={'requestPermissions'} />
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'} />
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications} />
{notifications}
{openedNotifications}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('NotificationsExampleApp', () => NotificationsExampleApp);
...@@ -7,240 +7,243 @@ ...@@ -7,240 +7,243 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 5004AC02233BE75A00490132 /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5004ABE1233BE75A00490132 /* CallKit.framework */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 50F1F0CC22CE3B4700FD5829 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F06022CE3A6100FD5829 /* libReact.a */; };
50F1F0CD22CE3B6300FD5829 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F08A22CE3AA000FD5829 /* libRCTActionSheet.a */; };
50F1F0CF22CE3B6300FD5829 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F09522CE3ABE00FD5829 /* libRCTImage.a */; };
50F1F0D022CE3B6300FD5829 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F09D22CE3ACA00FD5829 /* libRCTLinking.a */; };
50F1F0D122CE3B6300FD5829 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F0A622CE3B0600FD5829 /* libRCTNetwork.a */; };
50F1F0D222CE3B6300FD5829 /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F0AE22CE3B1000FD5829 /* libRCTSettings.a */; };
50F1F0D322CE3B6300FD5829 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F0B622CE3B1A00FD5829 /* libRCTText.a */; };
50F1F0D422CE3B6300FD5829 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F0BD22CE3B2400FD5829 /* libRCTVibration.a */; };
50F1F0D522CE3B6300FD5829 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F0C522CE3B2E00FD5829 /* libRCTWebSocket.a */; };
50F1F0D622CE3C0F00FD5829 /* libyoga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F06422CE3A6100FD5829 /* libyoga.a */; };
50F1F0D722CE3C1E00FD5829 /* libcxxreact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F1F06822CE3A6100FD5829 /* libcxxreact.a */; };
D84861182267695100E9103D /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D84861172267695100E9103D /* JavaScriptCore.framework */; };
D85498D21D97B37F00DEEE06 /* libRNNotifications.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85498D11D97B31100DEEE06 /* libRNNotifications.a */; }; D85498D21D97B37F00DEEE06 /* libRNNotifications.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85498D11D97B31100DEEE06 /* libRNNotifications.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 50E49F4022D1F06C007160C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTActionSheet;
};
00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteGlobalIDString = EDEBC6D6214B3E7000DD5AC8;
remoteInfo = RCTGeolocation; remoteInfo = jsi;
}; };
00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 50E49F4222D1F06C007160C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = RCTImage;
};
00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676; remoteGlobalIDString = EDEBC73B214B45A300DD5AC8;
remoteInfo = RCTNetwork; remoteInfo = jsiexecutor;
}; };
00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 50E49F4422D1F06C007160C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; remoteGlobalIDString = ED296FB6214C9A0900B7C4FE;
remoteInfo = RCTVibration; remoteInfo = "jsi-tvOS";
}; };
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { 50E49F4622D1F06C007160C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteGlobalIDString = ED296FEE214C9CF800B7C4FE;
remoteInfo = RCTSettings; remoteInfo = "jsiexecutor-tvOS";
}; };
139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { 50E49F4A22D1F06C007160C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; containerPortal = D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3C86DF461ADF2C930047B81A; remoteGlobalIDString = 508CE7C822D12B2600357815;
remoteInfo = RCTWebSocket; remoteInfo = RNNotificationsTests;
}; };
146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 50F1F05F22CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React; remoteInfo = React;
}; };
18B5569B2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06122CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
remoteInfo = fishhook;
};
18B5569D2007789B007ACD82 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "fishhook-tvOS"; remoteInfo = "React-tvOS";
}; };
18B556AD2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06322CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3C059A1DE3340900C268FA; remoteGlobalIDString = 3D3C059A1DE3340900C268FA;
remoteInfo = yoga; remoteInfo = yoga;
}; };
18B556AF2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06522CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3C06751DE3340C00C268FA; remoteGlobalIDString = 3D3C06751DE3340C00C268FA;
remoteInfo = "yoga-tvOS"; remoteInfo = "yoga-tvOS";
}; };
18B556B12007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06722CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;
remoteInfo = cxxreact; remoteInfo = cxxreact;
}; };
18B556B32007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06922CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;
remoteInfo = "cxxreact-tvOS"; remoteInfo = "cxxreact-tvOS";
}; };
18B556B52007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F06F22CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; remoteGlobalIDString = EBF21BDC1FC498900052F4D5;
remoteInfo = jschelpers; remoteInfo = jsinspector;
}; };
18B556B72007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F07122CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5;
remoteInfo = "jschelpers-tvOS"; remoteInfo = "jsinspector-tvOS";
}; };
18B556B92007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F07322CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7;
remoteInfo = "third-party"; remoteInfo = "third-party";
}; };
18B556BB2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F07522CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; remoteGlobalIDString = 3D383D3C1EBD27B6005632C8;
remoteInfo = "third-party-tvOS"; remoteInfo = "third-party-tvOS";
}; };
18B556BD2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F07722CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 139D7E881E25C6D100323FB7; remoteGlobalIDString = 139D7E881E25C6D100323FB7;
remoteInfo = "double-conversion"; remoteInfo = "double-conversion";
}; };
18B556BF2007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F07922CE3A6100FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 3D383D621EBD27B9005632C8; remoteGlobalIDString = 3D383D621EBD27B9005632C8;
remoteInfo = "double-conversion-tvOS"; remoteInfo = "double-conversion-tvOS";
}; };
18B556C12007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F08922CE3AA000FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 50F1F08522CE3A9F00FD5829 /* RCTActionSheet.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = privatedata; remoteInfo = RCTActionSheet;
}; };
18B556C32007789B007ACD82 /* PBXContainerItemProxy */ = { 50F1F09422CE3ABE00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = "privatedata-tvOS"; remoteInfo = RCTImage;
}; };
18BA9BF01DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F09622CE3ABE00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS"; remoteInfo = "RCTImage-tvOS";
}; };
18BA9BF41DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F09C22CE3ACA00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking;
};
50F1F09E22CE3ACA00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D; remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS"; remoteInfo = "RCTLinking-tvOS";
}; };
18BA9BF81DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F0A522CE3B0600FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
50F1F0A722CE3B0600FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; containerPortal = 50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D; remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS"; remoteInfo = "RCTNetwork-tvOS";
}; };
18BA9BFC1DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F0AD22CE3B1000FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
50F1F0AF22CE3B1000FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D; remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS"; remoteInfo = "RCTSettings-tvOS";
}; };
18BA9C001DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F0B522CE3B1A00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D; remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = "RCTText-tvOS"; remoteInfo = RCTText;
}; };
18BA9C051DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F0B722CE3B1A00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D; remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTWebSocket-tvOS"; remoteInfo = "RCTText-tvOS";
}; };
18BA9C091DEC2288001F416D /* PBXContainerItemProxy */ = { 50F1F0BC22CE3B2400FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D; remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
remoteInfo = "React-tvOS"; remoteInfo = RCTVibration;
}; };
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { 50F1F0C422CE3B2E00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
remoteInfo = RCTLinking; remoteInfo = RCTWebSocket;
}; };
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 50F1F0C622CE3B2E00FD5829 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2; proxyType = 2;
remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = RCTText; remoteInfo = "RCTWebSocket-tvOS";
}; };
D85498D01D97B31100DEEE06 /* PBXContainerItemProxy */ = { D85498D01D97B31100DEEE06 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
...@@ -253,15 +256,12 @@ ...@@ -253,15 +256,12 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; }; 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; }; 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = "<group>"; }; 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* NotificationsExampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationsExampleAppTests.m; sourceTree = "<group>"; }; 00E356F21AD99517003FC87E /* NotificationsExampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationsExampleAppTests.m; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; }; 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; }; 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* NotificationsExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NotificationsExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* NotificationsExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NotificationsExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = NotificationsExampleApp/AppDelegate.h; sourceTree = "<group>"; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = NotificationsExampleApp/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = NotificationsExampleApp/AppDelegate.m; sourceTree = "<group>"; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = NotificationsExampleApp/AppDelegate.m; sourceTree = "<group>"; };
...@@ -269,10 +269,14 @@ ...@@ -269,10 +269,14 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NotificationsExampleApp/Images.xcassets; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NotificationsExampleApp/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NotificationsExampleApp/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NotificationsExampleApp/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = NotificationsExampleApp/main.m; sourceTree = "<group>"; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = NotificationsExampleApp/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; }; 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; }; 5004ABE1233BE75A00490132 /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; }; 50F1F08522CE3A9F00FD5829 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; };
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = ../../RNNotifications/RNNotifications.xcodeproj; sourceTree = "<group>"; }; 50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
D84861172267695100E9103D /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = ../../lib/ios/RNNotifications.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -280,142 +284,146 @@ ...@@ -280,142 +284,146 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
50F1F0D722CE3C1E00FD5829 /* libcxxreact.a in Frameworks */,
50F1F0D622CE3C0F00FD5829 /* libyoga.a in Frameworks */,
50F1F0CD22CE3B6300FD5829 /* libRCTActionSheet.a in Frameworks */,
50F1F0CF22CE3B6300FD5829 /* libRCTImage.a in Frameworks */,
50F1F0D022CE3B6300FD5829 /* libRCTLinking.a in Frameworks */,
50F1F0D122CE3B6300FD5829 /* libRCTNetwork.a in Frameworks */,
50F1F0D222CE3B6300FD5829 /* libRCTSettings.a in Frameworks */,
50F1F0D322CE3B6300FD5829 /* libRCTText.a in Frameworks */,
50F1F0D422CE3B6300FD5829 /* libRCTVibration.a in Frameworks */,
5004AC02233BE75A00490132 /* CallKit.framework in Frameworks */,
50F1F0D522CE3B6300FD5829 /* libRCTWebSocket.a in Frameworks */,
50F1F0CC22CE3B4700FD5829 /* libReact.a in Frameworks */,
D84861182267695100E9103D /* JavaScriptCore.framework in Frameworks */,
D85498D21D97B37F00DEEE06 /* libRNNotifications.a in Frameworks */, D85498D21D97B37F00DEEE06 /* libRNNotifications.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
00C302A81ABCB8CE00DB3ED1 /* Products */ = { 00E356EF1AD99517003FC87E /* NotificationsExampleAppTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 00E356F21AD99517003FC87E /* NotificationsExampleAppTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
); );
name = Products; path = NotificationsExampleAppTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00C302B61ABCB90400DB3ED1 /* Products */ = { 00E356F01AD99517003FC87E /* Supporting Files */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 00E356F11AD99517003FC87E /* Info.plist */,
); );
name = Products; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00C302BC1ABCB91800DB3ED1 /* Products */ = { 13B07FAE1A68108700A75B9A /* NotificationsExampleApp */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */,
18BA9BF11DEC2288001F416D /* libRCTImage-tvOS.a */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
); );
name = Products; name = NotificationsExampleApp;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00C302D41ABCB9D200DB3ED1 /* Products */ = { 50F1F04D22CE3A6100FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 50F1F06022CE3A6100FD5829 /* libReact.a */,
18BA9BF91DEC2288001F416D /* libRCTNetwork-tvOS.a */, 50F1F06222CE3A6100FD5829 /* libReact.a */,
50F1F06422CE3A6100FD5829 /* libyoga.a */,
50F1F06622CE3A6100FD5829 /* libyoga.a */,
50F1F06822CE3A6100FD5829 /* libcxxreact.a */,
50F1F06A22CE3A6100FD5829 /* libcxxreact.a */,
50F1F07022CE3A6100FD5829 /* libjsinspector.a */,
50F1F07222CE3A6100FD5829 /* libjsinspector-tvOS.a */,
50F1F07422CE3A6100FD5829 /* libthird-party.a */,
50F1F07622CE3A6100FD5829 /* libthird-party.a */,
50F1F07822CE3A6100FD5829 /* libdouble-conversion.a */,
50F1F07A22CE3A6100FD5829 /* libdouble-conversion.a */,
50E49F4122D1F06C007160C1 /* libjsi.a */,
50E49F4322D1F06C007160C1 /* libjsiexecutor.a */,
50E49F4522D1F06C007160C1 /* libjsi-tvOS.a */,
50E49F4722D1F06C007160C1 /* libjsiexecutor-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00C302E01ABCB9EE00DB3ED1 /* Products */ = { 50F1F08622CE3A9F00FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 50F1F08A22CE3AA000FD5829 /* libRCTActionSheet.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00E356EF1AD99517003FC87E /* NotificationsExampleAppTests */ = { 50F1F09022CE3ABE00FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00E356F21AD99517003FC87E /* NotificationsExampleAppTests.m */, 50F1F09522CE3ABE00FD5829 /* libRCTImage.a */,
00E356F01AD99517003FC87E /* Supporting Files */, 50F1F09722CE3ABE00FD5829 /* libRCTImage-tvOS.a */,
); );
path = NotificationsExampleAppTests; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
00E356F01AD99517003FC87E /* Supporting Files */ = { 50F1F09822CE3ACA00FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
00E356F11AD99517003FC87E /* Info.plist */, 50F1F09D22CE3ACA00FD5829 /* libRCTLinking.a */,
50F1F09F22CE3ACA00FD5829 /* libRCTLinking-tvOS.a */,
); );
name = "Supporting Files"; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
139105B71AF99BAD00B5F7CC /* Products */ = { 50F1F0A122CE3B0600FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 50F1F0A622CE3B0600FD5829 /* libRCTNetwork.a */,
18BA9BFD1DEC2288001F416D /* libRCTSettings-tvOS.a */, 50F1F0A822CE3B0600FD5829 /* libRCTNetwork-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
139FDEE71B06529A00C62182 /* Products */ = { 50F1F0A922CE3B1000FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 50F1F0AE22CE3B1000FD5829 /* libRCTSettings.a */,
18BA9C061DEC2288001F416D /* libRCTWebSocket-tvOS.a */, 50F1F0B022CE3B1000FD5829 /* libRCTSettings-tvOS.a */,
18B5569C2007789B007ACD82 /* libfishhook.a */,
18B5569E2007789B007ACD82 /* libfishhook-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
13B07FAE1A68108700A75B9A /* NotificationsExampleApp */ = { 50F1F0B122CE3B1A00FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
008F07F21AC5B25A0029DE68 /* main.jsbundle */, 50F1F0B622CE3B1A00FD5829 /* libRCTText.a */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */, 50F1F0B822CE3B1A00FD5829 /* libRCTText-tvOS.a */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
); );
name = NotificationsExampleApp; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
146834001AC3E56700842450 /* Products */ = { 50F1F0B922CE3B2400FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
146834041AC3E56700842450 /* libReact.a */, 50F1F0BD22CE3B2400FD5829 /* libRCTVibration.a */,
18BA9C0A1DEC2288001F416D /* libReact.a */,
18B556AE2007789B007ACD82 /* libyoga.a */,
18B556B02007789B007ACD82 /* libyoga.a */,
18B556B22007789B007ACD82 /* libcxxreact.a */,
18B556B42007789B007ACD82 /* libcxxreact.a */,
18B556B62007789B007ACD82 /* libjschelpers.a */,
18B556B82007789B007ACD82 /* libjschelpers.a */,
18B556BA2007789B007ACD82 /* libthird-party.a */,
18B556BC2007789B007ACD82 /* libthird-party.a */,
18B556BE2007789B007ACD82 /* libdouble-conversion.a */,
18B556C02007789B007ACD82 /* libdouble-conversion.a */,
18B556C22007789B007ACD82 /* libprivatedata.a */,
18B556C42007789B007ACD82 /* libprivatedata-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
78C398B11ACF4ADC00677621 /* Products */ = { 50F1F0BE22CE3B2E00FD5829 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 50F1F0C522CE3B2E00FD5829 /* libRCTWebSocket.a */,
18BA9BF51DEC2288001F416D /* libRCTLinking-tvOS.a */, 50F1F0C722CE3B2E00FD5829 /* libRCTWebSocket-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -423,13 +431,13 @@ ...@@ -423,13 +431,13 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = { 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
50CBD3A222F2556900142352 /* RCTAnimation.xcodeproj */,
50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */,
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */, D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 50F1F08522CE3A9F00FD5829 /* RCTActionSheet.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
...@@ -438,15 +446,6 @@ ...@@ -438,15 +446,6 @@
name = Libraries; name = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
832341B11AAA6A8300B99B32 /* Products */ = {
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
18BA9C011DEC2288001F416D /* libRCTText-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
83CBB9F61A601CBA00E9B192 = { 83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -454,6 +453,7 @@ ...@@ -454,6 +453,7 @@
832341AE1AAA6A7D00B99B32 /* Libraries */, 832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* NotificationsExampleAppTests */, 00E356EF1AD99517003FC87E /* NotificationsExampleAppTests */,
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
D84860E82267695100E9103D /* Frameworks */,
); );
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -467,10 +467,20 @@ ...@@ -467,10 +467,20 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D84860E82267695100E9103D /* Frameworks */ = {
isa = PBXGroup;
children = (
5004ABE1233BE75A00490132 /* CallKit.framework */,
D84861172267695100E9103D /* JavaScriptCore.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
D85498C31D97B31100DEEE06 /* Products */ = { D85498C31D97B31100DEEE06 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D85498D11D97B31100DEEE06 /* libRNNotifications.a */, D85498D11D97B31100DEEE06 /* libRNNotifications.a */,
50E49F4B22D1F06C007160C1 /* RNNotificationsTests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -510,6 +520,7 @@ ...@@ -510,6 +520,7 @@
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
Base, Base,
); );
...@@ -518,43 +529,43 @@ ...@@ -518,43 +529,43 @@
projectDirPath = ""; projectDirPath = "";
projectReferences = ( projectReferences = (
{ {
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProductGroup = 50F1F08622CE3A9F00FD5829 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; ProjectRef = 50F1F08522CE3A9F00FD5829 /* RCTActionSheet.xcodeproj */;
}, },
{ {
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProductGroup = 50CBD3A322F2556900142352 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; ProjectRef = 50CBD3A222F2556900142352 /* RCTAnimation.xcodeproj */;
}, },
{ {
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; ProductGroup = 50F1F09022CE3ABE00FD5829 /* Products */;
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
}, },
{ {
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; ProductGroup = 50F1F09822CE3ACA00FD5829 /* Products */;
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
}, },
{ {
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; ProductGroup = 50F1F0A122CE3B0600FD5829 /* Products */;
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; ProjectRef = 50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */;
}, },
{ {
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; ProductGroup = 50F1F0A922CE3B1000FD5829 /* Products */;
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
}, },
{ {
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; ProductGroup = 50F1F0B122CE3B1A00FD5829 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
}, },
{ {
ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; ProductGroup = 50F1F0B922CE3B2400FD5829 /* Products */;
ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
}, },
{ {
ProductGroup = 139FDEE71B06529A00C62182 /* Products */; ProductGroup = 50F1F0BE22CE3B2E00FD5829 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
}, },
{ {
ProductGroup = 146834001AC3E56700842450 /* Products */; ProductGroup = 50F1F04D22CE3A6100FD5829 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
}, },
{ {
...@@ -570,221 +581,221 @@ ...@@ -570,221 +581,221 @@
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXReferenceProxy section */ /* Begin PBXReferenceProxy section */
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 50E49F4122D1F06C007160C1 /* libjsi.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTActionSheet.a;
remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTGeolocation.a; path = libjsi.a;
remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; remoteRef = 50E49F4022D1F06C007160C1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 50E49F4322D1F06C007160C1 /* libjsiexecutor.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTImage.a; path = libjsiexecutor.a;
remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; remoteRef = 50E49F4222D1F06C007160C1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 50E49F4522D1F06C007160C1 /* libjsi-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTNetwork.a; path = "libjsi-tvOS.a";
remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; remoteRef = 50E49F4422D1F06C007160C1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 50E49F4722D1F06C007160C1 /* libjsiexecutor-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTVibration.a; path = "libjsiexecutor-tvOS.a";
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; remoteRef = 50E49F4622D1F06C007160C1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { 50E49F4B22D1F06C007160C1 /* RNNotificationsTests.xctest */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = wrapper.cfbundle;
path = libRCTSettings.a; path = RNNotificationsTests.xctest;
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; remoteRef = 50E49F4A22D1F06C007160C1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { 50F1F06022CE3A6100FD5829 /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTWebSocket.a;
remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
146834041AC3E56700842450 /* libReact.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libReact.a; path = libReact.a;
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; remoteRef = 50F1F05F22CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
18B5569C2007789B007ACD82 /* libfishhook.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libfishhook.a;
remoteRef = 18B5569B2007789B007ACD82 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B5569E2007789B007ACD82 /* libfishhook-tvOS.a */ = { 50F1F06222CE3A6100FD5829 /* libReact.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libfishhook-tvOS.a"; path = libReact.a;
remoteRef = 18B5569D2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06122CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556AE2007789B007ACD82 /* libyoga.a */ = { 50F1F06422CE3A6100FD5829 /* libyoga.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libyoga.a; path = libyoga.a;
remoteRef = 18B556AD2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06322CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556B02007789B007ACD82 /* libyoga.a */ = { 50F1F06622CE3A6100FD5829 /* libyoga.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libyoga.a; path = libyoga.a;
remoteRef = 18B556AF2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06522CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556B22007789B007ACD82 /* libcxxreact.a */ = { 50F1F06822CE3A6100FD5829 /* libcxxreact.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libcxxreact.a; path = libcxxreact.a;
remoteRef = 18B556B12007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06722CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556B42007789B007ACD82 /* libcxxreact.a */ = { 50F1F06A22CE3A6100FD5829 /* libcxxreact.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libcxxreact.a; path = libcxxreact.a;
remoteRef = 18B556B32007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06922CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556B62007789B007ACD82 /* libjschelpers.a */ = { 50F1F07022CE3A6100FD5829 /* libjsinspector.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libjschelpers.a; path = libjsinspector.a;
remoteRef = 18B556B52007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F06F22CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556B82007789B007ACD82 /* libjschelpers.a */ = { 50F1F07222CE3A6100FD5829 /* libjsinspector-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libjschelpers.a; path = "libjsinspector-tvOS.a";
remoteRef = 18B556B72007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F07122CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556BA2007789B007ACD82 /* libthird-party.a */ = { 50F1F07422CE3A6100FD5829 /* libthird-party.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libthird-party.a"; path = "libthird-party.a";
remoteRef = 18B556B92007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F07322CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556BC2007789B007ACD82 /* libthird-party.a */ = { 50F1F07622CE3A6100FD5829 /* libthird-party.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libthird-party.a"; path = "libthird-party.a";
remoteRef = 18B556BB2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F07522CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556BE2007789B007ACD82 /* libdouble-conversion.a */ = { 50F1F07822CE3A6100FD5829 /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libdouble-conversion.a"; path = "libdouble-conversion.a";
remoteRef = 18B556BD2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F07722CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556C02007789B007ACD82 /* libdouble-conversion.a */ = { 50F1F07A22CE3A6100FD5829 /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libdouble-conversion.a"; path = "libdouble-conversion.a";
remoteRef = 18B556BF2007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F07922CE3A6100FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556C22007789B007ACD82 /* libprivatedata.a */ = { 50F1F08A22CE3AA000FD5829 /* libRCTActionSheet.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libprivatedata.a; path = libRCTActionSheet.a;
remoteRef = 18B556C12007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F08922CE3AA000FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18B556C42007789B007ACD82 /* libprivatedata-tvOS.a */ = { 50F1F09522CE3ABE00FD5829 /* libRCTImage.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libprivatedata-tvOS.a"; path = libRCTImage.a;
remoteRef = 18B556C32007789B007ACD82 /* PBXContainerItemProxy */; remoteRef = 50F1F09422CE3ABE00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9BF11DEC2288001F416D /* libRCTImage-tvOS.a */ = { 50F1F09722CE3ABE00FD5829 /* libRCTImage-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTImage-tvOS.a"; path = "libRCTImage-tvOS.a";
remoteRef = 18BA9BF01DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F09622CE3ABE00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
50F1F09D22CE3ACA00FD5829 /* libRCTLinking.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTLinking.a;
remoteRef = 50F1F09C22CE3ACA00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9BF51DEC2288001F416D /* libRCTLinking-tvOS.a */ = { 50F1F09F22CE3ACA00FD5829 /* libRCTLinking-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTLinking-tvOS.a"; path = "libRCTLinking-tvOS.a";
remoteRef = 18BA9BF41DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F09E22CE3ACA00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
50F1F0A622CE3B0600FD5829 /* libRCTNetwork.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTNetwork.a;
remoteRef = 50F1F0A522CE3B0600FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9BF91DEC2288001F416D /* libRCTNetwork-tvOS.a */ = { 50F1F0A822CE3B0600FD5829 /* libRCTNetwork-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTNetwork-tvOS.a"; path = "libRCTNetwork-tvOS.a";
remoteRef = 18BA9BF81DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F0A722CE3B0600FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9BFD1DEC2288001F416D /* libRCTSettings-tvOS.a */ = { 50F1F0AE22CE3B1000FD5829 /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTSettings.a;
remoteRef = 50F1F0AD22CE3B1000FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
50F1F0B022CE3B1000FD5829 /* libRCTSettings-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTSettings-tvOS.a"; path = "libRCTSettings-tvOS.a";
remoteRef = 18BA9BFC1DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F0AF22CE3B1000FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9C011DEC2288001F416D /* libRCTText-tvOS.a */ = { 50F1F0B622CE3B1A00FD5829 /* libRCTText.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTText-tvOS.a"; path = libRCTText.a;
remoteRef = 18BA9C001DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F0B522CE3B1A00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9C061DEC2288001F416D /* libRCTWebSocket-tvOS.a */ = { 50F1F0B822CE3B1A00FD5829 /* libRCTText-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = "libRCTWebSocket-tvOS.a"; path = "libRCTText-tvOS.a";
remoteRef = 18BA9C051DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F0B722CE3B1A00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
18BA9C0A1DEC2288001F416D /* libReact.a */ = { 50F1F0BD22CE3B2400FD5829 /* libRCTVibration.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libReact.a; path = libRCTVibration.a;
remoteRef = 18BA9C091DEC2288001F416D /* PBXContainerItemProxy */; remoteRef = 50F1F0BC22CE3B2400FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { 50F1F0C522CE3B2E00FD5829 /* libRCTWebSocket.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTLinking.a; path = libRCTWebSocket.a;
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; remoteRef = 50F1F0C422CE3B2E00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 50F1F0C722CE3B2E00FD5829 /* libRCTWebSocket-tvOS.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
path = libRCTText.a; path = "libRCTWebSocket-tvOS.a";
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; remoteRef = 50F1F0C622CE3B2E00FD5829 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
D85498D11D97B31100DEEE06 /* libRNNotifications.a */ = { D85498D11D97B31100DEEE06 /* libRNNotifications.a */ = {
...@@ -821,7 +832,7 @@ ...@@ -821,7 +832,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh\n";
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
...@@ -860,7 +871,7 @@ ...@@ -860,7 +871,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**", "$(SRCROOT)/../node_modules/react-native-notifications/lib/ios/**",
); );
INFOPLIST_FILE = NotificationsExampleApp/Info.plist; INFOPLIST_FILE = NotificationsExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 10.0;
...@@ -885,7 +896,7 @@ ...@@ -885,7 +896,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**", "$(SRCROOT)/../node_modules/react-native-notifications/lib/ios/**",
); );
INFOPLIST_FILE = NotificationsExampleApp/Info.plist; INFOPLIST_FILE = NotificationsExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 10.0;
......
...@@ -6,6 +6,20 @@ ...@@ -6,6 +6,20 @@
parallelizeBuildables = "NO" parallelizeBuildables = "NO"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3D3C04B91DE3340900C268FA"
BuildableName = "libyoga.a"
BlueprintName = "yoga"
ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
...@@ -17,7 +31,7 @@ ...@@ -17,7 +31,7 @@
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192" BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a" BuildableName = "libReact.a"
BlueprintName = "React" BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj"> ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
<BuildActionEntry <BuildActionEntry
...@@ -40,9 +54,36 @@ ...@@ -40,9 +54,36 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = "" codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES"> onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "NO">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "NotificationsExampleApp.app"
BlueprintName = "NotificationsExampleApp"
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "508CE7C722D12B2600357815"
BuildableName = "RNNotificationsTests.xctest"
BlueprintName = "RNNotificationsTests"
ReferencedContainer = "container:../../lib/ios/RNNotifications.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables> <Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "508CE7C722D12B2600357815"
BuildableName = "RNNotificationsTests.xctest"
BlueprintName = "RNNotificationsTests"
ReferencedContainer = "container:../../lib/ios/RNNotifications.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
...@@ -53,6 +94,13 @@ ...@@ -53,6 +94,13 @@
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj"> ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "TEST_ENABLED"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions> <AdditionalOptions>
</AdditionalOptions> </AdditionalOptions>
</TestAction> </TestAction>
...@@ -60,7 +108,6 @@ ...@@ -60,7 +108,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3D3C04B91DE3340900C268FA"
BuildableName = "libyoga.a"
BlueprintName = "yoga"
ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "NotificationsExampleApp.app"
BlueprintName = "NotificationsExampleApp"
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "NotificationsExampleApp.app"
BlueprintName = "NotificationsExampleApp"
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "NotificationsExampleApp.app"
BlueprintName = "NotificationsExampleApp"
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "NotificationsExampleApp.app"
BlueprintName = "NotificationsExampleApp"
ReferencedContainer = "container:NotificationsExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
...@@ -9,9 +9,7 @@ ...@@ -9,9 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <PushKit/PushKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate>
@interface AppDelegate : UIResponder <UIApplicationDelegate, PKPushRegistryDelegate>
@property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) UIWindow *window;
......
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RCTBundleURLProvider.h" #import <React/RCTBundleURLProvider.h>
#import "RCTRootView.h" #import <React/RCTRootView.h>
#import "RNNotifications.h" #import <RNNotifications/RNNotifications.h>
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{
NSURL *jsCodeLocation; NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"NotificationsExampleApp" moduleName:@"NotificationsExampleApp"
...@@ -34,54 +24,18 @@ ...@@ -34,54 +24,18 @@
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
return YES;
}
// PushKit API Example
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
[RNNotifications didUpdatePushCredentials:credentials forType:type];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
[RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}
[RNNotifications startMonitorNotifications];
// Required to register for notifications return YES;
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNNotifications didRegisterUserNotificationSettings:notificationSettings];
} }
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
{
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
} }
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
// Required for the notification event. [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification {
[RNNotifications didReceiveRemoteNotification:notification];
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNNotifications didReceiveLocalNotification:notification];
}
// Required for the notification actions.
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
[RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler];
} }
@end @end
{ {
"images" : [ "images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{ {
"idiom" : "iphone", "idiom" : "iphone",
"size" : "29x29", "size" : "29x29",
...@@ -29,6 +39,11 @@ ...@@ -29,6 +39,11 @@
"idiom" : "iphone", "idiom" : "iphone",
"size" : "60x60", "size" : "60x60",
"scale" : "3x" "scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
} }
], ],
"info" : { "info" : {
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
{
"name": "NotificationsExampleApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "16.6.0-alpha.8af6728",
"react-native": "^0.57.4",
"react-native-notifications": "latest"
}
}
import {NativeModules, DeviceEventEmitter} from "react-native";
import NotificationAndroid from "./notification";
const RNNotifications = NativeModules.WixRNNotifications;
let notificationReceivedListener;
let notificationReceivedInForegroundListener;
let notificationOpenedListener;
let registrationTokenUpdateListener;
export class NotificationsAndroid {
static setNotificationOpenedListener(listener) {
notificationOpenedListener = DeviceEventEmitter.addListener("notificationOpened", (notification) => listener(new NotificationAndroid(notification)));
}
static clearNotificationOpenedListener() {
if (notificationOpenedListener) {
notificationOpenedListener.remove();
notificationOpenedListener = null;
}
}
static setNotificationReceivedListener(listener) {
notificationReceivedListener = DeviceEventEmitter.addListener("notificationReceived", (notification) => listener(new NotificationAndroid(notification)));
}
static setNotificationReceivedInForegroundListener(listener) {
notificationReceivedInForegroundListener = DeviceEventEmitter.addListener("notificationReceivedInForeground", (notification) => listener(new NotificationAndroid(notification)));
}
static clearNotificationReceivedListener() {
if (notificationReceivedListener) {
notificationReceivedListener.remove();
notificationReceivedListener = null;
}
}
static clearNotificationReceivedInForegroundListener() {
if (notificationReceivedInForegroundListener) {
notificationReceivedInForegroundListener.remove();
notificationReceivedInForegroundListener = null;
}
}
static setRegistrationTokenUpdateListener(listener) {
registrationTokenUpdateListener = DeviceEventEmitter.addListener("remoteNotificationsRegistered", listener);
}
static clearRegistrationTokenUpdateListener() {
if (registrationTokenUpdateListener) {
registrationTokenUpdateListener.remove();
registrationTokenUpdateListener = null;
}
}
static async isRegisteredForRemoteNotifications() {
return await RNNotifications.isRegisteredForRemoteNotifications();
}
static refreshToken() {
RNNotifications.refreshToken();
}
static localNotification(notification: Object) {
const id = Math.random() * 100000000 | 0; // Bitwise-OR forces value onto a 32bit limit
RNNotifications.postLocalNotification(notification, id);
return id;
}
static cancelLocalNotification(id) {
RNNotifications.cancelLocalNotification(id);
}
}
export class PendingNotifications {
static getInitialNotification() {
return RNNotifications.getInitialNotification()
.then((rawNotification) => {
return rawNotification ? new NotificationAndroid(rawNotification) : undefined;
});
}
}
/**
* @flow
*/
"use strict";
import { NativeModules, DeviceEventEmitter, NativeAppEventEmitter } from "react-native";
import Map from "core-js/library/es6/map";
import uuid from "uuid";
const NativeRNNotifications = NativeModules.RNNotifications; // eslint-disable-line no-unused-vars
import IOSNotification from "./notification.ios";
export const DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT = "remoteNotificationsRegistered";
export const DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT = "remoteNotificationsRegistrationFailed";
export const DEVICE_PUSH_KIT_REGISTERED_EVENT = "pushKitRegistered";
export const DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT = "notificationReceivedForeground";
export const DEVICE_NOTIFICATION_RECEIVED_BACKGROUND_EVENT = "notificationReceivedBackground";
export const DEVICE_NOTIFICATION_OPENED_EVENT = "notificationOpened";
const DEVICE_NOTIFICATION_ACTION_RECEIVED = "notificationActionReceived";
const _exportedEvents = [
DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT,
DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT,
DEVICE_PUSH_KIT_REGISTERED_EVENT,
DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT,
DEVICE_NOTIFICATION_RECEIVED_BACKGROUND_EVENT,
DEVICE_NOTIFICATION_OPENED_EVENT
];
const _notificationHandlers = new Map();
const _actionHandlers = new Map();
let _actionListener;
export class NotificationAction {
options: Object;
handler: Function;
constructor(options: Object, handler: Function) {
this.options = options;
this.handler = handler;
}
}
export class NotificationCategory {
options: Object;
constructor(options: Object) {
this.options = options;
}
}
export default class NotificationsIOS {
/**
* Attaches a listener to remote notification events while the app is running
* in the foreground or the background.
*
* Valid events are:
*
* - `remoteNotificationsRegistered` : Fired when the user registers for remote notifications. The handler will be invoked with a hex string representing the deviceToken.
* - `notificationReceivedForeground` : Fired when a notification (local / remote) is received when app is on foreground state.
* - `notificationReceivedBackground`: Fired when a background notification is received.
* - `notificationOpened`: Fired when a notification (local / remote) is opened.
*/
static addEventListener(type: string, handler: Function) {
if (_exportedEvents.indexOf(type) !== -1) {
let listener;
if (type === DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT) {
listener = DeviceEventEmitter.addListener(
DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT,
registration => handler(registration.deviceToken)
);
} else if (type === DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT) {
listener = DeviceEventEmitter.addListener(
DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT,
error => handler(error)
);
} else if (type === DEVICE_PUSH_KIT_REGISTERED_EVENT) {
listener = DeviceEventEmitter.addListener(
DEVICE_PUSH_KIT_REGISTERED_EVENT,
registration => handler(registration.pushKitToken)
);
} else {
listener = DeviceEventEmitter.addListener(
type,
notification => handler(new IOSNotification(notification))
);
}
_notificationHandlers.set(handler, listener);
}
}
/**
* Removes the event listener. Do this in `componentWillUnmount` to prevent
* memory leaks
*/
static removeEventListener(type: string, handler: Function) {
if (_exportedEvents.indexOf(type) !== -1) {
const listener = _notificationHandlers.get(handler);
if (listener) {
listener.remove();
_notificationHandlers.delete(handler);
}
}
}
static _actionHandlerDispatcher(action: Object) {
const actionHandler = _actionHandlers.get(action.identifier);
if (actionHandler) {
action.notification = new IOSNotification(action.notification);
actionHandler(action, () => {
NativeRNNotifications.completionHandler(action.completionKey);
});
}
}
/**
* Sets the notification categories
*/
static requestPermissions(categories: Array<NotificationCategory>) {
let notificationCategories = [];
if (categories) {
// subscribe once for all actions
_actionListener = NativeAppEventEmitter.addListener(DEVICE_NOTIFICATION_ACTION_RECEIVED, this._actionHandlerDispatcher.bind(this));
notificationCategories = categories.map(category => {
return Object.assign({}, category.options, {
actions: category.options.actions.map(action => {
// subscribe to action event
_actionHandlers.set(action.options.identifier, action.handler);
return action.options;
})
});
});
}
NativeRNNotifications.requestPermissionsWithCategories(notificationCategories);
}
/**
* Unregister for all remote notifications received via Apple Push Notification service.
*/
static abandonPermissions() {
NativeRNNotifications.abandonPermissions();
}
/**
* Removes the event listener. Do this in `componentWillUnmount` to prevent
* memory leaks
*/
static resetCategories() {
if (_actionListener) {
_actionListener.remove();
}
_actionHandlers.clear();
}
static getBadgesCount(callback: Function) {
NativeRNNotifications.getBadgesCount(callback);
}
static setBadgesCount(count: number) {
NativeRNNotifications.setBadgesCount(count);
}
static registerPushKit() {
NativeRNNotifications.registerPushKit();
}
static backgroundTimeRemaining(callback: Function) {
NativeRNNotifications.backgroundTimeRemaining(callback);
}
static consumeBackgroundQueue() {
NativeRNNotifications.consumeBackgroundQueue();
}
static log(message: string) {
NativeRNNotifications.log(message);
}
static async getInitialNotification() {
const notification = await NativeRNNotifications.getInitialNotification();
if (notification) {
return new IOSNotification(notification);
} else {
return undefined;
}
}
/**
* Presenting local notification
*
* notification is an object containing:
*
* - `alertBody` : The message displayed in the notification alert.
* - `alertTitle` : The message title displayed in the notification.
* - `alertAction` : The "action" displayed beneath an actionable notification. Defaults to "view";
* - `soundName` : The sound played when the notification is fired (optional).
* - `silent` : If true, the notification sound will be suppressed (optional).
* - `category` : The category of this notification, required for actionable notifications (optional).
* - `userInfo` : An optional object containing additional notification data.
* - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately.
*/
static localNotification(notification: Object) {
const notificationId = uuid.v4();
NativeRNNotifications.localNotification(notification, notificationId);
return notificationId;
}
static cancelLocalNotification(notificationId: String) {
NativeRNNotifications.cancelLocalNotification(notificationId);
}
static cancelAllLocalNotifications() {
NativeRNNotifications.cancelAllLocalNotifications();
}
static isRegisteredForRemoteNotifications() {
return NativeRNNotifications.isRegisteredForRemoteNotifications();
}
static checkPermissions() {
return NativeRNNotifications.checkPermissions();
}
/**
* Remove all delivered notifications from Notification Center
*/
static removeAllDeliveredNotifications() {
return NativeRNNotifications.removeAllDeliveredNotifications();
}
/**
* Removes the specified notifications from Notification Center
*
* @param identifiers Array of notification identifiers
*/
static removeDeliveredNotifications(identifiers: Array<String>) {
return NativeRNNotifications.removeDeliveredNotifications(identifiers);
}
/**
* Provides you with a list of the app’s notifications that are still displayed in Notification Center
*
* @param callback Function which receive an array of delivered notifications
*
* A delivered notification is an object containing:
*
* - `identifier` : The identifier of this notification.
* - `alertBody` : The message displayed in the notification alert.
* - `alertTitle` : The message title displayed in the notification.
* - `category` : The category of this notification, if has one.
* - `userInfo` : An optional object containing additional notification data.
* - `thread-id` : The thread identifier of this notification, if has one.
* - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately.
*/
static getDeliveredNotifications(callback: (notifications: Array<Object>) => void) {
return NativeRNNotifications.getDeliveredNotifications(callback);
}
}
require('./example/index');
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>android</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
connection.project.dir=
eclipse.preferences.version=1
/build
google-services.json
\ No newline at end of file
import groovy.json.JsonSlurper
apply plugin: 'com.android.library'
apply from: '../prepare-robolectric.gradle'
String resolveFlavor() {
def packageSlurper = new JsonSlurper()
def reactNativePackageJson = packageSlurper.parse file('../../../node_modules/react-native/package.json')
def reactNativeVersion = reactNativePackageJson.version
List versionComponents = reactNativeVersion.tokenize('.')
if (versionComponents[1].toInteger() < 60) {
return "reactNative59"
} else {
return "reactNative60"
}
}
android {
compileSdkVersion 27
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
}
}
testOptions {
unitTests.all { t ->
reports {
html.enabled true
}
testLogging {
events "PASSED", "SKIPPED", "FAILED", "standardOut", "standardError"
}
afterSuite { desc, result ->
if (!desc.parent) { // will match the outermost suite
def output = " ${result.resultType} (${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped) "
def repeatLength = output.length()
println '\n\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) + '\n'
println "see report at file://${t.reports.html.destination}/index.html"
}
}
}
}
flavorDimensions "RNNotifications.reactNativeVersion"
productFlavors {
reactNative59 {
dimension "RNNotifications.reactNativeVersion"
}
reactNative60 {
dimension "RNNotifications.reactNativeVersion"
}
}
def flavor = resolveFlavor()
variantFilter { variant ->
def names = variant.flavors*.name
if (!names.contains(flavor)) {
setIgnore(true)
}
}
}
dependencies {
implementation "com.google.firebase:firebase-messaging:17.3.0"
implementation 'com.facebook.react:react-native:+'
// tests
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.5.1'
testImplementation 'org.assertj:assertj-core:3.8.0'
testImplementation 'com.squareup.assertj:assertj-android:1.1.1'
testImplementation 'org.mockito:mockito-core:2.13.0'
}
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed Jan 30 11:37:18 IST 2019
sdk.dir=/Users/yogevbd/android-sdk-macosx
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
package="com.wix.reactnativenotifications"> package="com.wix.reactnativenotifications">
<!-- <!--
Permissions required for enabling GCM. Permissions required for enabling FCM.
--> -->
<permission <permission
android:name="${applicationId}.permission.C2D_MESSAGE" android:name="${applicationId}.permission.C2D_MESSAGE"
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" /> <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<!-- Ref: http://stackoverflow.com/questions/13602190/java-lang-securityexception-requires-vibrate-permission-on-jelly-bean-4-2 --> <!-- Ref: http://stackoverflow.com/questions/13602190/java-lang-securityexception-requires-vibrate-permission-on-jelly-bean-4-2 -->
<uses-permission android:name="android.permission.VIBRATE" android:maxSdkVersion="18" /> <uses-permission android:name="android.permission.VIBRATE" />
<application> <application>
...@@ -21,39 +21,16 @@ ...@@ -21,39 +21,16 @@
--> -->
<service android:name=".core.ProxyService"/> <service android:name=".core.ProxyService"/>
<!--
Google's ready-to-use GcmReceiver.
1. Awaits actual GCM messages (e.g. push notifications) and invokes the GCM service with the concrete content.
2. Awaits instance-ID/token refresh requests from the GCM and invokes the Instance-ID listener service.
-->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<!-- Dispatched by the GcmReceiver when messages are received. -->
<service <service
android:name="com.wix.reactnativenotifications.gcm.GcmMessageHandlerService" android:name=".fcm.FcmInstanceIdListenerService">
android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</service>
<!-- Dispatched by the GcmReceiver. Starts the designated refresh-handling service. -->
<service
android:name=".gcm.GcmInstanceIdListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID" />
</intent-filter> </intent-filter>
</service> </service>
<service <service
android:name=".gcm.GcmInstanceIdRefreshHandlerService" android:name=".fcm.FcmInstanceIdRefreshHandlerService"
android:exported="false" /> android:exported="false" />
</application> </application>
......
...@@ -2,11 +2,9 @@ package com.wix.reactnativenotifications; ...@@ -2,11 +2,9 @@ package com.wix.reactnativenotifications;
public interface Defs { public interface Defs {
String LOGTAG = "ReactNativeNotifs"; String LOGTAG = "ReactNativeNotifs";
String GCM_SENDER_ID_ATTR_NAME = "com.wix.reactnativenotifications.gcmSenderId";
String TOKEN_RECEIVED_EVENT_NAME = "remoteNotificationsRegistered"; String TOKEN_RECEIVED_EVENT_NAME = "remoteNotificationsRegistered";
String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived"; String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
String NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME = "notificationReceivedInForeground";
String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened"; String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
} }
...@@ -5,58 +5,74 @@ import android.app.Application; ...@@ -5,58 +5,74 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder; import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder;
import com.wix.reactnativenotifications.core.InitialNotificationHolder; import com.wix.reactnativenotifications.core.InitialNotificationHolder;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
import com.wix.reactnativenotifications.core.ReactAppLifecycleFacade; import com.wix.reactnativenotifications.core.ReactAppLifecycleFacade;
import com.wix.reactnativenotifications.core.notification.IPushNotification; import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotification; import com.wix.reactnativenotifications.core.notification.PushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotificationProps; import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer; import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer;
import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer; import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer;
import com.wix.reactnativenotifications.gcm.GcmInstanceIdRefreshHandlerService; import com.wix.reactnativenotifications.fcm.FcmInstanceIdRefreshHandlerService;
import static com.wix.reactnativenotifications.Defs.LOGTAG; import static com.wix.reactnativenotifications.Defs.LOGTAG;
public class RNNotificationsModule extends ReactContextBaseJavaModule implements AppLifecycleFacade.AppVisibilityListener, Application.ActivityLifecycleCallbacks { public class RNNotificationsModule extends ReactContextBaseJavaModule implements ActivityEventListener {
public RNNotificationsModule(Application application, ReactApplicationContext reactContext) { public RNNotificationsModule(Application application, ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
if (AppLifecycleFacadeHolder.get() instanceof ReactAppLifecycleFacade) { if (AppLifecycleFacadeHolder.get() instanceof ReactAppLifecycleFacade) {
((ReactAppLifecycleFacade) AppLifecycleFacadeHolder.get()).init(reactContext); ((ReactAppLifecycleFacade) AppLifecycleFacadeHolder.get()).init(reactContext);
} }
AppLifecycleFacadeHolder.get().addVisibilityListener(this);
application.registerActivityLifecycleCallbacks(this); reactContext.addActivityEventListener(this);
} }
@Override @Override
public String getName() { public String getName() {
return "WixRNNotifications"; return "RNBridgeModule";
} }
@Override @Override
public void initialize() { public void initialize() {
Log.d(LOGTAG, "Native module init"); Log.d(LOGTAG, "Native module init");
startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_IS_APP_INIT); startFcmIntentService(FcmInstanceIdRefreshHandlerService.EXTRA_IS_APP_INIT);
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onAppInit(); notificationsDrawer.onAppInit();
} }
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
}
@Override
public void onNewIntent(Intent intent) {
if (NotificationIntentAdapter.canHandleIntent(intent)) {
Bundle notificationData = intent.getExtras();
final IPushNotification notification = PushNotification.get(getReactApplicationContext().getApplicationContext(), notificationData);
if (notification != null) {
notification.onOpened();
}
}
}
@ReactMethod @ReactMethod
public void refreshToken() { public void refreshToken() {
Log.d(LOGTAG, "Native method invocation: refreshToken()"); Log.d(LOGTAG, "Native method invocation: refreshToken()");
startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_MANUAL_REFRESH); startFcmIntentService(FcmInstanceIdRefreshHandlerService.EXTRA_MANUAL_REFRESH);
} }
@ReactMethod @ReactMethod
...@@ -91,54 +107,19 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements ...@@ -91,54 +107,19 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements
} }
@ReactMethod @ReactMethod
public void isRegisteredForRemoteNotifications(Promise promise) { public void setCategories(ReadableArray categories) {
boolean hasPermission = NotificationManagerCompat.from(getReactApplicationContext()).areNotificationsEnabled();
promise.resolve(new Boolean(hasPermission));
}
@Override
public void onAppVisible() {
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onAppVisible();
}
@Override
public void onAppNotVisible() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onNewActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
} }
@Override @ReactMethod
public void onActivityStopped(Activity activity) { public void isRegisteredForRemoteNotifications(Promise promise) {
} boolean hasPermission = NotificationManagerCompatFacade.from(getReactApplicationContext()).areNotificationsEnabled();
promise.resolve(new Boolean(hasPermission));
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
} }
protected void startGcmIntentService(String extraFlag) { protected void startFcmIntentService(String extraFlag) {
final Context appContext = getReactApplicationContext().getApplicationContext(); final Context appContext = getReactApplicationContext().getApplicationContext();
final Intent tokenFetchIntent = new Intent(appContext, GcmInstanceIdRefreshHandlerService.class); final Intent tokenFetchIntent = new Intent(appContext, FcmInstanceIdRefreshHandlerService.class);
tokenFetchIntent.putExtra(extraFlag, true); tokenFetchIntent.putExtra(extraFlag, true);
appContext.startService(tokenFetchIntent); appContext.startService(tokenFetchIntent);
} }
......
package com.wix.reactnativenotifications;
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.google.firebase.FirebaseApp;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotification;
import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer;
import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.wix.reactnativenotifications.Defs.LOGTAG;
public class RNNotificationsPackage implements ReactPackage, AppLifecycleFacade.AppVisibilityListener, Application.ActivityLifecycleCallbacks {
private final Application mApplication;
public RNNotificationsPackage(Application application) {
mApplication = application;
FirebaseApp.initializeApp(application.getApplicationContext());
AppLifecycleFacadeHolder.get().addVisibilityListener(this);
application.registerActivityLifecycleCallbacks(this);
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new RNNotificationsModule(mApplication, reactContext));
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public void onAppVisible() {
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(mApplication.getApplicationContext());
notificationsDrawer.onAppVisible();
}
@Override
public void onAppNotVisible() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(mApplication.getApplicationContext());
notificationsDrawer.onNewActivity(activity);
Intent intent = activity.getIntent();
if (NotificationIntentAdapter.canHandleIntent(intent)) {
Bundle notificationData = intent.getExtras();
final IPushNotification pushNotification = PushNotification.get(mApplication.getApplicationContext(), notificationData);
if (pushNotification != null) {
pushNotification.onOpened();
}
}
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
package com.wix.reactnativenotifications.core; package com.wix.reactnativenotifications.core;
import android.support.annotation.Nullable;
import com.wix.reactnativenotifications.core.notification.PushNotificationProps; import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
...@@ -32,7 +31,6 @@ public class InitialNotificationHolder { ...@@ -32,7 +31,6 @@ public class InitialNotificationHolder {
mNotification = null; mNotification = null;
} }
@Nullable
public PushNotificationProps get() { public PushNotificationProps get() {
return mNotification; return mNotification;
} }
......
...@@ -18,4 +18,15 @@ public class NotificationIntentAdapter { ...@@ -18,4 +18,15 @@ public class NotificationIntentAdapter {
public static Bundle extractPendingNotificationDataFromIntent(Intent intent) { public static Bundle extractPendingNotificationDataFromIntent(Intent intent) {
return intent.getBundleExtra(PUSH_NOTIFICATION_EXTRA_NAME); return intent.getBundleExtra(PUSH_NOTIFICATION_EXTRA_NAME);
} }
public static boolean canHandleIntent(Intent intent) {
if (intent != null) {
Bundle notificationData = intent.getExtras();
if (notificationData != null && intent.hasExtra(PUSH_NOTIFICATION_EXTRA_NAME)) {
return true;
}
}
return false;
}
} }
...@@ -8,6 +8,8 @@ import android.content.Context; ...@@ -8,6 +8,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Debug;
import android.util.Log;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.AppLaunchHelper;
...@@ -21,7 +23,6 @@ import com.wix.reactnativenotifications.core.ProxyService; ...@@ -21,7 +23,6 @@ import com.wix.reactnativenotifications.core.ProxyService;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME;
public class PushNotification implements IPushNotification { public class PushNotification implements IPushNotification {
...@@ -62,9 +63,6 @@ public class PushNotification implements IPushNotification { ...@@ -62,9 +63,6 @@ public class PushNotification implements IPushNotification {
public void onReceived() throws InvalidNotificationException { public void onReceived() throws InvalidNotificationException {
postNotification(null); postNotification(null);
notifyReceivedToJS(); notifyReceivedToJS();
if (mAppLifecycleFacade.isAppVisible()) {
notifiyReceivedForegroundNotificationToJS();
}
} }
@Override @Override
...@@ -149,11 +147,18 @@ public class PushNotification implements IPushNotification { ...@@ -149,11 +147,18 @@ public class PushNotification implements IPushNotification {
final Notification.Builder notification = new Notification.Builder(mContext) final Notification.Builder notification = new Notification.Builder(mContext)
.setContentTitle(mNotificationProps.getTitle()) .setContentTitle(mNotificationProps.getTitle())
.setContentText(mNotificationProps.getBody()) .setContentText(mNotificationProps.getBody())
.setSmallIcon(mContext.getApplicationInfo().icon)
.setContentIntent(intent) .setContentIntent(intent)
.setDefaults(Notification.DEFAULT_ALL) .setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true); .setAutoCancel(true);
int resourceID = mContext.getResources().getIdentifier("notification_icon", "drawable", mContext.getPackageName());
if (resourceID != 0) {
notification.setSmallIcon(resourceID);
} else {
notification.setSmallIcon(mContext.getApplicationInfo().icon);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME, CHANNEL_NAME,
...@@ -190,10 +195,6 @@ public class PushNotification implements IPushNotification { ...@@ -190,10 +195,6 @@ public class PushNotification implements IPushNotification {
mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext()); mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
} }
private void notifiyReceivedForegroundNotificationToJS() {
mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
}
private void notifyOpenedToJS() { private void notifyOpenedToJS() {
mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext()); mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
} }
......
...@@ -6,16 +6,6 @@ public class PushNotificationProps { ...@@ -6,16 +6,6 @@ public class PushNotificationProps {
protected Bundle mBundle; protected Bundle mBundle;
public PushNotificationProps() {
mBundle = new Bundle();
}
public PushNotificationProps(String title, String body) {
mBundle = new Bundle();
mBundle.putString("title", title);
mBundle.putString("body", body);
}
public PushNotificationProps(Bundle bundle) { public PushNotificationProps(Bundle bundle) {
mBundle = bundle; mBundle = bundle;
} }
......
package com.wix.reactnativenotifications.gcm; package com.wix.reactnativenotifications.fcm;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import com.google.android.gms.gcm.GcmListenerService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.wix.reactnativenotifications.core.notification.IPushNotification; import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotification; import com.wix.reactnativenotifications.core.notification.PushNotification;
import static com.wix.reactnativenotifications.Defs.LOGTAG; import static com.wix.reactnativenotifications.Defs.LOGTAG;
public class GcmMessageHandlerService extends GcmListenerService { /**
* Instance-ID + token refreshing handling service. Contacts the FCM to fetch the updated token.
*
* @author amitd
*/
public class FcmInstanceIdListenerService extends FirebaseMessagingService {
@Override @Override
public void onMessageReceived(String s, Bundle bundle) { public void onMessageReceived(RemoteMessage message){
Log.d(LOGTAG, "New message from GCM: " + bundle); Bundle bundle = message.toIntent().getExtras();
Log.d(LOGTAG, "New message from FCM: " + bundle);
try { try {
final IPushNotification notification = PushNotification.get(getApplicationContext(), bundle); final IPushNotification notification = PushNotification.get(getApplicationContext(), bundle);
notification.onReceived(); notification.onReceived();
} catch (IPushNotification.InvalidNotificationException e) { } catch (IPushNotification.InvalidNotificationException e) {
// A GCM message, yes - but not the kind we know how to work with. // An FCM message, yes - but not the kind we know how to work with.
Log.v(LOGTAG, "GCM message handling aborted", e); Log.v(LOGTAG, "FCM message handling aborted", e);
} }
} }
} }
package com.wix.reactnativenotifications.gcm; package com.wix.reactnativenotifications.fcm;
import android.app.IntentService; import android.app.IntentService;
import android.content.Intent; import android.content.Intent;
public class GcmInstanceIdRefreshHandlerService extends IntentService { public class FcmInstanceIdRefreshHandlerService extends IntentService {
public static String EXTRA_IS_APP_INIT = "isAppInit"; public static String EXTRA_IS_APP_INIT = "isAppInit";
public static String EXTRA_MANUAL_REFRESH = "doManualRefresh"; public static String EXTRA_MANUAL_REFRESH = "doManualRefresh";
public GcmInstanceIdRefreshHandlerService() { public FcmInstanceIdRefreshHandlerService() {
super(GcmInstanceIdRefreshHandlerService.class.getSimpleName()); super(FcmInstanceIdRefreshHandlerService.class.getSimpleName());
} }
@Override @Override
protected void onHandleIntent(Intent intent) { protected void onHandleIntent(Intent intent) {
IGcmToken gcmToken = GcmToken.get(this); IFcmToken fcmToken = FcmToken.get(this);
if (gcmToken == null) { if (fcmToken == null) {
return; return;
} }
if (intent.getBooleanExtra(EXTRA_IS_APP_INIT, false)) { if (intent.getBooleanExtra(EXTRA_IS_APP_INIT, false)) {
gcmToken.onAppReady(); fcmToken.onAppReady();
} else if (intent.getBooleanExtra(EXTRA_MANUAL_REFRESH, false)) { } else if (intent.getBooleanExtra(EXTRA_MANUAL_REFRESH, false)) {
gcmToken.onManualRefresh(); fcmToken.onManualRefresh();
} else { } else {
gcmToken.onNewTokenReady(); fcmToken.onNewTokenReady();
} }
} }
} }
package com.wix.reactnativenotifications.gcm; package com.wix.reactnativenotifications.fcm;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.android.gms.gcm.GoogleCloudMessaging; import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.iid.InstanceID; import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import static com.wix.reactnativenotifications.Defs.GCM_SENDER_ID_ATTR_NAME;
import static com.wix.reactnativenotifications.Defs.LOGTAG; import static com.wix.reactnativenotifications.Defs.LOGTAG;
import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME; import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME;
public class GcmToken implements IGcmToken { public class FcmToken implements IFcmToken {
final protected Context mAppContext; final protected Context mAppContext;
protected static String sToken; protected static String sToken;
protected GcmToken(Context appContext) { protected FcmToken(Context appContext) {
if (!(appContext instanceof ReactApplication)) { if (!(appContext instanceof ReactApplication)) {
throw new IllegalStateException("Application instance isn't a react-application"); throw new IllegalStateException("Application instance isn't a react-application");
} }
mAppContext = appContext; mAppContext = appContext;
} }
public static IGcmToken get(Context context) { public static IFcmToken get(Context context) {
Context appContext = context.getApplicationContext(); Context appContext = context.getApplicationContext();
if (appContext instanceof INotificationsGcmApplication) { if (appContext instanceof INotificationsFcmApplication) {
return ((INotificationsGcmApplication) appContext).getGcmToken(context); return ((INotificationsFcmApplication) appContext).getFcmToken(context);
} }
return new GcmToken(appContext); return new FcmToken(appContext);
} }
@Override @Override
...@@ -73,51 +70,14 @@ public class GcmToken implements IGcmToken { ...@@ -73,51 +70,14 @@ public class GcmToken implements IGcmToken {
} }
protected void refreshToken() { protected void refreshToken() {
try { FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( new OnSuccessListener<InstanceIdResult>() {
sToken = getNewToken(); @Override
} catch (Exception e) { public void onSuccess(InstanceIdResult instanceIdResult) {
Log.e(LOGTAG, "Failed to retrieve new token", e); sToken = instanceIdResult.getToken();
return; Log.i(LOGTAG, "FCM has a new token" + "=" + sToken);
}
sendTokenToJS(); sendTokenToJS();
} }
});
@NonNull
protected String getNewToken() throws Exception {
final InstanceID instanceId = InstanceID.getInstance(mAppContext);
Log.d(LOGTAG, "GCM is refreshing token... instanceId=" + instanceId.getId());
// TODO why is this needed?
GoogleCloudMessaging.getInstance(mAppContext).close();
try {
final String registrationToken = instanceId.getToken(getSenderId(), GoogleCloudMessaging.INSTANCE_ID_SCOPE);
Log.i(LOGTAG, "GCM has a new token: instanceId=" + instanceId.getId() + ", token=" + registrationToken);
return registrationToken;
} catch (Exception e) {
throw new Exception("FATAL: Failed to fetch a fresh new token, instanceId=" + instanceId.getId(), e);
}
}
protected String getSenderId() {
final String senderId = getSenderIdFromManifest();
if (senderId == null) {
throw new IllegalStateException("Sender ID not found in manifest. Did you forget to add it as the value of a '"+GCM_SENDER_ID_ATTR_NAME+"' meta-data field?");
}
return senderId;
}
protected String getSenderIdFromManifest() {
final ApplicationInfo appInfo;
try {
appInfo = mAppContext.getPackageManager().getApplicationInfo(mAppContext.getPackageName(), PackageManager.GET_META_DATA);
return appInfo.metaData.getString(GCM_SENDER_ID_ATTR_NAME);
} catch (PackageManager.NameNotFoundException e) {
// Should REALLY never happen cause we're querying for our own package.
Log.e(LOGTAG, "Failed to resolve sender ID from manifest", e);
return null;
}
} }
protected void sendTokenToJS() { protected void sendTokenToJS() {
......
package com.wix.reactnativenotifications.gcm; package com.wix.reactnativenotifications.fcm;
public interface IGcmToken { public interface IFcmToken {
/** /**
* Handle an event where we've been notified of a that a fresh token is now available from Google. * Handle an event where we've been notified of a that a fresh token is now available from Google.
......
package com.wix.reactnativenotifications.fcm;
import android.content.Context;
public interface INotificationsFcmApplication {
IFcmToken getFcmToken(Context context);
}
package com.wix.reactnativenotifications;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}
package com.wix.reactnativenotifications;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}
...@@ -6,6 +6,8 @@ import android.app.NotificationManager; ...@@ -6,6 +6,8 @@ import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
...@@ -54,6 +56,8 @@ public class PushNotificationTest { ...@@ -54,6 +56,8 @@ public class PushNotificationTest {
@Mock private AppLifecycleFacade mAppLifecycleFacade; @Mock private AppLifecycleFacade mAppLifecycleFacade;
@Mock private AppLaunchHelper mAppLaunchHelper; @Mock private AppLaunchHelper mAppLaunchHelper;
@Mock private JsIOHelper mJsIOHelper; @Mock private JsIOHelper mJsIOHelper;
@Mock private Resources mResources;
@Mock private PackageManager mPackageManager;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
...@@ -68,7 +72,8 @@ public class PushNotificationTest { ...@@ -68,7 +72,8 @@ public class PushNotificationTest {
ApplicationInfo ai = mock(ApplicationInfo.class); ApplicationInfo ai = mock(ApplicationInfo.class);
when(mContext.getApplicationInfo()).thenReturn(ai); when(mContext.getApplicationInfo()).thenReturn(ai);
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(mNotificationManager); when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(mNotificationManager);
} }
......
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.gms:google-services:4.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../../node_modules/react-native/android"
}
}
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
\ No newline at end of file
#Thu Jan 04 11:01:32 EET 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
def robolectricDependenciesFolder = new File(rootProject.buildDir, "robolectric-3.5.1-dependencies")
configurations.create('robolectricRuntime')
dependencies {
testImplementation "org.khronos:opengl-api:gl1.1-android-2.1_r1"
robolectricRuntime "org.robolectric:android-all:8.1.0-robolectric-4402310"
robolectricRuntime "org.robolectric:annotations:3.5.1"
robolectricRuntime "org.robolectric:junit:3.5.1"
robolectricRuntime "org.robolectric:resources:3.5.1"
robolectricRuntime "org.robolectric:sandbox:3.5.1"
robolectricRuntime "org.robolectric:utils:3.5.1"
robolectricRuntime "org.robolectric:shadows-framework:3.5.1"
}
rootProject.task(type: Copy, overwrite: true, "downloadRobolectricDependencies") {
println "downloadRobolectricDependencies into " + robolectricDependenciesFolder
from configurations.robolectricRuntime
into robolectricDependenciesFolder
}
project.afterEvaluate {
tasks.all {
if (it.name.startsWith("test")) {
it.dependsOn(rootProject.tasks.findByName("downloadRobolectricDependencies"))
}
}
}
android {
testOptions {
unitTests {
includeAndroidResources = true
}
unitTests.all {
systemProperty 'robolectric.offline', 'true'
systemProperty 'robolectric.dependency.dir', robolectricDependenciesFolder
}
}
}
/*
* Copyright (c) 2009-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@class OCObserverMockObject;
@interface NSNotificationCenter(OCMAdditions)
- (void)addMockObserver:(OCObserverMockObject *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender;
@end
/*
* Copyright (c) 2009-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@interface OCMArg : NSObject
// constraining arguments
+ (id)any;
+ (SEL)anySelector;
+ (void *)anyPointer;
+ (id __autoreleasing *)anyObjectRef;
+ (id)isNil;
+ (id)isNotNil;
+ (id)isEqual:(id)value;
+ (id)isNotEqual:(id)value;
+ (id)isKindOfClass:(Class)cls;
+ (id)checkWithSelector:(SEL)selector onObject:(id)anObject;
+ (id)checkWithBlock:(BOOL (^)(id obj))block;
// manipulating arguments
+ (id *)setTo:(id)value;
+ (void *)setToValue:(NSValue *)value;
+ (id)invokeBlock;
+ (id)invokeBlockWithArgs:(id)first,... NS_REQUIRES_NIL_TERMINATION;
+ (id)defaultValue;
// internal use only
+ (id)resolveSpecialValues:(NSValue *)value;
@end
#define OCMOCK_ANY [OCMArg any]
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define OCMOCK_VALUE(variable) \
({ __typeof__(variable) __v = (variable); [NSValue value:&__v withObjCType:@encode(__typeof__(__v))]; })
#else
#define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(__typeof__(variable))]
#endif
/*
* Copyright (c) 2007-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@interface OCMConstraint : NSObject
+ (instancetype)constraint;
- (BOOL)evaluate:(id)value;
// if you are looking for any, isNil, etc, they have moved to OCMArg
// try to use [OCMArg checkWith...] instead of the constraintWith... methods below
+ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject;
+ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue;
@end
@interface OCMAnyConstraint : OCMConstraint
@end
@interface OCMIsNilConstraint : OCMConstraint
@end
@interface OCMIsNotNilConstraint : OCMConstraint
@end
@interface OCMIsNotEqualConstraint : OCMConstraint
{
@public
id testValue;
}
@end
@interface OCMInvocationConstraint : OCMConstraint
{
@public
NSInvocation *invocation;
}
@end
@interface OCMBlockConstraint : OCMConstraint
{
BOOL (^block)(id);
}
- (instancetype)initWithConstraintBlock:(BOOL (^)(id))block;
@end
#define CONSTRAINT(aSelector) [OCMConstraint constraintWithSelector:aSelector onObject:self]
#define CONSTRAINTV(aSelector, aValue) [OCMConstraint constraintWithSelector:aSelector onObject:self withValue:(aValue)]
/*
* Copyright (c) 2014-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
#if defined(__cplusplus)
#define OCMOCK_EXTERN extern "C"
#else
#define OCMOCK_EXTERN extern
#endif
OCMOCK_EXTERN BOOL OCMIsObjectType(const char *objCType);
/*
* Copyright (c) 2014-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
#import "OCMFunctions.h"
@interface OCMLocation : NSObject
{
id testCase;
NSString *file;
NSUInteger line;
}
+ (instancetype)locationWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine;
- (instancetype)initWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine;
- (id)testCase;
- (NSString *)file;
- (NSUInteger)line;
@end
OCMOCK_EXTERN OCMLocation *OCMMakeLocation(id testCase, const char *file, int line);
/*
* Copyright (c) 2014-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@class OCMLocation;
@class OCMRecorder;
@class OCMStubRecorder;
@class OCMockObject;
@interface OCMMacroState : NSObject
{
OCMRecorder *recorder;
}
+ (void)beginStubMacro;
+ (OCMStubRecorder *)endStubMacro;
+ (void)beginExpectMacro;
+ (OCMStubRecorder *)endExpectMacro;
+ (void)beginRejectMacro;
+ (OCMStubRecorder *)endRejectMacro;
+ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation;
+ (void)endVerifyMacro;
+ (OCMMacroState *)globalState;
- (OCMRecorder *)recorder;
- (void)switchToClassMethod;
@end
/*
* Copyright (c) 2014-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@class OCMockObject;
@class OCMInvocationMatcher;
@interface OCMRecorder : NSProxy
{
OCMockObject *mockObject;
OCMInvocationMatcher *invocationMatcher;
}
- (instancetype)init;
- (instancetype)initWithMockObject:(OCMockObject *)aMockObject;
- (void)setMockObject:(OCMockObject *)aMockObject;
- (OCMInvocationMatcher *)invocationMatcher;
- (id)classMethod;
- (id)ignoringNonObjectArgs;
@end
/*
* Copyright (c) 2004-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <OCMock/OCMRecorder.h>
#import <OCMock/OCMFunctions.h>
#import <objc/runtime.h>
@interface OCMStubRecorder : OCMRecorder
- (id)andReturn:(id)anObject;
- (id)andReturnValue:(NSValue *)aValue;
- (id)andThrow:(NSException *)anException;
- (id)andPost:(NSNotification *)aNotification;
- (id)andCall:(SEL)selector onObject:(id)anObject;
- (id)andDo:(void (^)(NSInvocation *invocation))block;
- (id)andForwardToRealObject;
@end
@interface OCMStubRecorder (Properties)
#define andReturn(aValue) _andReturn(({ \
__typeof__(aValue) _val = (aValue); \
NSValue *_nsval = [NSValue value:&_val withObjCType:@encode(__typeof__(_val))]; \
if (OCMIsObjectType(@encode(__typeof(_val)))) { \
objc_setAssociatedObject(_nsval, "OCMAssociatedBoxedValue", *(__unsafe_unretained id *) (void *) &_val, OBJC_ASSOCIATION_RETAIN); \
} \
_nsval; \
}))
@property (nonatomic, readonly) OCMStubRecorder *(^ _andReturn)(NSValue *);
#define andThrow(anException) _andThrow(anException)
@property (nonatomic, readonly) OCMStubRecorder *(^ _andThrow)(NSException *);
#define andPost(aNotification) _andPost(aNotification)
@property (nonatomic, readonly) OCMStubRecorder *(^ _andPost)(NSNotification *);
#define andCall(anObject, aSelector) _andCall(anObject, aSelector)
@property (nonatomic, readonly) OCMStubRecorder *(^ _andCall)(id, SEL);
#define andDo(aBlock) _andDo(aBlock)
@property (nonatomic, readonly) OCMStubRecorder *(^ _andDo)(void (^)(NSInvocation *));
#define andForwardToRealObject() _andForwardToRealObject()
@property (nonatomic, readonly) OCMStubRecorder *(^ _andForwardToRealObject)(void);
@end
/*
* Copyright (c) 2004-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <OCMock/OCMockObject.h>
#import <OCMock/OCMRecorder.h>
#import <OCMock/OCMStubRecorder.h>
#import <OCMock/OCMConstraint.h>
#import <OCMock/OCMArg.h>
#import <OCMock/OCMLocation.h>
#import <OCMock/OCMMacroState.h>
#import <OCMock/NSNotificationCenter+OCMAdditions.h>
#import <OCMock/OCMFunctions.h>
#define OCMClassMock(cls) [OCMockObject niceMockForClass:cls]
#define OCMStrictClassMock(cls) [OCMockObject mockForClass:cls]
#define OCMProtocolMock(protocol) [OCMockObject niceMockForProtocol:protocol]
#define OCMStrictProtocolMock(protocol) [OCMockObject mockForProtocol:protocol]
#define OCMPartialMock(obj) [OCMockObject partialMockForObject:obj]
#define OCMObserverMock() [OCMockObject observerMock]
#define OCMStub(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@finally{ \
recorder = [OCMMacroState endStubMacro]; \
} \
recorder; \
); \
})
#define OCMExpect(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginExpectMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@finally{ \
recorder = [OCMMacroState endExpectMacro]; \
} \
recorder; \
); \
})
#define OCMReject(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginRejectMacro]; \
OCMStubRecorder *recorder = nil; \
@try{ \
invocation; \
}@finally{ \
recorder = [OCMMacroState endRejectMacro]; \
} \
recorder; \
); \
})
#define ClassMethod(invocation) \
_OCMSilenceWarnings( \
[[OCMMacroState globalState] switchToClassMethod]; \
invocation; \
);
#define OCMVerifyAll(mock) [mock verifyAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]
#define OCMVerifyAllWithDelay(mock, delay) [mock verifyWithDelay:delay atLocation:OCMMakeLocation(self, __FILE__, __LINE__)]
#define OCMVerify(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \
@try{ \
invocation; \
}@finally{ \
[OCMMacroState endVerifyMacro]; \
} \
); \
})
#define _OCMSilenceWarnings(macro) \
({ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunused-value\"") \
_Pragma("clang diagnostic ignored \"-Wunused-getter-return-value\"") \
macro \
_Pragma("clang diagnostic pop") \
})
/*
* Copyright (c) 2004-2016 Erik Doernenburg and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use these files except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#import <Foundation/Foundation.h>
@class OCMLocation;
@class OCMInvocationStub;
@class OCMStubRecorder;
@class OCMInvocationMatcher;
@class OCMInvocationExpectation;
@interface OCMockObject : NSProxy
{
BOOL isNice;
BOOL expectationOrderMatters;
NSMutableArray *stubs;
NSMutableArray *expectations;
NSMutableArray *exceptions;
NSMutableArray *invocations;
}
+ (id)mockForClass:(Class)aClass;
+ (id)mockForProtocol:(Protocol *)aProtocol;
+ (id)partialMockForObject:(NSObject *)anObject;
+ (id)niceMockForClass:(Class)aClass;
+ (id)niceMockForProtocol:(Protocol *)aProtocol;
+ (id)observerMock;
- (instancetype)init;
- (void)setExpectationOrderMatters:(BOOL)flag;
- (id)stub;
- (id)expect;
- (id)reject;
- (id)verify;
- (id)verifyAtLocation:(OCMLocation *)location;
- (void)verifyWithDelay:(NSTimeInterval)delay;
- (void)verifyWithDelay:(NSTimeInterval)delay atLocation:(OCMLocation *)location;
- (void)stopMocking;
// internal use only
- (void)addStub:(OCMInvocationStub *)aStub;
- (void)addExpectation:(OCMInvocationExpectation *)anExpectation;
- (BOOL)handleInvocation:(NSInvocation *)anInvocation;
- (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation;
- (BOOL)handleSelector:(SEL)sel;
- (void)verifyInvocation:(OCMInvocationMatcher *)matcher;
- (void)verifyInvocation:(OCMInvocationMatcher *)matcher atLocation:(OCMLocation *)location;
@end
#import <React/RCTConvert.h>
@import UserNotifications;
@interface RCTConvert (UIMutableUserNotificationAction)
+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json;
@end
@interface RCTConvert (UNMutableUserNotificationCategory)
+ (UNNotificationCategory *)UNMutableUserNotificationCategory:(id)json;
@end
@interface RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSNumber*)notificationId;
@end
@interface RCTConvert (UNNotification)
+ (NSDictionary *)UNNotificationPayload:(UNNotification *)notification;
@end
@interface RCTConvert (UNNotificationPresentationOptions)
+ (UNNotificationPresentationOptions)UNNotificationPresentationOptions:(id)json;
@end
#import "RCTConvert+RNNotifications.h"
@implementation RCTConvert (UNNotificationActionOptions)
+ (UNNotificationActionOptions)UNUserNotificationActionOptions:(id)json {
UNNotificationActionOptions options = UNNotificationActionOptionNone;
if ([json[@"activationMode"] isEqualToString:@"foreground"]) {
options = options | UNNotificationActionOptionForeground;
}
if ([RCTConvert BOOL:json[@"authenticationRequired"]]) {
options = options | UNNotificationActionOptionAuthenticationRequired;
}
if ([RCTConvert BOOL:json[@"destructive"]]) {
options = options | UNNotificationActionOptionDestructive;
}
return options;
}
@end
@implementation RCTConvert (UNMutableUserNotificationAction)
+ (UNNotificationAction *)UNMutableUserNotificationAction:(id)json {
UNNotificationAction* action;
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
if (details[@"textInput"]) {
action = [UNTextInputNotificationAction actionWithIdentifier:details[@"identifier"] title:details[@"title"] options:[RCTConvert UNUserNotificationActionOptions:details] textInputButtonTitle:details[@"textInput"][@"buttonTitle"] textInputPlaceholder:details[@"textInput"][@"placeholder"]];
} else {
action = [UNNotificationAction actionWithIdentifier:details[@"identifier"] title:details[@"title"] options:[RCTConvert UNUserNotificationActionOptions:details]];
}
return action;
}
@end
@implementation RCTConvert (UNMutableUserNotificationCategory)
+ (UNNotificationCategory *)UNMutableUserNotificationCategory:(id)json {
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
NSMutableArray* actions = [NSMutableArray new];
for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) {
[actions addObject:[RCTConvert UNMutableUserNotificationAction:actionJson]];
}
UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier:details[@"identifier"] actions:actions intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
return category;
}
@end
@implementation RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSNumber*)notificationId
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
content.body = [RCTConvert NSString:details[@"body"]];
content.title = [RCTConvert NSString:details[@"title"]];
content.sound = [RCTConvert NSString:details[@"sound"]]
? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"sound"]]]
: [UNNotificationSound defaultSound];
if ([RCTConvert BOOL:details[@"silent"]]) {
content.sound = nil;
}
content.userInfo = [RCTConvert NSDictionary:details] ?: @{};
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
UNCalendarNotificationTrigger *trigger = nil;
if (triggerDate != nil) {
NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
components:NSCalendarUnitYear +
NSCalendarUnitMonth + NSCalendarUnitDay +
NSCalendarUnitHour + NSCalendarUnitMinute +
NSCalendarUnitSecond + NSCalendarUnitTimeZone
fromDate:triggerDate];
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
repeats:NO];
}
return [UNNotificationRequest requestWithIdentifier:[NSString stringWithFormat:@"%@", notificationId]
content:content trigger:trigger];
}
@end
@implementation RCTConvert (UNNotification)
+ (NSDictionary *)UNNotificationPayload:(UNNotification *)notification {
NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
UNNotificationContent *content = notification.request.content;
formattedNotification[@"identifier"] = notification.request.identifier;
if (notification.date) {
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
NSString *dateString = [formatter stringFromDate:notification.date];
formattedNotification[@"date"] = dateString;
}
formattedNotification[@"title"] = RCTNullIfNil(content.title);
formattedNotification[@"body"] = RCTNullIfNil(content.body);
formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
formattedNotification[@"thread"] = RCTNullIfNil(content.threadIdentifier);
[formattedNotification addEntriesFromDictionary:[NSDictionary dictionaryWithDictionary:RCTNullIfNil(RCTJSONClean(content.userInfo))]];
return formattedNotification;
}
@end
@implementation RCTConvert (UNNotificationPresentationOptions)
+ (UNNotificationPresentationOptions)UNNotificationPresentationOptions:(id)json {
UNNotificationPresentationOptions options = UNNotificationPresentationOptionNone;
if ([RCTConvert BOOL:json[@"alert"]]) {
options = options | UNNotificationPresentationOptionAlert;
}
if ([RCTConvert BOOL:json[@"badge"]]) {
options = options | UNNotificationPresentationOptionBadge;
}
if ([RCTConvert BOOL:json[@"sound"]]) {
options = options | UNNotificationPresentationOptionSound;
}
return options;
}
@end
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RNBridgeModule : NSObject <RCTBridgeModule>
@end
#import "RNBridgeModule.h"
#import "RNCommandsHandler.h"
#import "RCTConvert+RNNotifications.h"
#import "RNNotificationsStore.h"
#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridge.h>
@implementation RNBridgeModule {
RNCommandsHandler* _commandsHandler;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE();
- (instancetype)init {
self = [super init];
_commandsHandler = [[RNCommandsHandler alloc] init];
return self;
}
+ (BOOL)requiresMainQueueSetup {
return YES;
}
- (void)setBridge:(RCTBridge *)bridge {
_bridge = bridge;
if ([_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
[[RNNotificationsStore sharedInstance] setInitialNotification:[_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]];
}
}
#pragma mark - JS interface
RCT_EXPORT_METHOD(requestPermissions) {
[_commandsHandler requestPermissions];
}
RCT_EXPORT_METHOD(setCategories:(NSArray *)categories) {
[_commandsHandler setCategories:categories];
}
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
[_commandsHandler getInitialNotification:resolve reject:reject];
}
RCT_EXPORT_METHOD(finishHandlingAction:(NSString *)completionKey) {
[_commandsHandler finishHandlingAction:completionKey];
}
RCT_EXPORT_METHOD(finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions) {
[_commandsHandler finishPresentingNotification:completionKey presentingOptions:presentingOptions];
}
RCT_EXPORT_METHOD(abandonPermissions) {
[_commandsHandler abandonPermissions];
}
RCT_EXPORT_METHOD(registerPushKit) {
[_commandsHandler registerPushKit];
}
RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback) {
[_commandsHandler getBadgesCount:callback];
}
RCT_EXPORT_METHOD(setBadgesCount:(int)count) {
[_commandsHandler setBadgesCount:count];
}
RCT_EXPORT_METHOD(postLocalNotification:(NSDictionary *)notification withId:(nonnull NSNumber *)notificationId) {
[_commandsHandler postLocalNotification:notification withId:notificationId];
}
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId) {
[_commandsHandler cancelLocalNotification:notificationId];
}
RCT_EXPORT_METHOD(cancelAllLocalNotifications) {
[_commandsHandler cancelAllLocalNotifications];
}
RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
[_commandsHandler isRegisteredForRemoteNotifications:resolve reject:reject];
}
RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
[_commandsHandler checkPermissions:resolve reject:reject];
}
#if !TARGET_OS_TV
RCT_EXPORT_METHOD(removeAllDeliveredNotifications) {
[_commandsHandler removeAllDeliveredNotifications];
}
RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers) {
[_commandsHandler removeDeliveredNotifications:identifiers];
}
RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) {
[_commandsHandler getDeliveredNotifications:callback];
}
#endif
@end
#import <Foundation/Foundation.h>
#import "RNNotificationCenter.h"
@interface RNCommandsHandler : NSObject
- (instancetype)init;
- (void)requestPermissions;
- (void)setCategories:(NSArray *)categories;
- (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
- (void)finishHandlingAction:(NSString *)completionKey;
- (void)finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions;
- (void)abandonPermissions;
- (void)registerPushKit;
- (void)getBadgesCount:(RCTResponseSenderBlock)callback;
- (void)setBadgesCount:(int)count;
- (void)postLocalNotification:(NSDictionary *)notification withId:(NSNumber *)notificationId;
- (void)cancelLocalNotification:(NSString *)notificationId;
- (void)cancelAllLocalNotifications;
- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject;
- (void)checkPermissions:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject;
- (void)removeAllDeliveredNotifications;
- (void)removeDeliveredNotifications:(NSArray<NSString *> *)identifiers;
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback;
@end
#import "RNCommandsHandler.h"
#import "RNNotifications.h"
#import "RNNotificationsStore.h"
#import "RCTConvert+RNNotifications.h"
@implementation RNCommandsHandler {
RNNotificationCenter* _notificationCenter;
}
- (instancetype)init {
self = [super init];
_notificationCenter = [RNNotificationCenter new];
return self;
}
- (void)requestPermissions {
[_notificationCenter requestPermissions];
}
- (void)setCategories:(NSArray *)categories {
[_notificationCenter setCategories:categories];
}
- (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
resolve([[RNNotificationsStore sharedInstance] initialNotification]);
}
- (void)finishHandlingAction:(NSString *)completionKey {
[[RNNotificationsStore sharedInstance] completeAction:completionKey];
}
- (void)finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions {
[[RNNotificationsStore sharedInstance] completePresentation:completionKey withPresentationOptions:[RCTConvert UNNotificationPresentationOptions:presentingOptions]];
}
- (void)abandonPermissions {
[[UIApplication sharedApplication] unregisterForRemoteNotifications];
}
- (void)registerPushKit {
[RNNotifications startMonitorPushKitNotifications];
}
- (void)getBadgesCount:(RCTResponseSenderBlock)callback {
NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber;
callback(@[ [NSNumber numberWithInteger:count] ]);
}
- (void)setBadgesCount:(int)count {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
- (void)postLocalNotification:(NSDictionary *)notification withId:(NSNumber *)notificationId {
[_notificationCenter postLocalNotification:notification withId:notificationId];
}
- (void)cancelLocalNotification:(NSString *)notificationId {
[_notificationCenter cancelLocalNotification:notificationId];
}
- (void)cancelAllLocalNotifications {
[_notificationCenter cancelAllLocalNotifications];
}
- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[_notificationCenter isRegisteredForRemoteNotifications:resolve];
}
- (void)checkPermissions:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[_notificationCenter checkPermissions:resolve];
}
- (void)removeAllDeliveredNotifications {
[_notificationCenter removeAllDeliveredNotifications];
}
- (void)removeDeliveredNotifications:(NSArray<NSString *> *)identifiers {
[_notificationCenter removeDeliveredNotifications:identifiers];
}
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback {
[_notificationCenter getDeliveredNotifications:callback];
}
@end
#import <React/RCTEventEmitter.h>
static NSString* const RNRegistered = @"remoteNotificationsRegistered";
static NSString* const RNRegistrationFailed = @"remoteNotificationsRegistrationFailed";
static NSString* const RNPushKitRegistered = @"pushKitRegistered";
static NSString* const RNNotificationReceived = @"notificationReceived";
static NSString* const RNNotificationOpened = @"notificationOpened";
static NSString* const RNPushKitNotificationReceived = @"pushKitNotificationReceived";
@interface RNEventEmitter : RCTEventEmitter <RCTBridgeModule>
+ (void)sendEvent:(NSString *)event body:(NSDictionary *)body;
@end
#import "RNEventEmitter.h"
@implementation RNEventEmitter
RCT_EXPORT_MODULE();
-(NSArray<NSString *> *)supportedEvents {
return @[RNRegistered,
RNRegistrationFailed,
RNPushKitRegistered,
RNNotificationReceived,
RNNotificationOpened,
RNPushKitNotificationReceived];
}
- (instancetype)init {
self = [super init];
for (NSString *event in [self supportedEvents]) {
[self addListener:event];
}
return self;
}
+ (BOOL)requiresMainQueueSetup {
return YES;
}
# pragma mark public
+ (void)sendEvent:(NSString *)event body:(NSDictionary *)body {
[[NSNotificationCenter defaultCenter] postNotificationName:event
object:self
userInfo:body];
}
# pragma mark private
- (void)startObserving {
for (NSString *event in [self supportedEvents]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotification:)
name:event
object:nil];
}
}
- (void)stopObserving {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)handleNotification:(NSNotification *)notification {
[self sendEventWithName:notification.name body:notification.userInfo];
}
@end
#import <Foundation/Foundation.h>
typedef void (^RCTPromiseResolveBlock)(id result);
typedef void (^RCTResponseSenderBlock)(NSArray *response);
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
@import UserNotifications;
@interface RNNotificationCenter : NSObject
- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve;
- (void)requestPermissions;
- (void)setCategories:(NSArray *)json;
- (void)checkPermissions:(RCTPromiseResolveBlock)resolve;
- (void)postLocalNotification:(NSDictionary *)notification withId:(NSNumber *)notificationId;
- (void)cancelLocalNotification:(NSString *)notificationId;
- (void)removeAllDeliveredNotifications;
- (void)removeDeliveredNotifications:(NSArray<NSString *> *)identifiers;
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback;
- (void)cancelAllLocalNotifications;
@end
#import "RNNotificationCenter.h"
#import "RCTConvert+RNNotifications.h"
@implementation RNNotificationCenter
- (void)requestPermissions {
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
[UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
[UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) {
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
}
}];
}
}];
}
- (void)setCategories:(NSArray *)json {
NSMutableSet<UNNotificationCategory *>* categories = nil;
if ([json count] > 0) {
categories = [NSMutableSet new];
for (NSDictionary* categoryJson in json) {
[categories addObject:[RCTConvert UNMutableUserNotificationCategory:categoryJson]];
}
}
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories];
}
- (void)postLocalNotification:(NSDictionary *)notification withId:(NSNumber *)notificationId {
UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
}
- (void)cancelLocalNotification:(NSString *)notificationId {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
}
- (void)removeAllDeliveredNotifications {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeAllDeliveredNotifications];
}
- (void)removeDeliveredNotifications:(NSArray<NSString *> *)identifiers {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeDeliveredNotificationsWithIdentifiers:identifiers];
}
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];
for (UNNotification *notification in notifications) {
[formattedNotifications addObject:[RCTConvert UNNotificationPayload:notification]];
}
callback(@[formattedNotifications]);
}];
}
- (void)cancelAllLocalNotifications {
[[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
}
- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.alertSetting == UNNotificationSettingEnabled || settings.soundSetting == UNNotificationSettingEnabled || settings.badgeSetting == UNNotificationSettingEnabled) {
resolve(@(YES));
} else {
resolve(@(NO));
}
}];
}
- (void)checkPermissions:(RCTPromiseResolveBlock)resolve {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
resolve(@{
@"badge": @(settings.badgeSetting == UNNotificationSettingEnabled),
@"sound": @(settings.soundSetting == UNNotificationSettingEnabled),
@"alert": @(settings.alertSetting == UNNotificationSettingEnabled),
});
}];
}
@end
#import <Foundation/Foundation.h>
@import UserNotifications;
#import "RNNotificationEventHandler.h"
@interface RNNotificationCenterListener : NSObject <UNUserNotificationCenterDelegate>
- (instancetype)initWithNotificationEventHandler:(RNNotificationEventHandler *)notificationEventHandler;
@end
#import "RNNotificationCenterListener.h"
#import "RCTConvert+RNNotifications.h"
@implementation RNNotificationCenterListener {
RNNotificationEventHandler* _notificationEventHandler;
}
- (instancetype)initWithNotificationEventHandler:(RNNotificationEventHandler *)notificationEventHandler {
self = [super init];
_notificationEventHandler = notificationEventHandler;
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
return self;
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
[_notificationEventHandler didReceiveForegroundNotification:notification withCompletionHandler:completionHandler];
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[_notificationEventHandler didReceiveNotificationResponse:response completionHandler:completionHandler];
}
@end
#import <Foundation/Foundation.h>
@import UserNotifications;
#import "RNNotificationsStore.h"
#import "RNEventEmitter.h"
@interface RNNotificationEventHandler : NSObject
- (instancetype)initWithStore:(RNNotificationsStore *)store;
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken;
- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
- (void)didReceiveForegroundNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler;
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)notificationResponse completionHandler:(void (^)(void))completionHandler;
@end
#import "RNNotificationEventHandler.h"
#import "RNEventEmitter.h"
#import "RNNotificationUtils.h"
#import "RCTConvert+RNNotifications.h"
#import "RNNotificationParser.h"
@implementation RNNotificationEventHandler {
RNNotificationsStore* _store;
}
- (instancetype)initWithStore:(RNNotificationsStore *)store {
self = [super init];
_store = store;
return self;
}
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken {
NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [RNNotificationUtils deviceTokenToString:deviceToken];
[RNEventEmitter sendEvent:RNRegistered body:@{@"deviceToken": tokenRepresentation}];
}
- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[RNEventEmitter sendEvent:RNRegistrationFailed body:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
}
- (void)didReceiveForegroundNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
[_store setPresentationCompletionHandler:completionHandler withCompletionKey:notification.request.identifier];
[RNEventEmitter sendEvent:RNNotificationReceived body:[RNNotificationParser parseNotification:notification]];
}
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(void))completionHandler {
[_store setActionCompletionHandler:completionHandler withCompletionKey:response.notification.request.identifier];
[RNEventEmitter sendEvent:RNNotificationOpened body:[RNNotificationParser parseNotificationResponse:response]];
}
@end
#import <Foundation/Foundation.h>
@import UserNotifications;
@interface RNNotificationParser : NSObject
+ (NSDictionary *)parseNotificationResponse:(UNNotificationResponse *)response;
+ (NSDictionary *)parseNotification:(UNNotification *)notification;
@end
#import "RNNotificationParser.h"
#import "RCTConvert+RNNotifications.h"
@implementation RNNotificationParser
+ (NSDictionary *)parseNotification:(UNNotification *)notification {
return [RCTConvert UNNotificationPayload:notification];
}
+ (NSDictionary *)parseNotificationResponse:(UNNotificationResponse *)response {
NSDictionary* responseDict = @{@"notification": [RCTConvert UNNotificationPayload:response.notification], @"identifier": response.notification.request.identifier, @"action": [self parseNotificationResponseAction:response]};
return responseDict;
}
+ (NSDictionary *)parseNotificationResponseAction:(UNNotificationResponse *)response {
NSMutableDictionary* responseAction = [NSMutableDictionary dictionaryWithDictionary:@{@"identifier": response.actionIdentifier}];
NSString* responseText = [response isKindOfClass:[UNTextInputNotificationResponse class]] ? ((UNTextInputNotificationResponse *)response).userText : nil;
if (responseText) {
[responseAction setObject:responseText forKey:@"text"];
}
return responseAction;
}
@end
#import <Foundation/Foundation.h>
@interface RNNotificationUtils : NSObject
+ (NSString *)deviceTokenToString:(NSData *)deviceToken;
@end
#import "RNNotificationUtils.h"
@implementation RNNotificationUtils
+ (NSString *)deviceTokenToString:(NSData *)deviceToken {
NSMutableString *result = [NSMutableString string];
NSUInteger deviceTokenLength = deviceToken.length;
const unsigned char *bytes = deviceToken.bytes;
for (NSUInteger i = 0; i < deviceTokenLength; i++) {
[result appendFormat:@"%02x", bytes[i]];
}
return [result copy];
}
@end
@import UIKit;
#import <PushKit/PushKit.h>
@import UserNotifications;
@interface RNNotifications : NSObject
+ (instancetype)sharedInstance;
+ (void)startMonitorNotifications;
+ (void)startMonitorPushKitNotifications;
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken;
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
@end
#import <UIKit/UIKit.h>
#import <PushKit/PushKit.h>
#import "RNNotifications.h"
#import "RNNotificationCenterListener.h"
#import "RNPushKit.h"
@implementation RNNotifications {
RNPushKit* _pushKit;
RNNotificationCenterListener* _notificationCenterListener;
RNNotificationEventHandler* _notificationEventHandler;
RNPushKitEventHandler* _pushKitEventHandler;
RNEventEmitter* _eventEmitter;
}
- (instancetype)init {
self = [super init];
_notificationEventHandler = [[RNNotificationEventHandler alloc] initWithStore:[RNNotificationsStore new]];
return self;
}
+ (instancetype)sharedInstance {
static RNNotifications *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[RNNotifications alloc] init];
});
return sharedInstance;
}
+ (void)startMonitorNotifications {
[[self sharedInstance] startMonitorNotifications];
}
+ (void)startMonitorPushKitNotifications {
[[self sharedInstance] startMonitorPushKitNotifications];
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken {
[[self sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[[self sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error];
}
- (void)startMonitorNotifications {
_notificationCenterListener = [[RNNotificationCenterListener alloc] initWithNotificationEventHandler:_notificationEventHandler];
}
- (void)startMonitorPushKitNotifications {
_pushKitEventHandler = [RNPushKitEventHandler new];
_pushKit = [[RNPushKit alloc] initWithEventHandler:_pushKitEventHandler];
}
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken {
[_notificationEventHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[_notificationEventHandler didFailToRegisterForRemoteNotificationsWithError:error];
}
@end
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
50002A4D22E88266008F6742 /* RNCommandsHandlerIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50002A4C22E88266008F6742 /* RNCommandsHandlerIntegrationTest.m */; };
50002A8022E8885A008F6742 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50002A7F22E8885A008F6742 /* libOCMock.a */; };
50002AC822E9D5EA008F6742 /* RNNotificationsStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 50002AC722E9D5EA008F6742 /* RNNotificationsStoreTests.m */; };
50002ACA22E9DB8A008F6742 /* RNNotificationEventHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 50002AC922E9DB8A008F6742 /* RNNotificationEventHandlerTests.m */; };
50351F8F22CD782F000713B3 /* RNEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F8E22CD782F000713B3 /* RNEventEmitter.m */; };
50351F9222CD7DF4000713B3 /* RNBridgeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9122CD7DF4000713B3 /* RNBridgeModule.m */; };
50351F9522CD7FF1000713B3 /* RCTConvert+RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */; };
50351F9822CD8604000713B3 /* RNCommandsHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9722CD8604000713B3 /* RNCommandsHandler.m */; };
504D54BE22E4B8660088F2E4 /* RNBridgeModuleIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 504D54BD22E4B8660088F2E4 /* RNBridgeModuleIntegrationTest.m */; };
507DCCF522CE3EBD005D4E0B /* RNNotifications.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */; };
507DCCF722CE3EF7005D4E0B /* RNBridgeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9022CD7DF4000713B3 /* RNBridgeModule.h */; };
507DCCF922CE3F04005D4E0B /* RNNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */; };
507DCCFA22CE3F04005D4E0B /* RNEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F8D22CD782F000713B3 /* RNEventEmitter.h */; };
507DCCFB22CE3F04005D4E0B /* RNCommandsHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9622CD8604000713B3 /* RNCommandsHandler.h */; };
507DCCFC22CE3F04005D4E0B /* RCTConvert+RNNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */; };
50858B7822E87767008272FE /* libyoga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B7722E87767008272FE /* libyoga.a */; };
50858B7922E8777C008272FE /* libRNNotifications.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134814201AA4EA6300B7C361 /* libRNNotifications.a */; };
50858B7B22E87FBA008272FE /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B7A22E87FBA008272FE /* libRCTActionSheet.a */; };
50858B7D22E87FBA008272FE /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B7C22E87FBA008272FE /* libRCTImage.a */; };
50858B7F22E87FBA008272FE /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B7E22E87FBA008272FE /* libRCTLinking.a */; };
50858B8122E87FBA008272FE /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B8022E87FBA008272FE /* libRCTNetwork.a */; };
50858B8322E87FBA008272FE /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B8222E87FBA008272FE /* libRCTSettings.a */; };
50858B8522E87FBA008272FE /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B8422E87FBA008272FE /* libRCTText.a */; };
50858B8722E87FBA008272FE /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B8622E87FBA008272FE /* libRCTVibration.a */; };
50858B8922E87FBA008272FE /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50858B8822E87FBA008272FE /* libRCTWebSocket.a */; };
508CE7CB22D12B2600357815 /* RNNotificationsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE7CA22D12B2600357815 /* RNNotificationsTests.m */; };
508CE7D522D12CCA00357815 /* RNNotificationEventHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */; };
508CE7D622D12CCA00357815 /* RNNotificationEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */; };
508CE81422D12FC700357815 /* RNNotificationCenterListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81322D12FC600357815 /* RNNotificationCenterListener.m */; };
508CE81622D12FF600357815 /* RNNotificationCenterListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81522D12FF500357815 /* RNNotificationCenterListener.h */; };
508CE81922D130B900357815 /* RNPushKitEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81722D130B900357815 /* RNPushKitEventListener.h */; };
508CE81A22D130B900357815 /* RNPushKitEventListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81822D130B900357815 /* RNPushKitEventListener.m */; };
508CE81D22D1337200357815 /* RNPushKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81B22D1337200357815 /* RNPushKit.h */; };
508CE81E22D1337200357815 /* RNPushKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81C22D1337200357815 /* RNPushKit.m */; };
508CE82222D1372E00357815 /* RNNotificationUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE82022D1372E00357815 /* RNNotificationUtils.h */; };
508CE82322D1372E00357815 /* RNNotificationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE82122D1372E00357815 /* RNNotificationUtils.m */; };
50AD1FCA22D13ADB00E12362 /* RNPushKitEventHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */; };
50AD1FCB22D13ADB00E12362 /* RNPushKitEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */; };
50E4164822E4CA500048367F /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E4164722E4CA500048367F /* libReact.a */; };
50E4164A22E4CA5A0048367F /* libcxxreact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E4164922E4CA5A0048367F /* libcxxreact.a */; };
50E4164D22E4CA750048367F /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 504D54E422E4BCE00088F2E4 /* JavaScriptCore.framework */; };
50E49F0722D1E4E0007160C1 /* RNNotificationsStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */; };
50E49F0822D1E4E0007160C1 /* RNNotificationsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */; };
50FED76622D3E06500DDD516 /* RNNotificationCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 50FED76422D3E06500DDD516 /* RNNotificationCenter.h */; };
50FED76722D3E06500DDD516 /* RNNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 50FED76522D3E06500DDD516 /* RNNotificationCenter.m */; };
50FED76E22D3EBA800DDD516 /* RNNotificationParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */; };
50FED76F22D3EBA800DDD516 /* RNNotificationParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */; };
D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
508CE7CE22D12B2600357815 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 58B511D31A9E6C8500147676 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 58B511DA1A9E6C8500147676;
remoteInfo = RNNotifications;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
507DCCF522CE3EBD005D4E0B /* RNNotifications.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; };
50002A4C22E88266008F6742 /* RNCommandsHandlerIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCommandsHandlerIntegrationTest.m; sourceTree = "<group>"; };
50002A7F22E8885A008F6742 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
50002AC722E9D5EA008F6742 /* RNNotificationsStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsStoreTests.m; sourceTree = "<group>"; };
50002AC922E9DB8A008F6742 /* RNNotificationEventHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationEventHandlerTests.m; sourceTree = "<group>"; };
50351F8D22CD782F000713B3 /* RNEventEmitter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNEventEmitter.h; sourceTree = "<group>"; };
50351F8E22CD782F000713B3 /* RNEventEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNEventEmitter.m; sourceTree = "<group>"; };
50351F9022CD7DF4000713B3 /* RNBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNBridgeModule.h; sourceTree = "<group>"; };
50351F9122CD7DF4000713B3 /* RNBridgeModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNBridgeModule.m; sourceTree = "<group>"; };
50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+RNNotifications.h"; sourceTree = "<group>"; };
50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+RNNotifications.m"; sourceTree = "<group>"; };
50351F9622CD8604000713B3 /* RNCommandsHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCommandsHandler.h; sourceTree = "<group>"; };
50351F9722CD8604000713B3 /* RNCommandsHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCommandsHandler.m; sourceTree = "<group>"; };
504D54BD22E4B8660088F2E4 /* RNBridgeModuleIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNBridgeModuleIntegrationTest.m; sourceTree = "<group>"; };
504D54CC22E4BCB80088F2E4 /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54CE22E4BCB80088F2E4 /* libdouble-conversion.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libdouble-conversion.a"; sourceTree = BUILT_PRODUCTS_DIR; };
504D54D022E4BCB80088F2E4 /* libfishhook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libfishhook.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54D222E4BCB80088F2E4 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54D422E4BCB80088F2E4 /* libRCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTLinking.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54D622E4BCB80088F2E4 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54D822E4BCB80088F2E4 /* libRCTSettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTSettings.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54DA22E4BCB80088F2E4 /* libRCTText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTText.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54DC22E4BCB80088F2E4 /* libRCTVibration.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTVibration.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54DE22E4BCB80088F2E4 /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54E022E4BCB80088F2E4 /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54E222E4BCB80088F2E4 /* libyoga.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libyoga.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54E422E4BCE00088F2E4 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
504D54E622E4BDD10088F2E4 /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54E822E4BE2C0088F2E4 /* libjsi.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libjsi.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54EA22E4BE3A0088F2E4 /* libdouble-conversion.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libdouble-conversion.a"; sourceTree = BUILT_PRODUCTS_DIR; };
504D54EC22E4BE470088F2E4 /* libjsiexecutor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libjsiexecutor.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54EE22E4BE470088F2E4 /* libjsinspector.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libjsinspector.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54F022E4BE470088F2E4 /* libRCTActionSheet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTActionSheet.a; sourceTree = BUILT_PRODUCTS_DIR; };
504D54F222E4BE470088F2E4 /* libthird-party.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libthird-party.a"; sourceTree = BUILT_PRODUCTS_DIR; };
50858B7722E87767008272FE /* libyoga.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libyoga.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B7A22E87FBA008272FE /* libRCTActionSheet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTActionSheet.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B7C22E87FBA008272FE /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B7E22E87FBA008272FE /* libRCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTLinking.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B8022E87FBA008272FE /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B8222E87FBA008272FE /* libRCTSettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTSettings.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B8422E87FBA008272FE /* libRCTText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTText.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B8622E87FBA008272FE /* libRCTVibration.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTVibration.a; sourceTree = BUILT_PRODUCTS_DIR; };
50858B8822E87FBA008272FE /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
508CE7CA22D12B2600357815 /* RNNotificationsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsTests.m; sourceTree = "<group>"; };
508CE7CC22D12B2600357815 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationEventHandler.h; sourceTree = "<group>"; };
508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationEventHandler.m; sourceTree = "<group>"; };
508CE81322D12FC600357815 /* RNNotificationCenterListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationCenterListener.m; sourceTree = "<group>"; };
508CE81522D12FF500357815 /* RNNotificationCenterListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotificationCenterListener.h; sourceTree = "<group>"; };
508CE81722D130B900357815 /* RNPushKitEventListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKitEventListener.h; sourceTree = "<group>"; };
508CE81822D130B900357815 /* RNPushKitEventListener.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKitEventListener.m; sourceTree = "<group>"; };
508CE81B22D1337200357815 /* RNPushKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKit.h; sourceTree = "<group>"; };
508CE81C22D1337200357815 /* RNPushKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKit.m; sourceTree = "<group>"; };
508CE82022D1372E00357815 /* RNNotificationUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationUtils.h; sourceTree = "<group>"; };
508CE82122D1372E00357815 /* RNNotificationUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationUtils.m; sourceTree = "<group>"; };
50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKitEventHandler.h; sourceTree = "<group>"; };
50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKitEventHandler.m; sourceTree = "<group>"; };
50E4164622E4C98B0048367F /* RNNotificationsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNNotificationsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
50E4164722E4CA500048367F /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
50E4164922E4CA5A0048367F /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
50E4164B22E4CA6A0048367F /* libjsi.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libjsi.a; sourceTree = BUILT_PRODUCTS_DIR; };
50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsStore.h; sourceTree = "<group>"; };
50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsStore.m; sourceTree = "<group>"; };
50FED76422D3E06500DDD516 /* RNNotificationCenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationCenter.h; sourceTree = "<group>"; };
50FED76522D3E06500DDD516 /* RNNotificationCenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationCenter.m; sourceTree = "<group>"; };
50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationParser.h; sourceTree = "<group>"; };
50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationParser.m; sourceTree = "<group>"; };
D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotifications.m; sourceTree = "<group>"; };
D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotifications.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
508CE7C522D12B2600357815 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
50E4164D22E4CA750048367F /* JavaScriptCore.framework in Frameworks */,
50E4164A22E4CA5A0048367F /* libcxxreact.a in Frameworks */,
50858B7B22E87FBA008272FE /* libRCTActionSheet.a in Frameworks */,
50858B7D22E87FBA008272FE /* libRCTImage.a in Frameworks */,
50858B7F22E87FBA008272FE /* libRCTLinking.a in Frameworks */,
50858B8122E87FBA008272FE /* libRCTNetwork.a in Frameworks */,
50858B8322E87FBA008272FE /* libRCTSettings.a in Frameworks */,
50858B8522E87FBA008272FE /* libRCTText.a in Frameworks */,
50858B8722E87FBA008272FE /* libRCTVibration.a in Frameworks */,
50858B8922E87FBA008272FE /* libRCTWebSocket.a in Frameworks */,
50858B7822E87767008272FE /* libyoga.a in Frameworks */,
50E4164822E4CA500048367F /* libReact.a in Frameworks */,
50858B7922E8777C008272FE /* libRNNotifications.a in Frameworks */,
50002A8022E8885A008F6742 /* libOCMock.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRNNotifications.a */,
);
name = Products;
sourceTree = "<group>";
};
504D54BC22E4B83E0088F2E4 /* Integration */ = {
isa = PBXGroup;
children = (
504D54BD22E4B8660088F2E4 /* RNBridgeModuleIntegrationTest.m */,
50002A4C22E88266008F6742 /* RNCommandsHandlerIntegrationTest.m */,
);
path = Integration;
sourceTree = "<group>";
};
504D54CB22E4BCB80088F2E4 /* Frameworks */ = {
isa = PBXGroup;
children = (
50002A7F22E8885A008F6742 /* libOCMock.a */,
50858B7A22E87FBA008272FE /* libRCTActionSheet.a */,
50858B7C22E87FBA008272FE /* libRCTImage.a */,
50858B7E22E87FBA008272FE /* libRCTLinking.a */,
50858B8022E87FBA008272FE /* libRCTNetwork.a */,
50858B8222E87FBA008272FE /* libRCTSettings.a */,
50858B8422E87FBA008272FE /* libRCTText.a */,
50858B8622E87FBA008272FE /* libRCTVibration.a */,
50858B8822E87FBA008272FE /* libRCTWebSocket.a */,
50858B7722E87767008272FE /* libyoga.a */,
50E4164B22E4CA6A0048367F /* libjsi.a */,
50E4164922E4CA5A0048367F /* libcxxreact.a */,
50E4164722E4CA500048367F /* libReact.a */,
504D54EC22E4BE470088F2E4 /* libjsiexecutor.a */,
504D54EE22E4BE470088F2E4 /* libjsinspector.a */,
504D54F022E4BE470088F2E4 /* libRCTActionSheet.a */,
504D54F222E4BE470088F2E4 /* libthird-party.a */,
504D54EA22E4BE3A0088F2E4 /* libdouble-conversion.a */,
504D54E822E4BE2C0088F2E4 /* libjsi.a */,
504D54E622E4BDD10088F2E4 /* libReact.a */,
504D54E422E4BCE00088F2E4 /* JavaScriptCore.framework */,
504D54CC22E4BCB80088F2E4 /* libcxxreact.a */,
504D54CE22E4BCB80088F2E4 /* libdouble-conversion.a */,
504D54D022E4BCB80088F2E4 /* libfishhook.a */,
504D54D222E4BCB80088F2E4 /* libRCTImage.a */,
504D54D422E4BCB80088F2E4 /* libRCTLinking.a */,
504D54D622E4BCB80088F2E4 /* libRCTNetwork.a */,
504D54D822E4BCB80088F2E4 /* libRCTSettings.a */,
504D54DA22E4BCB80088F2E4 /* libRCTText.a */,
504D54DC22E4BCB80088F2E4 /* libRCTVibration.a */,
504D54DE22E4BCB80088F2E4 /* libRCTWebSocket.a */,
504D54E022E4BCB80088F2E4 /* libReact.a */,
504D54E222E4BCB80088F2E4 /* libyoga.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
508CE7C922D12B2600357815 /* RNNotificationsTests */ = {
isa = PBXGroup;
children = (
504D54BC22E4B83E0088F2E4 /* Integration */,
508CE7CA22D12B2600357815 /* RNNotificationsTests.m */,
50002AC722E9D5EA008F6742 /* RNNotificationsStoreTests.m */,
50002AC922E9DB8A008F6742 /* RNNotificationEventHandlerTests.m */,
508CE7CC22D12B2600357815 /* Info.plist */,
);
path = RNNotificationsTests;
sourceTree = "<group>";
};
508CE81122D12F3C00357815 /* Notifications */ = {
isa = PBXGroup;
children = (
50FED76422D3E06500DDD516 /* RNNotificationCenter.h */,
50FED76522D3E06500DDD516 /* RNNotificationCenter.m */,
50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */,
50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */,
508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */,
508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */,
508CE81522D12FF500357815 /* RNNotificationCenterListener.h */,
508CE81322D12FC600357815 /* RNNotificationCenterListener.m */,
50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */,
50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */,
);
name = Notifications;
sourceTree = "<group>";
};
508CE81222D12F4300357815 /* PushKitNotifications */ = {
isa = PBXGroup;
children = (
508CE81B22D1337200357815 /* RNPushKit.h */,
508CE81C22D1337200357815 /* RNPushKit.m */,
508CE81722D130B900357815 /* RNPushKitEventListener.h */,
508CE81822D130B900357815 /* RNPushKitEventListener.m */,
50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */,
50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */,
);
name = PushKitNotifications;
sourceTree = "<group>";
};
508CE81F22D1371700357815 /* Helpers */ = {
isa = PBXGroup;
children = (
508CE82022D1372E00357815 /* RNNotificationUtils.h */,
508CE82122D1372E00357815 /* RNNotificationUtils.m */,
50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */,
50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */,
);
name = Helpers;
sourceTree = "<group>";
};
50E4A03D22D220E8007160C1 /* Bridge */ = {
isa = PBXGroup;
children = (
50351F9022CD7DF4000713B3 /* RNBridgeModule.h */,
50351F9122CD7DF4000713B3 /* RNBridgeModule.m */,
50351F8D22CD782F000713B3 /* RNEventEmitter.h */,
50351F8E22CD782F000713B3 /* RNEventEmitter.m */,
);
name = Bridge;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
50E4A03D22D220E8007160C1 /* Bridge */,
508CE81222D12F4300357815 /* PushKitNotifications */,
508CE81122D12F3C00357815 /* Notifications */,
508CE81F22D1371700357815 /* Helpers */,
D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */,
D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */,
50351F9622CD8604000713B3 /* RNCommandsHandler.h */,
50351F9722CD8604000713B3 /* RNCommandsHandler.m */,
508CE7C922D12B2600357815 /* RNNotificationsTests */,
134814211AA4EA7D00B7C361 /* Products */,
504D54CB22E4BCB80088F2E4 /* Frameworks */,
50E4164622E4C98B0048367F /* RNNotificationsTests.xctest */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
507DCCF622CE3EEF005D4E0B /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
508CE81D22D1337200357815 /* RNPushKit.h in Headers */,
50AD1FCA22D13ADB00E12362 /* RNPushKitEventHandler.h in Headers */,
508CE7D522D12CCA00357815 /* RNNotificationEventHandler.h in Headers */,
50FED76622D3E06500DDD516 /* RNNotificationCenter.h in Headers */,
508CE81922D130B900357815 /* RNPushKitEventListener.h in Headers */,
50E49F0722D1E4E0007160C1 /* RNNotificationsStore.h in Headers */,
508CE81622D12FF600357815 /* RNNotificationCenterListener.h in Headers */,
507DCCF922CE3F04005D4E0B /* RNNotifications.h in Headers */,
508CE82222D1372E00357815 /* RNNotificationUtils.h in Headers */,
50FED76E22D3EBA800DDD516 /* RNNotificationParser.h in Headers */,
507DCCFA22CE3F04005D4E0B /* RNEventEmitter.h in Headers */,
507DCCFB22CE3F04005D4E0B /* RNCommandsHandler.h in Headers */,
507DCCFC22CE3F04005D4E0B /* RCTConvert+RNNotifications.h in Headers */,
507DCCF722CE3EF7005D4E0B /* RNBridgeModule.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
508CE7C722D12B2600357815 /* RNNotificationsTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 508CE7D022D12B2600357815 /* Build configuration list for PBXNativeTarget "RNNotificationsTests" */;
buildPhases = (
508CE7C422D12B2600357815 /* Sources */,
508CE7C522D12B2600357815 /* Frameworks */,
508CE7C622D12B2600357815 /* Resources */,
);
buildRules = (
);
dependencies = (
508CE7CF22D12B2600357815 /* PBXTargetDependency */,
);
name = RNNotificationsTests;
productName = RNNotificationsTests;
productReference = 50E4164622E4C98B0048367F /* RNNotificationsTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
58B511DA1A9E6C8500147676 /* RNNotifications */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */;
buildPhases = (
507DCCF622CE3EEF005D4E0B /* Headers */,
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNNotifications;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRNNotifications.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
508CE7C722D12B2600357815 = {
CreatedOnToolsVersion = 10.1;
ProvisioningStyle = Automatic;
};
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RNNotifications */,
508CE7C722D12B2600357815 /* RNNotificationsTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
508CE7C622D12B2600357815 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
508CE7C422D12B2600357815 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
50002AC822E9D5EA008F6742 /* RNNotificationsStoreTests.m in Sources */,
508CE7CB22D12B2600357815 /* RNNotificationsTests.m in Sources */,
504D54BE22E4B8660088F2E4 /* RNBridgeModuleIntegrationTest.m in Sources */,
50002A4D22E88266008F6742 /* RNCommandsHandlerIntegrationTest.m in Sources */,
50002ACA22E9DB8A008F6742 /* RNNotificationEventHandlerTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
50351F9822CD8604000713B3 /* RNCommandsHandler.m in Sources */,
D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */,
50351F8F22CD782F000713B3 /* RNEventEmitter.m in Sources */,
508CE81A22D130B900357815 /* RNPushKitEventListener.m in Sources */,
50351F9222CD7DF4000713B3 /* RNBridgeModule.m in Sources */,
50FED76722D3E06500DDD516 /* RNNotificationCenter.m in Sources */,
50FED76F22D3EBA800DDD516 /* RNNotificationParser.m in Sources */,
508CE81E22D1337200357815 /* RNPushKit.m in Sources */,
508CE7D622D12CCA00357815 /* RNNotificationEventHandler.m in Sources */,
50E49F0822D1E4E0007160C1 /* RNNotificationsStore.m in Sources */,
508CE82322D1372E00357815 /* RNNotificationUtils.m in Sources */,
50351F9522CD7FF1000713B3 /* RCTConvert+RNNotifications.m in Sources */,
50AD1FCB22D13ADB00E12362 /* RNPushKitEventHandler.m in Sources */,
508CE81422D12FC700357815 /* RNNotificationCenterListener.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
508CE7CF22D12B2600357815 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 58B511DA1A9E6C8500147676 /* RNNotifications */;
targetProxy = 508CE7CE22D12B2600357815 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
508CE7D122D12B2600357815 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/RNNotificationsTests/OCMock",
);
INFOPLIST_FILE = RNNotificationsTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-l\"c++\"",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
508CE7D222D12B2600357815 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/RNNotificationsTests/OCMock",
);
INFOPLIST_FILE = RNNotificationsTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-l\"c++\"",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../example/node_modules/react-native/React/**",
"$(SRCROOT)/../../react-native/React/**",
"$(SRCROOT)/../../../React/**",
);
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNNotifications;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/../example/node_modules/react-native/React/**",
"$(SRCROOT)/../../react-native/React/**",
"$(SRCROOT)/../../../React/**",
);
LIBRARY_SEARCH_PATHS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNNotifications;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
508CE7D022D12B2600357815 /* Build configuration list for PBXNativeTarget "RNNotificationsTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
508CE7D122D12B2600357815 /* Debug */,
508CE7D222D12B2600357815 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3D3C04B91DE3340900C268FA"
BuildableName = "libyoga.a"
BlueprintName = "yoga"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
BuildableName = "libRNNotifications.a"
BlueprintName = "RNNotifications"
ReferencedContainer = "container:RNNotifications.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "508CE7C722D12B2600357815"
BuildableName = "RNNotificationsTests.xctest"
BlueprintName = "RNNotificationsTests"
ReferencedContainer = "container:RNNotifications.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
BuildableName = "libRNNotifications.a"
BlueprintName = "RNNotifications"
ReferencedContainer = "container:RNNotifications.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
BuildableName = "libRNNotifications.a"
BlueprintName = "RNNotifications"
ReferencedContainer = "container:RNNotifications.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
BuildableName = "libRNNotifications.a"
BlueprintName = "RNNotifications"
ReferencedContainer = "container:RNNotifications.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
#import <Foundation/Foundation.h>
@import UserNotifications;
@interface RNNotificationsStore : NSObject
@property (nonatomic, retain) NSDictionary* initialNotification;
+ (instancetype)sharedInstance;
- (void)completeAction:(NSString *)completionKey;
- (void)completePresentation:(NSString *)completionKey withPresentationOptions:(UNNotificationPresentationOptions)presentationOptions;
- (void)setActionCompletionHandler:(void (^)(void))completionHandler withCompletionKey:(NSString *)completionKey;
- (void)setPresentationCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler withCompletionKey:(NSString *)completionKey;
- (void (^)(void))getActionCompletionHandler:(NSString *)key;
- (void (^)(UNNotificationPresentationOptions))getPresentationCompletionHandler:(NSString *)key;
@end
#import "RNNotificationsStore.h"
@implementation RNNotificationsStore
NSMutableDictionary* _actionCompletionHandlers;
NSMutableDictionary* _presentationCompletionHandlers;
+ (instancetype)sharedInstance {
static RNNotificationsStore *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[RNNotificationsStore alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
_actionCompletionHandlers = [NSMutableDictionary new];
_presentationCompletionHandlers = [NSMutableDictionary new];
return self;
}
- (void)setActionCompletionHandler:(void (^)(void))completionHandler withCompletionKey:(NSString *)completionKey {
_actionCompletionHandlers[completionKey] = completionHandler;
}
- (void)setPresentationCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler withCompletionKey:(NSString *)completionKey {
_presentationCompletionHandlers[completionKey] = completionHandler;
}
- (void (^)(void))getActionCompletionHandler:(NSString *)key {
return _actionCompletionHandlers[key];
}
- (void (^)(UNNotificationPresentationOptions))getPresentationCompletionHandler:(NSString *)key {
return _presentationCompletionHandlers[key];
}
- (void)completeAction:(NSString *)completionKey {
void (^completionHandler)() = (void (^)())[_actionCompletionHandlers valueForKey:completionKey];
if (completionHandler) {
completionHandler();
[_actionCompletionHandlers removeObjectForKey:completionKey];
}
}
- (void)completePresentation:(NSString *)completionKey withPresentationOptions:(UNNotificationPresentationOptions)presentationOptions {
void (^completionHandler)() = (void (^)(UNNotificationPresentationOptions))[_presentationCompletionHandlers valueForKey:completionKey];
if (completionHandler) {
completionHandler(presentationOptions);
[_presentationCompletionHandlers removeObjectForKey:completionKey];
}
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
#import <XCTest/XCTest.h>
#import "RNBridgeModule.h"
#import <OCMock/OCMock.h>
@interface RNBridgeModuleIntegrationTest : XCTestCase
@property (nonatomic, strong) RNBridgeModule* bridgeModule;
@end
@implementation RNBridgeModuleIntegrationTest
- (void)setUp {
}
- (void)tearDown {
}
- (void)testRequestPermissionsWithCategories {
}
@end
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import <objc/runtime.h>
#import "RNCommandsHandler.h"
#import "RNNotificationsStore.h"
@interface RNCommandsHandlerIntegrationTest : XCTestCase
@property (nonatomic, retain) RNCommandsHandler* uut;
@property (nonatomic, retain) id notificationCenter;
@property (nonatomic, retain) id mockUserNotifications;
@end
@implementation RNCommandsHandlerIntegrationTest
- (void)setUp {
_mockUserNotifications = [OCMockObject mockForProtocol:[self getMockUserNotificationCenterProtocol]];
id notificationCenterMock = OCMClassMock([UNUserNotificationCenter class]);
OCMStub(ClassMethod([notificationCenterMock currentNotificationCenter])).andReturn(_mockUserNotifications);
UIApplication* sharedApplication = [OCMockObject mockForClass:[UIApplication class]];
id mockedApplicationClass = OCMClassMock([UIApplication class]);
OCMStub(ClassMethod([mockedApplicationClass sharedApplication])).andReturn(sharedApplication);
_uut = [RNCommandsHandler new];
_notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
}
- (void)testRequestPermissions_userAuthorizedPermissions {
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
UNNotificationSettings* settings = [UNNotificationSettings new];
[settings setValue:@(UNAuthorizationStatusAuthorized) forKey:@"authorizationStatus"];
[[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]];
[[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]];
[[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications];
[_uut requestPermissions];
[_notificationCenter verify];
}
- (void)testRequestPermissions_userDeniedPermissions {
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
UNNotificationSettings* settings = [UNNotificationSettings new];
[settings setValue:@(UNAuthorizationStatusDenied) forKey:@"authorizationStatus"];
[[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]];
[[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]];
[[(id)[UIApplication sharedApplication] reject] registerForRemoteNotifications];
[_uut requestPermissions];
[_notificationCenter verify];
}
- (void)testSetCategories_shouldSetCategories {
NSArray* json = @[@{@"identifier": @"categoryId", @"actions": @[@{@"identifier" : @"actionId", @"activationMode": @"foreground"}]}];
[[_notificationCenter expect] setNotificationCategories:[OCMArg checkWithBlock:^BOOL(NSMutableSet<UNNotificationCategory *>* categories) {
UNNotificationCategory* category = categories.allObjects.firstObject;
UNNotificationAction* action = category.actions.firstObject;
return ([category.identifier isEqualToString:@"categoryId"] &&
[action.identifier isEqualToString:@"actionId"] &&
action.options == UNNotificationActionOptionForeground);
}]];
[_uut setCategories:json];
[_notificationCenter verify];
}
- (void)testGetInitialNotification {
NSDictionary* initialNotification = @{};
[[RNNotificationsStore sharedInstance] setInitialNotification:initialNotification];
[self.uut getInitialNotification:^(id result) {
XCTAssertEqual(result, initialNotification);
} reject:^(NSString *code, NSString *message, NSError *error) {
}];
}
- (Protocol *)getMockUserNotificationCenterProtocol {
Protocol *aProtocol = objc_getProtocol("MockUserNotificationCenter");
if (!aProtocol) {
aProtocol = objc_allocateProtocol("MockUserNotificationCenter");
unsigned int methodCount = 0;
Method *methods = class_copyMethodList([UNUserNotificationCenter class], &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
protocol_addMethodDescription(aProtocol, method_getName(method), method_getTypeEncoding(method), YES, YES);
}
free(methods);
objc_registerProtocol(aProtocol);
}
return aProtocol;
}
@end
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import "RNNotificationEventHandler.h"
#import "RNNotificationUtils.h"
@interface RNNotificationEventHandlerTests : XCTestCase
@property (nonatomic, retain) RNNotificationEventHandler* uut;
@property (nonatomic, retain) RNNotificationsStore* store;
@property (nonatomic, retain) id mockedNotificationCenter;
@end
@implementation RNNotificationEventHandlerTests
- (void)setUp {
_store = [RNNotificationsStore sharedInstance];
_uut = [[RNNotificationEventHandler alloc] initWithStore:_store];
_mockedNotificationCenter = [OCMockObject partialMockForObject:[NSNotificationCenter new]];
[[[[[OCMockObject niceMockForClass:NSNotificationCenter.class] stub] classMethod] andReturn:_mockedNotificationCenter] defaultCenter];
}
- (void)testDidRegisterForRemoteNotifications_ShouldEmitEventWithDeviceTokenDataString {
NSData* deviceToken = [@"740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad" dataUsingEncoding:NSUTF32StringEncoding];
[[_mockedNotificationCenter expect] postNotificationName:RNRegistered object:[OCMArg any] userInfo:[OCMArg checkWithBlock:^BOOL(id obj) {
return ([[obj objectForKey:@"deviceToken"] isEqualToString:[RNNotificationUtils deviceTokenToString:deviceToken]]);
}]];
[_uut didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
[_mockedNotificationCenter verify];
}
- (void)testDidRegisterForRemoteNotifications_ShouldEmitEventWithDeviceTokenString {
NSString* deviceToken = @"740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad";
[[_mockedNotificationCenter expect] postNotificationName:RNRegistered object:[OCMArg any] userInfo:[OCMArg checkWithBlock:^BOOL(id obj) {
return ([[obj objectForKey:@"deviceToken"] isEqualToString:deviceToken]);
}]];
[_uut didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
[_mockedNotificationCenter verify];
}
- (void)testDidFailToRegisterForRemoteNotifications_ShouldEmitEvent {
NSError* error = [NSError errorWithDomain:@"domain" code:1 userInfo:nil];
[[_mockedNotificationCenter expect] postNotificationName:RNRegistrationFailed object:[OCMArg any] userInfo:[OCMArg checkWithBlock:^BOOL(id obj) {
return ([[obj valueForKey:@"code"] isEqualToNumber:@(1)] &&
[[obj valueForKey:@"domain"] isEqualToString:@"domain"]);
}]];
[_uut didFailToRegisterForRemoteNotificationsWithError:error];
[_mockedNotificationCenter verify];
}
- (void)testDidReceiveForegroundNotification_ShouldSaveCompletionBlockToStore {
UNNotification* notification = [self createNotificationWithIdentifier:@"id" andUserInfo:@{}];
void (^testBlock)(UNNotificationPresentationOptions) = ^void(UNNotificationPresentationOptions options) {};
[_uut didReceiveForegroundNotification:notification withCompletionHandler:testBlock];
XCTAssertEqual([_store getPresentationCompletionHandler:@"id"], testBlock);
}
- (void)testDidReceiveForegroundNotification_ShouldEmitEvent {
UNNotification* notification = [self createNotificationWithIdentifier:@"id" andUserInfo:@{@"extraKey": @"extraValue"}];
void (^testBlock)(UNNotificationPresentationOptions) = ^void(UNNotificationPresentationOptions options) {};
[[_mockedNotificationCenter expect] postNotificationName:RNNotificationReceived object:[OCMArg any] userInfo:[OCMArg checkWithBlock:^BOOL(id obj) {
return ([[obj valueForKey:@"identifier"] isEqualToString:@"id"] &&
[[obj valueForKey:@"extraKey"] isEqualToString:@"extraValue"]);
}]];
[_uut didReceiveForegroundNotification:notification withCompletionHandler:testBlock];
[_mockedNotificationCenter verify];
}
- (void)testDidReceiveNotificationResponse_ShouldEmitEvent {
UNNotification* notification = [self createNotificationWithIdentifier:@"id" andUserInfo:@{@"extraKey": @"extraValue"}];
UNNotificationResponse* response = [self createNotificationResponseWithIdentifier:@"actionId" andNotification:notification];
void (^testBlock)(void) = ^void() {};
[[_mockedNotificationCenter expect] postNotificationName:RNNotificationOpened object:[OCMArg any] userInfo:[OCMArg checkWithBlock:^BOOL(id response) {
NSDictionary* notification = response[@"notification"];
NSDictionary* action = response[@"action"];
return ([[notification valueForKey:@"identifier"] isEqualToString:@"id"] &&
[[notification valueForKey:@"extraKey"] isEqualToString:@"extraValue"] && [action[@"identifier"] isEqualToString:@"actionId"]);
}]];
[_uut didReceiveNotificationResponse:response completionHandler:testBlock];
[_mockedNotificationCenter verify];
}
- (void)testDidReceiveNotificationResponse_ShouldSaveCompletionBlockToStore {
UNNotification* notification = [self createNotificationWithIdentifier:@"id" andUserInfo:@{@"extraKey": @"extraValue"}];
UNNotificationResponse* response = [self createNotificationResponseWithIdentifier:@"id" andNotification:notification];
void (^testBlock)(void) = ^void() {};
[_uut didReceiveNotificationResponse:response completionHandler:testBlock];
XCTAssertEqual([_store getActionCompletionHandler:@"id"], testBlock);
}
- (UNNotification *)createNotificationWithIdentifier:(NSString *)identifier andUserInfo:(NSDictionary *)userInfo {
UNNotification* notification = [OCMockObject niceMockForClass:[UNNotification class]];
UNNotificationContent* content = [OCMockObject niceMockForClass:[UNNotificationContent class]];
OCMStub([content userInfo]).andReturn(userInfo);
UNNotificationRequest* request = [OCMockObject partialMockForObject:[UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil]];
OCMStub(notification.request).andReturn(request);
OCMStub(request.content).andReturn(content);
return notification;
}
- (UNNotificationResponse *)createNotificationResponseWithIdentifier:(NSString *)identifier andNotification:(UNNotification *)notification {
UNNotificationResponse* notificationResponse = [OCMockObject niceMockForClass:[UNNotificationResponse class]];
OCMStub(notificationResponse.actionIdentifier).andReturn(identifier);
OCMStub(notificationResponse.notification).andReturn(notification);
return notificationResponse;
}
@end
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import "RNNotificationsStore.h"
@interface RNNotificationsStoreTests : XCTestCase
@property (nonatomic, retain) RNNotificationsStore* store;
@end
@implementation RNNotificationsStoreTests
- (void)setUp {
_store = [RNNotificationsStore sharedInstance];
}
- (void)testSetActionCompletionHandler_ShouldStoreBlock {
void (^testBlock)(void) = ^void() {};
[_store setActionCompletionHandler:testBlock withCompletionKey:@"actionTestBlock"];
XCTAssertEqual(testBlock, [_store getActionCompletionHandler:@"actionTestBlock"]);
}
- (void)testCompleteAction_ShouldInvokeBlock {
__block BOOL blockInvoked = NO;
void (^testBlock)(void) = ^void() {
blockInvoked = YES;
};
[_store setActionCompletionHandler:testBlock withCompletionKey:@"actionTestBlock"];
[_store completeAction:@"actionTestBlock"];
XCTAssertTrue(blockInvoked);
}
- (void)testCompleteAction_ShouldRemoveBlock {
__block BOOL blockInvoked = NO;
void (^testBlock)(void) = ^void() {
blockInvoked = YES;
};
[_store setActionCompletionHandler:testBlock withCompletionKey:@"actionTestBlock"];
[_store completeAction:@"actionTestBlock"];
XCTAssertNil([_store getActionCompletionHandler:@"actionTestBlock"]);
}
- (void)testSetPersentationCompletionHandler_ShouldStoreBlock {
void (^testBlock)(UNNotificationPresentationOptions) = ^void(UNNotificationPresentationOptions options) {};
[_store setPresentationCompletionHandler:testBlock withCompletionKey:@"presentationTestBlock"];
XCTAssertEqual(testBlock, [_store getPresentationCompletionHandler:@"presentationTestBlock"]);
}
- (void)testCompletePresentation_ShouldInvokeBlockWithParams {
__block UNNotificationPresentationOptions presentationOptions;
void (^testBlock)(UNNotificationPresentationOptions) = ^void(UNNotificationPresentationOptions options) {
presentationOptions = options;
};
[_store setPresentationCompletionHandler:testBlock withCompletionKey:@"presentationTestBlock"];
[_store completePresentation:@"presentationTestBlock" withPresentationOptions:UNNotificationPresentationOptionAlert];
XCTAssertEqual(presentationOptions, UNNotificationPresentationOptionAlert);
}
- (void)testCompletePresentation_ShouldRemoveBlock {
__block UNNotificationPresentationOptions presentationOptions;
void (^testBlock)(UNNotificationPresentationOptions) = ^void(UNNotificationPresentationOptions options) {
presentationOptions = options;
};
[_store setPresentationCompletionHandler:testBlock withCompletionKey:@"presentationTestBlock"];
[_store completePresentation:@"presentationTestBlock" withPresentationOptions:UNNotificationPresentationOptionAlert];
XCTAssertNil([_store getPresentationCompletionHandler:@"presentationTestBlock"]);
}
@end
//
// RNNotificationsTests.m
// RNNotificationsTests
//
// Created by Yogev Ben David on 06/07/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface RNNotificationsTests : XCTestCase
@end
@implementation RNNotificationsTests
- (void)setUp {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@end
#import <UIKit/UIKit.h>
#import "RNPushKitEventListener.h"
@import PushKit;
@interface RNPushKit : NSObject
- (instancetype)initWithEventHandler:(RNPushKitEventHandler *)pushKitEventHandler;
@end
#import "RNPushKit.h"
@implementation RNPushKit {
RNPushKitEventListener* _pushKitEventListener;
}
- (instancetype)initWithEventHandler:(RNPushKitEventHandler *)pushKitEventHandler {
self = [super init];
_pushKitEventListener = [[RNPushKitEventListener alloc] initWithPushKitEventHandler:pushKitEventHandler];
PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
pushKitRegistry.delegate = _pushKitEventListener;
pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
return self;
}
@end
#import <Foundation/Foundation.h>
#import "RNNotificationEventHandler.h"
@interface RNPushKitEventHandler : RNNotificationEventHandler
- (void)registeredWithToken:(NSString *)token;
- (void)didReceiveIncomingPushWithPayload:(NSDictionary *)payload;
@end
#import "RNPushKitEventHandler.h"
#import "RNEventEmitter.h"
@implementation RNPushKitEventHandler
- (void)registeredWithToken:(NSString *)token {
[RNEventEmitter sendEvent:RNPushKitRegistered body:@{@"pushKitToken": token}];
}
- (void)didReceiveIncomingPushWithPayload:(NSDictionary *)payload {
[RNEventEmitter sendEvent:RNPushKitNotificationReceived body:payload];
}
@end
#import <Foundation/Foundation.h>
@import PushKit;
#import "RNPushKitEventHandler.h"
@interface RNPushKitEventListener : NSObject <PKPushRegistryDelegate>
- (instancetype)initWithPushKitEventHandler:(RNPushKitEventHandler *)pushKitEventHandler;
@end
#import "RNPushKitEventListener.h"
#import "RNNotificationUtils.h"
@implementation RNPushKitEventListener {
PKPushRegistry* _pushRegistry;
RNPushKitEventHandler* _pushKitEventHandler;
}
- (instancetype)initWithPushKitEventHandler:(RNPushKitEventHandler *)pushKitEventHandler {
self = [super init];
_pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
_pushKitEventHandler = pushKitEventHandler;
return self;
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
[_pushKitEventHandler registeredWithToken:[RNNotificationUtils deviceTokenToString:credentials.token]];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
[_pushKitEventHandler didReceiveIncomingPushWithPayload:payload.dictionaryPayload];
}
@end
import { Notification } from './Notification';
describe('Notification', () => {
it('Should create notification with payload', () => {
const payload = { p: 'p' };
const notification = new Notification(payload);
expect(notification.data).toEqual(payload);
});
it('Should create notification with identifier', () => {
const payload = { identifier: 'identifier' };
const notification = new Notification(payload);
expect(notification.identifier).toEqual(payload.identifier);
});
it('Should return title from payload', () => {
const payload = { title: 'title' };
const notification = new Notification(payload);
expect(notification.title).toEqual(payload.title);
});
it('Should return body from payload', () => {
const payload = { body: 'body' };
const notification = new Notification(payload);
expect(notification.body).toEqual(payload.body);
});
it('Should return sound from payload', () => {
const payload = { sound: 'sound.mp4' };
const notification = new Notification(payload);
expect(notification.sound).toEqual(payload.sound);
});
it('Should return badge from payload', () => {
const payload = { badge: 1 };
const notification = new Notification(payload);
expect(notification.badge).toEqual(payload.badge);
});
it('Should return type from payload', () => {
const payload = { type: 'type' };
const notification = new Notification(payload);
expect(notification.type).toEqual(payload.type);
});
it('Should return thread from payload', () => {
const payload = { thread: 'thread' };
const notification = new Notification(payload);
expect(notification.thread).toEqual(payload.thread);
});
});
export class Notification {
identifier: string;
private _data?: any;
constructor(payload: object) {
this._data = payload;
this.identifier = this._data.identifier;
}
get data(): any {
return this._data;
}
get title(): string {
return this._data.title;
}
get body(): string {
return this._data.body;
}
get sound(): string {
return this._data.sound;
}
get badge(): number {
return this._data.badge;
}
get type(): string {
return this._data.type;
}
get thread(): string {
return this._data.thread;
}
}
import { NativeCommandsSender } from './adapters/NativeCommandsSender';
import { NativeEventsReceiver } from './adapters/NativeEventsReceiver';
import { Commands } from './commands/Commands';
import { EventsRegistry } from './events/EventsRegistry';
import { EventsRegistryIOS } from './events/EventsRegistryIOS';
import { Notification } from './DTO/Notification';
import { UniqueIdProvider } from './adapters/UniqueIdProvider';
import { CompletionCallbackWrapper } from './adapters/CompletionCallbackWrapper';
import { NotificationCategory } from './interfaces/NotificationCategory';
import { NotificationsIOS } from './NotificationsIOS';
import { NotificationsAndroid } from './NotificationsAndroid';
export class NotificationsRoot {
public readonly ios: NotificationsIOS;
public readonly android: NotificationsAndroid;
private readonly nativeEventsReceiver: NativeEventsReceiver;
private readonly nativeCommandsSender: NativeCommandsSender;
private readonly commands: Commands;
private readonly eventsRegistry: EventsRegistry;
private readonly eventsRegistryIOS: EventsRegistryIOS;
private readonly uniqueIdProvider: UniqueIdProvider;
private readonly completionCallbackWrapper: CompletionCallbackWrapper;
constructor() {
this.nativeEventsReceiver = new NativeEventsReceiver();
this.nativeCommandsSender = new NativeCommandsSender();
this.completionCallbackWrapper = new CompletionCallbackWrapper(this.nativeCommandsSender);
this.uniqueIdProvider = new UniqueIdProvider();
this.commands = new Commands(
this.nativeCommandsSender,
this.uniqueIdProvider
);
this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.completionCallbackWrapper);
this.eventsRegistryIOS = new EventsRegistryIOS(this.nativeEventsReceiver);
this.ios = new NotificationsIOS(this.commands, this.eventsRegistryIOS);
this.android = new NotificationsAndroid(this.commands);
}
/**
* registerRemoteNotifications
*/
public registerRemoteNotifications() {
this.ios.registerRemoteNotifications();
this.android.registerRemoteNotifications();
}
/**
* postLocalNotification
*/
public postLocalNotification(notification: Notification, id: number) {
return this.commands.postLocalNotification(notification, id);
}
/**
* getInitialNotification
*/
public getInitialNotification(): Promise<Notification | undefined> {
return this.commands.getInitialNotification();
}
/**
* setCategories
*/
public setCategories(categories: [NotificationCategory?]) {
this.commands.setCategories(categories);
}
/**
* cancelLocalNotification
*/
public cancelLocalNotification(notificationId: string) {
return this.commands.cancelLocalNotification(notificationId);
}
/**
* isRegisteredForRemoteNotifications
*/
public isRegisteredForRemoteNotifications(): Promise<boolean> {
return this.commands.isRegisteredForRemoteNotifications();
}
/**
* Obtain the events registry instance
*/
public events(): EventsRegistry {
return this.eventsRegistry;
}
}
import { Commands } from './commands/Commands';
import { Platform } from 'react-native';
export class NotificationsAndroid {
constructor(private readonly commands: Commands) {
return new Proxy(this, {
get(target, name) {
if (Platform.OS === 'android') {
return (target as any)[name];
} else {
return () => {};
}
}
});
}
/**
* Refresh FCM token
*/
public registerRemoteNotifications() {
this.commands.refreshToken();
}
}
import { Notification } from './DTO/Notification';
import { Commands } from './commands/Commands';
import { Platform } from 'react-native';
import { EventsRegistryIOS } from './events/EventsRegistryIOS';
export class NotificationsIOS {
constructor(private readonly commands: Commands, private readonly eventsRegistry: EventsRegistryIOS) {
return new Proxy(this, {
get(target, name) {
if (Platform.OS === 'ios') {
return (target as any)[name];
} else {
return () => {};
}
}
});
}
/**
* Request permissions to send remote notifications
*/
public registerRemoteNotifications() {
return this.commands.requestPermissions();
}
/**
* Unregister for all remote notifications received via Apple Push Notification service
*/
public abandonPermissions() {
return this.commands.abandonPermissions();
}
/**
* registerPushKit
*/
public registerPushKit() {
return this.commands.registerPushKit();
}
/**
* getBadgesCount
*/
public getBadgeCount(): Promise<number> {
return this.commands.getBadgeCount();
}
/**
* setBadgeCount
* @param count number of the new badge count
*/
public setBadgeCount(count: number) {
return this.commands.setBadgeCount(count);
}
/**
* cancelAllLocalNotifications
*/
public cancelAllLocalNotifications() {
this.commands.cancelAllLocalNotifications();
}
/**
* checkPermissions
*/
public checkPermissions() {
return this.commands.checkPermissions();
}
/**
* removeAllDeliveredNotifications
*/
public removeAllDeliveredNotifications() {
return this.commands.removeAllDeliveredNotifications();
}
/**
* removeDeliveredNotifications
* @param identifiers Array of notification identifiers
*/
public removeDeliveredNotifications(identifiers: Array<string>) {
return this.commands.removeDeliveredNotifications(identifiers);
}
/**
* getDeliveredNotifications
*/
public getDeliveredNotifications(): Array<Notification> {
return this.commands.getDeliveredNotifications();
}
/**
* Obtain the events registry instance
*/
public events(): EventsRegistryIOS {
return this.eventsRegistry;
}
}
import { NativeCommandsSender } from './NativeCommandsSender';
import { Notification } from '../DTO/Notification';
import { NotificationCompletion } from '../interfaces/NotificationCompletion';
import { Platform } from 'react-native';
export class CompletionCallbackWrapper {
constructor(
private readonly nativeCommandsSender: NativeCommandsSender
) {}
public wrapReceivedCallback(callback: Function): (notification: Notification) => void {
return (notification) => {
const completion = (response: NotificationCompletion) => {
if (Platform.OS === 'ios') {
this.nativeCommandsSender.finishPresentingNotification(notification.identifier, response);
}
};
callback(notification, completion);
}
}
public wrapOpenedCallback(callback: Function): (notification: Notification) => void {
return (notification) => {
const completion = () => {
if (Platform.OS === 'ios') {
this.nativeCommandsSender.finishHandlingAction(notification.identifier);
}
};
callback(notification, completion);
}
}
}
export const { NativeCommandsSender } = jest.genMockFromModule('./NativeCommandsSender');
import { NativeModules } from 'react-native';
import { Notification } from '../DTO/Notification';
import { NotificationCompletion } from '../interfaces/NotificationCompletion';
import { NotificationPermissions } from '../interfaces/NotificationPermissions';
import { NotificationCategory } from '../interfaces/NotificationCategory';
interface NativeCommandsModule {
getInitialNotification(): Promise<Object>;
postLocalNotification(notification: Notification, id: number): void;
requestPermissions(): void;
abandonPermissions(): void;
refreshToken(): void;
registerPushKit(): void;
getBadgeCount(): Promise<number>;
setBadgeCount(count: number): void;
cancelLocalNotification(notificationId: string): void;
cancelAllLocalNotifications(): void;
isRegisteredForRemoteNotifications(): Promise<boolean>;
checkPermissions(): Promise<NotificationPermissions>;
removeDeliveredNotifications(identifiers: Array<string>): void;
removeAllDeliveredNotifications(): void;
getDeliveredNotifications(): Array<Notification>;
setCategories(categories: [NotificationCategory?]): void;
finishPresentingNotification(notificationId: string, callback: NotificationCompletion): void;
finishHandlingAction(notificationId: string): void;
}
export class NativeCommandsSender {
private readonly nativeCommandsModule: NativeCommandsModule;
constructor() {
this.nativeCommandsModule = NativeModules.RNBridgeModule;
}
postLocalNotification(notification: Notification, id: number) {
return this.nativeCommandsModule.postLocalNotification(notification, id);
}
getInitialNotification(): Promise<Object> {
return this.nativeCommandsModule.getInitialNotification();
}
requestPermissions() {
return this.nativeCommandsModule.requestPermissions();
}
abandonPermissions() {
return this.nativeCommandsModule.abandonPermissions();
}
refreshToken() {
this.nativeCommandsModule.refreshToken();
}
registerPushKit() {
return this.nativeCommandsModule.registerPushKit();
}
setCategories(categories: [NotificationCategory?]) {
this.nativeCommandsModule.setCategories(categories);
}
getBadgeCount(): Promise<number> {
return this.nativeCommandsModule.getBadgeCount();
}
setBadgeCount(count: number) {
this.nativeCommandsModule.setBadgeCount(count);
}
cancelLocalNotification(notificationId: string) {
this.nativeCommandsModule.cancelLocalNotification(notificationId);
}
cancelAllLocalNotifications() {
this.nativeCommandsModule.cancelAllLocalNotifications();
}
isRegisteredForRemoteNotifications(): Promise<any> {
return this.nativeCommandsModule.isRegisteredForRemoteNotifications();
}
checkPermissions() {
return this.nativeCommandsModule.checkPermissions();
}
removeAllDeliveredNotifications() {
return this.nativeCommandsModule.removeAllDeliveredNotifications();
}
removeDeliveredNotifications(identifiers: Array<string>) {
return this.nativeCommandsModule.removeDeliveredNotifications(identifiers);
}
public getDeliveredNotifications(): Array<Notification> {
return this.nativeCommandsModule.getDeliveredNotifications();
}
finishPresentingNotification(notificationId: string, notificationCompletion: NotificationCompletion): void {
this.nativeCommandsModule.finishPresentingNotification(notificationId, notificationCompletion);
}
finishHandlingAction(notificationId: string): void {
this.nativeCommandsModule.finishHandlingAction(notificationId);
}
}
export const { NativeEventsReceiver } = jest.genMockFromModule('./NativeEventsReceiver');
import { NativeModules, NativeEventEmitter, EventEmitter, EmitterSubscription } from 'react-native';
import {
Registered, RegistrationError, RegisteredPushKit
} from '../interfaces/NotificationEvents';
import { Notification } from '../DTO/Notification';
import { NotificationActionResponse } from '../interfaces/NotificationActionResponse';
export class NativeEventsReceiver {
private emitter: EventEmitter;
constructor() {
this.emitter = new NativeEventEmitter(NativeModules.RNEventEmitter);
}
public registerRemoteNotificationsRegistered(callback: (event: Registered) => void): EmitterSubscription {
return this.emitter.addListener('remoteNotificationsRegistered', callback);
}
public registerPushKitRegistered(callback: (event: RegisteredPushKit) => void): EmitterSubscription {
return this.emitter.addListener('pushKitRegistered', callback);
}
public registerRemoteNotificationReceived(callback: (notification: Notification) => void): EmitterSubscription {
return this.emitter.addListener('notificationReceived', (payload) => {
callback(new Notification(payload));
});
}
public registerPushKitNotificationReceived(callback: (event: object) => void): EmitterSubscription {
return this.emitter.addListener('pushKitNotificationReceived', callback);
}
public registerRemoteNotificationOpened(callback: (notification: Notification, completion: () => void, actionResponse?: NotificationActionResponse) => void): EmitterSubscription {
return this.emitter.addListener('notificationOpened', (response, completion) => {
const action = response.action ? new NotificationActionResponse(response.action) : undefined
callback(new Notification(response.notification), completion, action);
});
}
public registerRemoteNotificationsRegistrationFailed(callback: (event: RegistrationError) => void): EmitterSubscription {
return this.emitter.addListener('remoteNotificationsRegistrationFailed', callback);
}
}
import * as _ from 'lodash';
export class UniqueIdProvider {
generate(): number {
return parseInt(_.uniqueId());
}
}
import * as _ from 'lodash';
import { mock, verify, instance, when, anyNumber } from 'ts-mockito';
import { Commands } from './Commands';
import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
import { Notification } from '../DTO/Notification';
import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
import { NotificationCategory } from '../interfaces/NotificationCategory';
import { NotificationPermissions } from '../interfaces/NotificationPermissions';
describe('Commands', () => {
let uut: Commands;
let mockedNativeCommandsSender: NativeCommandsSender;
let mockedUniqueIdProvider: UniqueIdProvider;
beforeEach(() => {
mockedNativeCommandsSender = mock(NativeCommandsSender);
mockedUniqueIdProvider = mock(UniqueIdProvider);
when(mockedUniqueIdProvider.generate()).thenCall(() => 12);
uut = new Commands(
instance(mockedNativeCommandsSender),
instance(mockedUniqueIdProvider)
);
});
describe('getInitialNotification', () => {
it('sends to native', () => {
uut.getInitialNotification();
verify(mockedNativeCommandsSender.getInitialNotification()).called();
});
it('returns a promise with the initial notification', async () => {
const expectedNotification: Notification = new Notification({identifier: 'id'});
when(mockedNativeCommandsSender.getInitialNotification()).thenResolve(
{identifier: 'id'}
);
const result = await uut.getInitialNotification();
expect(result).toEqual(expectedNotification);
});
});
describe('requestPermissions', () => {
it('sends to native', () => {
uut.requestPermissions();
verify(mockedNativeCommandsSender.requestPermissions()).called();
});
});
describe('registerPushKit', () => {
it('sends to native', () => {
uut.registerPushKit();
verify(mockedNativeCommandsSender.registerPushKit()).called();
});
});
describe('setCategories', () => {
it('sends to native', () => {
const emptyCategoriesArray: [NotificationCategory?] = [];
uut.setCategories(emptyCategoriesArray);
verify(mockedNativeCommandsSender.setCategories(emptyCategoriesArray)).called();
});
it('sends to native with categories', () => {
const category: NotificationCategory = {identifier: 'id', actions: []};
const categoriesArray: [NotificationCategory] = [category];
uut.setCategories(categoriesArray);
verify(mockedNativeCommandsSender.setCategories(categoriesArray)).called();
});
});
describe('abandonPermissions', () => {
it('sends to native', () => {
uut.abandonPermissions();
verify(mockedNativeCommandsSender.abandonPermissions()).called();
});
});
describe('postLocalNotification', () => {
it('sends to native', () => {
const notification: Notification = new Notification({identifier: 'id'});
uut.postLocalNotification(notification);
verify(mockedNativeCommandsSender.postLocalNotification(notification, anyNumber())).called();
});
it('generates unique identifier', () => {
const notification: Notification = new Notification({identifier: 'id'});
uut.postLocalNotification(notification);
verify(mockedNativeCommandsSender.postLocalNotification(notification, anyNumber())).called();
});
it('use passed notification id', () => {
const notification: Notification = new Notification({identifier: 'id'});
const passedId: number = 2;
uut.postLocalNotification(notification, passedId);
verify(mockedNativeCommandsSender.postLocalNotification(notification, passedId)).called();
});
});
describe('getBadgeCount', () => {
it('sends to native', () => {
uut.getBadgeCount();
verify(mockedNativeCommandsSender.getBadgeCount()).called();
});
});
describe('setBadgeCount', () => {
it('sends to native', () => {
uut.setBadgeCount(10);
verify(mockedNativeCommandsSender.setBadgeCount(10)).called();
});
});
describe('cancelLocalNotification', () => {
it('sends to native', () => {
uut.cancelLocalNotification("notificationId");
verify(mockedNativeCommandsSender.cancelLocalNotification("notificationId")).called();
});
});
describe('cancelAllLocalNotifications', () => {
it('sends to native', () => {
uut.cancelAllLocalNotifications();
verify(mockedNativeCommandsSender.cancelAllLocalNotifications()).called();
});
});
describe('isRegisteredForRemoteNotifications', () => {
it('sends to native', () => {
uut.isRegisteredForRemoteNotifications();
verify(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).called();
});
it('return positive response from native', async () => {
when(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).thenResolve(
true
);
const isRegistered = await uut.isRegisteredForRemoteNotifications();
verify(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).called();
expect(isRegistered).toEqual(true);
});
it('return negative response from native', async () => {
when(mockedNativeCommandsSender.isRegisteredForRemoteNotifications()).thenResolve(
false
);
const isRegistered = await uut.isRegisteredForRemoteNotifications();
expect(isRegistered).toEqual(false);
});
});
describe('checkPermissions', () => {
it('sends to native', () => {
uut.checkPermissions();
verify(mockedNativeCommandsSender.checkPermissions()).called();
});
it('return negative response from native', async () => {
const expectedPermissions: NotificationPermissions = {badge: false, alert: true, sound: false};
when(mockedNativeCommandsSender.checkPermissions()).thenResolve(
expectedPermissions
);
const permissions = await uut.checkPermissions();
expect(permissions).toEqual(expectedPermissions);
});
});
describe('removeAllDeliveredNotifications', () => {
it('sends to native', () => {
uut.removeAllDeliveredNotifications();
verify(mockedNativeCommandsSender.removeAllDeliveredNotifications()).called();
});
});
describe('removeDeliveredNotifications', async () => {
it('sends to native', () => {
const identifiers: Array<string> = ["id1", "id2"];
uut.removeDeliveredNotifications(identifiers);
verify(mockedNativeCommandsSender.removeDeliveredNotifications(identifiers)).called();
});
});
describe('getDeliveredNotifications', () => {
it('sends to native', () => {
uut.getDeliveredNotifications();
verify(mockedNativeCommandsSender.getDeliveredNotifications()).called();
});
});
describe('refreshToken', () => {
it('sends to native', () => {
uut.refreshToken();
verify(mockedNativeCommandsSender.refreshToken()).called();
});
});
});
import * as _ from 'lodash';
import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
import { Notification } from '../DTO/Notification';
import { NotificationCategory } from '../interfaces/NotificationCategory';
import { NotificationPermissions } from '../interfaces/NotificationPermissions';
import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
export class Commands {
constructor(
private readonly nativeCommandsSender: NativeCommandsSender,
private readonly uniqueIdProvider: UniqueIdProvider
) { }
public postLocalNotification(notification: Notification, id?: number) {
const notificationId: number = id ? id : this.uniqueIdProvider.generate();
const result = this.nativeCommandsSender.postLocalNotification(notification, notificationId);
return result;
}
public async getInitialNotification(): Promise<Notification | undefined> {
return this.nativeCommandsSender.getInitialNotification().then((payload) => {
if (payload) {
return new Notification(payload);
}
return undefined;
});
}
public requestPermissions() {
const result = this.nativeCommandsSender.requestPermissions();
return result;
}
public abandonPermissions() {
const result = this.nativeCommandsSender.abandonPermissions();
return result;
}
public registerPushKit() {
this.nativeCommandsSender.registerPushKit();
}
public setCategories(categories: [NotificationCategory?]) {
this.nativeCommandsSender.setCategories(categories);
}
public getBadgeCount(): Promise<number> {
return this.nativeCommandsSender.getBadgeCount();
}
public setBadgeCount(count: number) {
this.nativeCommandsSender.setBadgeCount(count);
}
public cancelLocalNotification(notificationId: string) {
this.nativeCommandsSender.cancelLocalNotification(notificationId);
}
public cancelAllLocalNotifications() {
this.nativeCommandsSender.cancelAllLocalNotifications();
}
public isRegisteredForRemoteNotifications(): Promise<boolean> {
return this.nativeCommandsSender.isRegisteredForRemoteNotifications();
}
public checkPermissions(): Promise<NotificationPermissions> {
return this.nativeCommandsSender.checkPermissions();
}
public removeAllDeliveredNotifications() {
this.nativeCommandsSender.removeAllDeliveredNotifications();
}
public removeDeliveredNotifications(identifiers: Array<string>) {
return this.nativeCommandsSender.removeDeliveredNotifications(identifiers);
}
public getDeliveredNotifications(): Array<Notification> {
return this.nativeCommandsSender.getDeliveredNotifications();
}
public refreshToken() {
this.nativeCommandsSender.refreshToken();
}
}
import { EventsRegistry } from './EventsRegistry';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver.mock';
import { Notification } from '../DTO/Notification';
import { CompletionCallbackWrapper } from '../adapters/CompletionCallbackWrapper';
import { NativeCommandsSender } from '../adapters/NativeCommandsSender.mock';
import { NotificationResponse } from '../interfaces/NotificationEvents';
import { Platform } from 'react-native';
import { NotificationCompletion } from '../interfaces/NotificationCompletion';
describe('EventsRegistry', () => {
let uut: EventsRegistry;
const mockNativeEventsReceiver = new NativeEventsReceiver();
const mockNativeCommandsSender = new NativeCommandsSender();
const completionCallbackWrapper = new CompletionCallbackWrapper(mockNativeCommandsSender);
beforeEach(() => {
uut = new EventsRegistry(mockNativeEventsReceiver, completionCallbackWrapper);
});
describe('registerRemoteNotificationsReceived', () => {
it('delegates to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerNotificationReceived(cb);
expect(mockNativeEventsReceiver.registerRemoteNotificationReceived).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerRemoteNotificationReceived).toHaveBeenCalledWith(expect.any(Function));
});
it('should wrap callback with completion block', () => {
const wrappedCallback = jest.fn();
const notification: Notification = new Notification({identifier: 'identifier'});
uut.registerNotificationReceived(wrappedCallback);
const call = mockNativeEventsReceiver.registerRemoteNotificationReceived.mock.calls[0][0];
call(notification);
expect(wrappedCallback).toBeCalledWith(notification, expect.any(Function));
expect(wrappedCallback).toBeCalledTimes(1);
});
it('should wrap callback with completion block', () => {
const expectedNotification: Notification = new Notification({identifier: 'identifier'});
uut.registerNotificationReceived((notification) => {
expect(notification).toEqual(expectedNotification);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationReceived.mock.calls[0][0];
call(expectedNotification);
});
it('should invoke finishPresentingNotification', () => {
const notification: Notification = new Notification({identifier: 'notificationId'});
const response: NotificationCompletion = {alert: true}
uut.registerNotificationReceived((notification, completion) => {
completion(response);
expect(mockNativeCommandsSender.finishPresentingNotification).toBeCalledWith(notification.identifier, response);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationReceived.mock.calls[0][0];
call(notification);
});
it('should not invoke finishPresentingNotification on Android', () => {
Platform.OS = 'android';
const expectedNotification: Notification = new Notification({identifier: 'notificationId'});
const response: NotificationCompletion = {alert: true}
uut.registerNotificationReceived((notification, completion) => {
completion(response);
expect(expectedNotification).toEqual(notification);
expect(mockNativeCommandsSender.finishPresentingNotification).toBeCalledTimes(0);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationReceived.mock.calls[0][0];
call(expectedNotification);
});
});
describe('', () => {
it('delegates to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerRemoteNotificationOpened(cb);
expect(mockNativeEventsReceiver.registerRemoteNotificationOpened).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerRemoteNotificationOpened).toHaveBeenCalledWith(expect.any(Function));
});
it('should wrap callback with completion block', () => {
const wrappedCallback = jest.fn();
const notification: Notification = new Notification({identifier: 'identifier'});
const response: NotificationResponse = {notification, identifier: 'responseId'};
uut.registerRemoteNotificationOpened(wrappedCallback);
const call = mockNativeEventsReceiver.registerRemoteNotificationOpened.mock.calls[0][0];
call(response);
expect(wrappedCallback).toBeCalledWith(response, expect.any(Function));
expect(wrappedCallback).toBeCalledTimes(1);
});
it('should wrap callback with completion block', () => {
const notification: Notification = new Notification({identifier: 'identifier'});
const expectedResponse: NotificationResponse = {notification, identifier: 'responseId'}
uut.registerRemoteNotificationOpened((response) => {
expect(response).toEqual(expectedResponse);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationOpened.mock.calls[0][0];
call(expectedResponse);
});
it('calling completion should invoke finishHandlingAction', () => {
const expectedNotification: Notification = new Notification({identifier: 'notificationId'});
uut.registerRemoteNotificationOpened((notification, completion) => {
completion();
expect(expectedNotification).toEqual(notification);
expect(mockNativeCommandsSender.finishHandlingAction).toBeCalledWith(notification.identifier);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationOpened.mock.calls[0][0];
call(expectedNotification);
});
it('should not invoke finishHandlingAction on Android', () => {
Platform.OS = 'android';
const expectedNotification: Notification = new Notification({identifier: 'notificationId'});
uut.registerRemoteNotificationOpened((notification, completion) => {
completion();
expect(expectedNotification).toEqual(notification);
expect(mockNativeCommandsSender.finishHandlingAction).toBeCalledTimes(0);
});
const call = mockNativeEventsReceiver.registerRemoteNotificationOpened.mock.calls[0][0];
call(expectedNotification);
});
});
it('delegates registerRemoteNotificationsRegistered to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerRemoteNotificationsRegistered(cb);
expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistered).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistered).toHaveBeenCalledWith(cb);
});
it('delegates registerRemoteNotificationsRegistrationFailed to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerRemoteNotificationsRegistrationFailed(cb);
expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistrationFailed).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistrationFailed).toHaveBeenCalledWith(cb);
});
});
import { EmitterSubscription } from 'react-native';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
import {
Registered,
RegistrationError,
NotificationResponse
} from '../interfaces/NotificationEvents';
import { CompletionCallbackWrapper } from '../adapters/CompletionCallbackWrapper';
import { Notification } from '../DTO/Notification';
import { NotificationCompletion } from '../interfaces/NotificationCompletion';
export class EventsRegistry {
constructor(
private nativeEventsReceiver: NativeEventsReceiver,
private completionCallbackWrapper: CompletionCallbackWrapper)
{}
public registerRemoteNotificationsRegistered(callback: (event: Registered) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationsRegistered(callback);
}
public registerNotificationReceived(callback: (notification: Notification, completion: (response: NotificationCompletion) => void) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationReceived(this.completionCallbackWrapper.wrapReceivedCallback(callback));
}
public registerRemoteNotificationOpened(callback: (response: NotificationResponse, completion: () => void) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationOpened(this.completionCallbackWrapper.wrapOpenedCallback(callback));
}
public registerRemoteNotificationsRegistrationFailed(callback: (event: RegistrationError) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationsRegistrationFailed(callback);
}
}
import { EventsRegistryIOS } from './EventsRegistryIOS';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver.mock';
describe('EventsRegistryIOS', () => {
let uut: EventsRegistryIOS;
const mockNativeEventsReceiver = new NativeEventsReceiver();
beforeEach(() => {
uut = new EventsRegistryIOS(mockNativeEventsReceiver);
});
it('delegates registerPushKitRegistered to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitRegistered(cb);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledWith(cb);
});
it('delegates registerPushKitNotificationReceived to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitNotificationReceived(cb);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledWith(cb);
});
});
import { EmitterSubscription } from 'react-native';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
import {
RegisteredPushKit
} from '../interfaces/NotificationEvents';
export class EventsRegistryIOS {
constructor(
private nativeEventsReceiver: NativeEventsReceiver)
{}
public registerPushKitRegistered(callback: (event: RegisteredPushKit) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitRegistered(callback);
}
public registerPushKitNotificationReceived(callback: (event: object) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitNotificationReceived(callback);
}
}
import { NotificationsRoot } from './Notifications';
const notificationsSingleton = new NotificationsRoot();
export const Notifications = notificationsSingleton;
export * from './interfaces/EventSubscription';
export * from './DTO/Notification';
export * from './interfaces/NotificationEvents';
export * from './interfaces/NotificationCategory';
export interface EventSubscription {
remove(): void;
}
export class NotificationActionResponse {
identifier: string;
text?: string;
constructor(response: any) {
this.identifier = response.identifier;
this.text = response.text;
}
}
export class NotificationCategory {
identifier: string
actions: [NotificationAction?];
constructor(identifier: string, actions: [NotificationAction?]) {
this.identifier = identifier;
this.actions = actions;
}
}
export interface NotificationTextInput {
buttonTitle: string;
placeholder: string;
}
export class NotificationAction {
identifier: string;
activationMode: 'foreground' | 'authenticationRequired' | 'destructive';
title: string;
authenticationRequired: boolean;
textInput: NotificationTextInput;
constructor(identifier: string, activationMode: 'foreground' | 'authenticationRequired' | 'destructive', title: string, authenticationRequired: boolean, textInput: NotificationTextInput) {
this.identifier = identifier;
this.activationMode = activationMode;
this.title = title;
this.authenticationRequired = authenticationRequired;
this.textInput = textInput;
}
}
\ No newline at end of file
export interface NotificationCompletion {
badge?: boolean;
alert?: boolean;
sound?: boolean;
}
import { Notification } from '../DTO/Notification';
import { NotificationActionResponse } from './NotificationActionResponse';
export interface Registered {
deviceToken: string;
}
export interface RegistrationError {
code: string;
domain: string;
localizedDescription: string;
}
export interface RegisteredPushKit {
pushKitToken: string;
}
export interface NotificationResponse {
identifier: string;
notification: Notification;
action?: NotificationActionResponse
}
export interface NotificationPermissions {
badge: boolean;
alert: boolean;
sound: boolean;
}
\ No newline at end of file
module.exports = {
projectRoot: `${__dirname}/example`,
watchFolders: [
__dirname,
],
resolver: {
sourceExts: ['ts', 'tsx', 'js']
},
transformer: {
babelTransformerPath: require.resolve('react-native-typescript-transformer'),
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
})
}
};
/** A wrapper to align Android with iOS in terms on notification structure. */
export default class NotificationAndroid {
constructor(notification) {
this.data = notification;
}
getData() {
return this.data;
}
getTitle() {
return this.data.title;
}
getMessage() {
return this.data.body;
}
}
export default class IOSNotification {
_data: Object;
_alert: string | Object;
_sound: string;
_badge: number;
_category: string;
_type: string; // regular / managed
_thread: string;
constructor(notification: Object) {
this._data = {};
if (notification.aps &&
notification.aps["content-available"] &&
notification.aps["content-available"] === 1 &&
!notification.aps.alert &&
!notification.aps.sound &&
notification.managedAps) {
// managed notification
this._alert = notification.managedAps.alert;
this._sound = notification.managedAps.sound;
this._badge = notification.aps.badge;
this._category = notification.managedAps.category;
this._type = "managed";
this._thread = notification.aps["thread-id"];
} else if (
notification.aps &&
notification.aps.alert) {
// regular notification
this._alert = notification.aps.alert;
this._sound = notification.aps.sound;
this._badge = notification.aps.badge;
this._category = notification.aps.category;
this._type = "regular";
this._thread = notification.aps["thread-id"];
}
Object.keys(notification).filter(key => key !== "aps").forEach(key => {
this._data[key] = notification[key];
});
}
getMessage(): ?string | ?Object {
return this._alert;
}
getSound(): ?string {
return this._sound;
}
getBadgeCount(): ?number {
return this._badge;
}
getCategory(): ?string {
return this._category;
}
getData(): ?Object {
return this._data;
}
getType(): ?string {
return this._type;
}
getThread(): ?string {
return this._thread;
}
}
{ {
"name": "react-native-notifications", "name": "react-native-notifications",
"version": "1.1.23", "version": "3.0.0-beta.1",
"description": "Advanced Push Notifications (Silent, interactive notifications) for iOS & Android", "description": "Advanced Push Notifications (Silent, interactive notifications) for iOS & Android",
"author": "Lidan Hifi <lidan.hifi@gmail.com>", "author": "Lidan Hifi <lidan.hifi@gmail.com>",
"license": "MIT", "license": "MIT",
...@@ -19,31 +19,56 @@ ...@@ -19,31 +19,56 @@
"actionable-notifications", "actionable-notifications",
"interactive-notifications" "interactive-notifications"
], ],
"nativePackage": true, "main": "lib/dist/index.js",
"dependencies": { "typings": "lib/dist/index.d.ts",
"core-js": "^1.0.0", "scripts": {
"uuid": "^2.0.3" "build": "rm -rf ./lib/dist && tsc",
"prestart": "npm run build",
"pretest-js": "npm run build",
"pretest-unit-ios": "npm run build",
"pretest-unit-android": "npm run build",
"test": "node scripts/test",
"start": "node ./scripts/start",
"pretest-e2e-ios-release": "npm run build",
"clean": "node ./scripts/clean",
"test-e2e-ios": "node ./scripts/test-e2e --ios",
"test-e2e-ios-release": "node ./scripts/test-e2e --ios --release",
"test-unit-ios": "node ./scripts/test-unit --ios",
"test-unit-android": "node ./scripts/test-unit --android",
"test-js": "node ./scripts/test-js",
"xcode": "open example/ios/NotificationsExampleApp.xcodeproj",
"androidStudio": "open -a /Applications/Android\\ Studio.app ./example/android",
"prerelease": "npm run build",
"release": "node ./scripts/release",
"generate-changelog": "gren changelog"
}, },
"nativePackage": true,
"peerDependencies": { "peerDependencies": {
"react": ">=0.14.5", "react": ">=0.14.5",
"react-native": ">=0.25.1" "react-native": ">=0.25.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "16.x.x",
"@types/react-native": "0.57.7",
"@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", "babel-eslint": "9.0.0",
"babel-preset-react-native": "^1.9.0", "tslint": "5.x.x",
"babel-register": "^6.7.2", "ts-mockito": "^2.3.1",
"chai": "^3.5.0",
"chokidar-cli": "^1.2.0",
"eslint": "5.1.x",
"mocha": "^2.5.3", "mocha": "^2.5.3",
"proxyquire": "^1.7.4", "shell-utils": "1.x.x",
"sinon": "^1.17.3", "react-native": "0.60.5",
"sinon-chai": "^2.8.0" "react": "16.8.6",
}, "detox": "14.x.x",
"scripts": { "jsc-android": "236355.x.x",
"pretest": "./node_modules/.bin/eslint *.js test", "jest": "24.8.0",
"test": "./node_modules/.bin/mocha --compilers js:babel-register --reporter spec \"test/*.spec.js\"", "metro-react-native-babel-preset": "0.55.x",
"start": "npm run test --silent; ./node_modules/.bin/chokidar \"test/*.js\" \"*.js\" -c 'npm run test --silent' --silent" "react-native-typescript-transformer": "1.2.12",
"github-release-notes": "0.17.0"
}, },
"publishConfig": { "publishConfig": {
"registry": "https://registry.npmjs.org/" "registry": "https://registry.npmjs.org/"
...@@ -56,9 +81,63 @@ ...@@ -56,9 +81,63 @@
"bugs": { "bugs": {
"url": "https://github.com/wix/react-native-notifications/issues" "url": "https://github.com/wix/react-native-notifications/issues"
}, },
"babel": { "detox": {
"presets": [ "test-runner": "jest",
"react-native" "specs": "",
"configurations": {
"ios.none": {
"binaryPath": "playground/ios/DerivedData/playground/Build/Products/Debug-iphonesimulator/playground.app",
"type": "ios.none",
"name": "iPhone 11",
"session": {
"server": "ws://localhost:8099",
"sessionId": "playground"
}
},
"ios.sim.debug": {
"binaryPath": "example/ios/DerivedData/NotificationsExampleApp/Build/Products/Debug-iphonesimulator/NotificationsExampleApp.app",
"build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme NotificationsExampleApp -project example/ios/NotificationsExampleApp.xcodeproj -sdk iphonesimulator -configuration Debug -derivedDataPath example/ios/DerivedData/NotificationsExampleApp ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
"type": "ios.simulator",
"name": "iPhone 11"
},
"ios.sim.release": {
"binaryPath": "example/ios/DerivedData/NotificationsExampleApp/Build/Products/Release-iphonesimulator/NotificationsExampleApp.app",
"build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme NotificationsExampleApp_release -project example/ios/NotificationsExampleApp.xcodeproj -sdk iphonesimulator -configuration Release -derivedDataPath example/ios/DerivedData/NotificationsExampleApp ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
"type": "ios.simulator",
"name": "iPhone 11"
}
}
},
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
},
"roots": [
"<rootDir>/node_modules/",
"<rootDir>/lib/dist/"
],
"collectCoverageFrom": [
"lib/dist/**/*.js",
"lib/src/**/*.js",
"integration/**/*.js",
"!lib/dist/index.js",
"!lib/dist/Notifications.js",
"!lib/dist/NotificationsIOS.js",
"!lib/dist/NotificationsAndroid.js",
"!lib/dist/adapters/**/*",
"!lib/dist/interfaces/**/*",
"!lib/dist/**/*.test.*",
"!integration/**/*.test.*",
"!integration/*.test.*"
],
"resetMocks": true,
"resetModules": true,
"coverageReporters": [
"json",
"lcov",
"text",
"html"
] ]
} }
} }
\ No newline at end of file
const exec = require('shell-utils').exec;
run();
function run() {
exec.killPort(8081);
exec.execSync(`watchman watch-del-all || true`);
exec.execSync(`adb reverse tcp:8081 tcp:8081 || true`);
exec.execSync(`rm -rf lib/ios/DerivedData`);
exec.execSync(`rm -rf example/ios/DerivedData`);
exec.execSync(`rm -rf lib/android/build`);
exec.execSync(`rm -rf lib/android/app/build`);
exec.execSync(`rm -rf example/android/build`);
exec.execSync(`rm -rf example/android/app/build`);
exec.execSync(`rm -rf lib/dist`);
}
#!/bin/bash -e
echo node -v $(node -v)
$(dirname "$0")/install.sh
if [ -z "$ZZ_BITRISE" ]; then
ANDROID_API=28
ANDROID_HOME="/usr/local/share/android-sdk"
echo "export ANDROID_API=${ANDROID_API}" >> $BASH_ENV
echo "export ANDROID_HOME=${ANDROID_HOME}" >> $BASH_ENV
cat $BASH_ENV
# jdk
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew untap adoptopenjdk/openjdk
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew cask install adoptopenjdk8
export PATH="$PATH:$ANDROID_HOME/tools/bin"
# set variables
ANDROID_SDK_URL="https://dl.google.com/android/repository/sdk-tools-darwin-4333796.zip"
TMP_DIR=$(mktemp -d)
# download and install sdk
cd "$TMP_DIR"
curl "$ANDROID_SDK_URL" > "sdk.zip"
unzip "sdk.zip" -d "$ANDROID_HOME"
# install what you need
yes | sdkmanager --licenses || :
echo y | sdkmanager "platforms;android-${ANDROID_API}"
echo y | sdkmanager "platform-tools"
echo y | sdkmanager "build-tools;28.0.3"
echo y | sdkmanager "extras;android;m2repository"
echo y | sdkmanager "extras;google;m2repository"
echo y | sdkmanager "system-images;android-${ANDROID_API};google_apis;x86_64"
echo y | sdkmanager "extras;intel;Hardware_Accelerated_Execution_Manager"
echo y | sdkmanager "extras;google;google_play_services"
echo no | avdmanager create avd --force --name Nexus_5X_API_${ANDROID_API} --abi x86_64 --device "Nexus 5X" -k "system-images;android-${ANDROID_API};google_apis;x86_64"
fi
\ No newline at end of file
#!/bin/bash -e
$(dirname "$0")/install.sh
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew install ruby
export PATH="/usr/local/opt/ruby/bin:$PATH"
gem install xcpretty
export CODE_SIGNING_REQUIRED=NO
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew
HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils
echo 'export PATH=$PATH' >> $BASH_ENV
\ No newline at end of file
#!/bin/bash -e
npm install -g react-native-cli
npm install -g detox-cli
git submodule update --init --recursive
/* tslint:disable: no-console */
const exec = require('shell-utils').exec;
const semver = require('semver');
const fs = require('fs');
const _ = require('lodash');
const grenrc = require('../.grenrc');
// Workaround JS
const isRelease = process.env.RELEASE_BUILD === 'true';
const BRANCH = process.env.BRANCH;
const VERSION_TAG = isRelease ? 'latest' : 'snapshot';
const VERSION_INC = 'patch';
function run() {
if (!validateEnv()) {
return;
}
setupGit();
createNpmRc();
versionTagAndPublish();
}
function validateEnv() {
if (!process.env.JENKINS_CI) {
throw new Error(`releasing is only available from CI`);
}
if (!process.env.JENKINS_MASTER) {
console.log(`not publishing on a different build`);
return false;
}
return true;
}
function setupGit() {
exec.execSyncSilent(`git config --global push.default simple`);
exec.execSyncSilent(`git config --global user.email "${process.env.GIT_EMAIL}"`);
exec.execSyncSilent(`git config --global user.name "${process.env.GIT_USER}"`);
const remoteUrl = new RegExp(`https?://(\\S+)`).exec(exec.execSyncRead(`git remote -v`))[1];
exec.execSyncSilent(`git remote add deploy "https://${process.env.GIT_USER}:${process.env.GIT_TOKEN}@${remoteUrl}"`);
// exec.execSync(`git checkout ${ONLY_ON_BRANCH}`);
}
function createNpmRc() {
exec.execSync(`rm -f package-lock.json`);
const content = `
email=\${NPM_EMAIL}
//registry.npmjs.org/:_authToken=\${NPM_TOKEN}
`;
fs.writeFileSync(`.npmrc`, content);
}
function versionTagAndPublish() {
const packageVersion = semver.clean(process.env.npm_package_version);
console.log(`package version: ${packageVersion}`);
const currentPublished = findCurrentPublishedVersion();
console.log(`current published version: ${currentPublished}`);
const version = isRelease
? process.env.VERSION
: semver.gt(packageVersion, currentPublished)
? `${packageVersion}-snapshot.${process.env.BUILD_ID}`
: `${currentPublished}-snapshot.${process.env.BUILD_ID}`;
console.log(`Publishing version: ${version}`);
tryPublishAndTag(version);
}
function findCurrentPublishedVersion() {
return exec.execSyncRead(`npm view ${process.env.npm_package_name} dist-tags.latest`);
}
function tryPublishAndTag(version) {
let theCandidate = version;
for (let retry = 0; retry < 5; retry++) {
try {
tagAndPublish(theCandidate);
console.log(`Released ${theCandidate}`);
return;
} catch (err) {
const alreadyPublished = _.includes(err.toString(), 'You cannot publish over the previously published version');
if (!alreadyPublished) {
throw err;
}
console.log(`previously published. retrying with increased ${VERSION_INC}...`);
theCandidate = semver.inc(theCandidate, VERSION_INC);
}
}
}
function tagAndPublish(newVersion) {
console.log(`trying to publish ${newVersion}...`);
exec.execSync(`npm --no-git-tag-version version ${newVersion}`);
exec.execSync(`npm publish --tag ${VERSION_TAG}`);
exec.execSync(`git tag -a ${newVersion} -m "${newVersion}"`);
exec.execSyncSilent(`git push deploy ${newVersion} || true`);
if (isRelease) {
updateGit(newVersion);
}
}
function getPackageJsonPath() {
return `${process.cwd()}/package.json`;
}
function writePackageJson(packageJson) {
fs.writeFileSync(getPackageJsonPath(), JSON.stringify(packageJson, null, 2));
}
function readPackageJson() {
return JSON.parse(fs.readFileSync(getPackageJsonPath()));
}
function updateGit(version) {
exec.execSync(`git checkout ${BRANCH}`);
updatePackageJson(version);
generateChangelog();
exec.execSync(`git commit -m "Update package.json version to ${version} and generate CHANGELOG.gren.md [ci skip]"`);
exec.execSync(`git push deploy ${BRANCH}`);
}
function updatePackageJson(version) {
const packageJson = readPackageJson();
packageJson.version = version;
writePackageJson(packageJson);
exec.execSync(`git add package.json`);
}
function generateChangelog() {
exec.execSync('npm run generate-changelog');
exec.execSync(`git add ${grenrc.changelogFilename}`);
}
run();
const exec = require('shell-utils').exec;
run();
function run() {
exec.killPort(8081);
exec.execSync(`watchman watch-del-all || true`);
exec.execSync(`adb reverse tcp:8081 tcp:8081 || true`);
exec.execSync(`node ./node_modules/react-native/local-cli/cli.js start`);
}
const _ = require('lodash');
const exec = require('shell-utils').exec;
const android = _.includes(process.argv, '--android');
const release = _.includes(process.argv, '--release');
const skipBuild = _.includes(process.argv, '--skipBuild');
const headless = _.includes(process.argv, '--headless');
const multi = _.includes(process.argv, '--multi');
run();
function run() {
const prefix = android ? `android.emu` : `ios.sim`;
const suffix = release ? `release` : `debug`;
const configuration = `${prefix}.${suffix}`;
const headless$ = android ? headless ? `--headless` : `` : ``;
const workers = multi ? 3 : 1;
if (!skipBuild) {
exec.execSync(`detox build --configuration ${configuration}`);
}
exec.execSync(`detox test --loglevel verbose --configuration ${configuration} ${headless$} ${!android ? `-w ${workers}` : ``}`); //-f "ScreenStyle.test.js" --loglevel trace
}
const exec = require('shell-utils').exec;
const _ = require('lodash');
const fix = _.includes(process.argv, '--fix') ? '--fix' : '';
const dirs = [
'lib',
'scripts',
'example/src'
];
run();
function run() {
const paths = _.chain(dirs).map((d) => d === 'e2e' ? `${d}/**/*.[tj]s` : `${d}/**/*.[tj]sx?`).join(' ').value();
exec.execSync(`tslint ${paths} ${fix} --format verbose`);
assertAllTsFilesInSrc();
exec.execSync(`jest --coverage`);
}
function assertAllTsFilesInSrc() {
const allFiles = exec.execSyncRead('find ./lib/src -type f');
const lines = _.split(allFiles, '\n');
const offenders = _.filter(lines, (f) => !f.endsWith('.ts') && !f.endsWith('.tsx'));
if (offenders.length) {
throw new Error(`\n\nOnly ts/tsx files are allowed:\n${offenders.join('\n')}\n\n\n`);
}
}
const _ = require('lodash');
const exec = require('shell-utils').exec;
const android = _.includes(process.argv, '--android');
const release = _.includes(process.argv, '--release');
function run() {
if (android) {
runAndroidUnitTests();
} else {
runIosUnitTests();
}
}
function runAndroidUnitTests() {
const conf = release ? 'testReactNative60ReleaseUnitTest' : 'testReactNative60DebugUnitTest';
if (android && process.env.JENKINS_CI) {
const sdkmanager = '/usr/local/share/android-sdk/tools/bin/sdkmanager';
exec.execSync(`yes | ${sdkmanager} --licenses`);
// exec.execSync(`echo y | ${sdkmanager} --update && echo y | ${sdkmanager} --licenses`);
}
exec.execSync(`cd lib/android && ./gradlew ${conf}`);
}
function runIosUnitTests() {
const conf = release ? `Release` : `Debug`;
exec.execSync(`cd ./example/ios &&
RCT_NO_LAUNCH_PACKAGER=true
xcodebuild build build-for-testing
-scheme "NotificationsExampleApp"
-project NotificationsExampleApp.xcodeproj
-sdk iphonesimulator
-configuration ${conf}
-derivedDataPath ./example/ios/DerivedData/NotificationsExampleApp
-quiet
-UseModernBuildSystem=NO
ONLY_ACTIVE_ARCH=YES`);
exec.execSync(`cd ./example/ios &&
RCT_NO_LAUNCH_PACKAGER=true
xcodebuild test-without-building
-scheme "NotificationsExampleApp"
-project NotificationsExampleApp.xcodeproj
-sdk iphonesimulator
-configuration ${conf}
-destination 'platform=iOS Simulator,name=iPhone 11'
-derivedDataPath ./example/ios/DerivedData/NotificationsExampleApp
ONLY_ACTIVE_ARCH=YES`);
}
run();
const exec = require('shell-utils').exec;
run();
function run() {
exec.execSync(`npm run test-js`);
exec.execSync(`npm run test-unit-ios`);
exec.execSync(`npm run test-unit-android`);
}
"use strict";
let expect = require("chai").use(require("sinon-chai")).expect;
import proxyquire from "proxyquire";
import sinon from "sinon";
describe("Notifications-Android > ", () => {
proxyquire.noCallThru();
let refreshTokenStub;
let getInitialNotificationStub;
let postLocalNotificationStub;
let cancelLocalNotificationStub;
let deviceEventEmitterListenerStub;
let libUnderTest;
beforeEach(() => {
refreshTokenStub = sinon.stub();
getInitialNotificationStub = sinon.stub();
postLocalNotificationStub = sinon.stub();
cancelLocalNotificationStub = sinon.stub();
deviceEventEmitterListenerStub = sinon.stub();
libUnderTest = proxyquire("../index.android", {
"react-native": {
NativeModules: {
WixRNNotifications: {
refreshToken: refreshTokenStub,
getInitialNotification: getInitialNotificationStub,
postLocalNotification: postLocalNotificationStub,
cancelLocalNotification: cancelLocalNotificationStub
}
},
DeviceEventEmitter: {
addListener: deviceEventEmitterListenerStub
}
},
"./notification": require("../notification.android")
});
});
describe("Registration token API", () => {
it("should assign callback to native event upon listener registration", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListener = () => {};
libUnderTest.NotificationsAndroid.setRegistrationTokenUpdateListener(userListener);
expect(deviceEventEmitterListenerStub).to.have.been.calledWith("remoteNotificationsRegistered", userListener);
expect(deviceEventEmitterListenerStub).to.have.been.calledOnce;
});
it("should clear native event listener upon listener deregister", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListener = () => {};
const nativeListener = {
remove: sinon.spy()
};
deviceEventEmitterListenerStub.returns(nativeListener);
libUnderTest.NotificationsAndroid.setRegistrationTokenUpdateListener(userListener);
libUnderTest.NotificationsAndroid.clearRegistrationTokenUpdateListener();
expect(nativeListener.remove).to.have.been.calledOnce;
});
it("shouldn't fail if deregister without registering", () => {
libUnderTest.NotificationsAndroid.clearRegistrationTokenUpdateListener();
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
});
});
describe("notification-opening API", () => {
it("should assign callback to native event upon registration", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListenerStub = sinon.stub();
libUnderTest.NotificationsAndroid.setNotificationOpenedListener(userListenerStub);
expect(deviceEventEmitterListenerStub).to.have.been.calledOnce;
expect(deviceEventEmitterListenerStub).to.have.been.calledWith("notificationOpened", sinon.match.func);
});
it("should assign a wrapper-callback upon registration", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListenerStub = sinon.stub();
const notification = { foo: "bar" };
libUnderTest.NotificationsAndroid.setNotificationOpenedListener(userListenerStub);
expect(userListenerStub).to.not.have.been.called;
deviceEventEmitterListenerStub.args[0][1](notification);
expect(userListenerStub).to.have.been.calledOnce;
expect(userListenerStub.args[0][0].getData()).to.equal(notification);
});
it("should clear native event listener upon listener deregister", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListener = () => {};
const nativeListener = {
remove: sinon.spy()
};
deviceEventEmitterListenerStub.returns(nativeListener);
libUnderTest.NotificationsAndroid.setNotificationOpenedListener(userListener);
libUnderTest.NotificationsAndroid.clearNotificationOpenedListener();
expect(nativeListener.remove).to.have.been.calledOnce;
});
it("shouldn't fail if deregister without registering", () => {
libUnderTest.NotificationsAndroid.clearNotificationOpenedListener();
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
});
});
describe("notification-receive API", () => {
it("should assign callback to native event upon registration", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListenerStub = sinon.stub();
libUnderTest.NotificationsAndroid.setNotificationReceivedListener(userListenerStub);
expect(deviceEventEmitterListenerStub).to.have.been.calledOnce;
expect(deviceEventEmitterListenerStub).to.have.been.calledWith("notificationReceived", sinon.match.func);
});
it("should assign a wrapper-callback upon registration", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListenerStub = sinon.stub();
const notification = { foo: "bar" };
libUnderTest.NotificationsAndroid.setNotificationReceivedListener(userListenerStub);
expect(userListenerStub).to.not.have.been.called;
deviceEventEmitterListenerStub.args[0][1](notification);
expect(userListenerStub).to.have.been.calledOnce;
expect(userListenerStub.args[0][0].getData()).to.equal(notification);
});
it("should clear native event listener upon listener deregister", () => {
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
const userListener = () => {};
const nativeListener = {
remove: sinon.spy()
};
deviceEventEmitterListenerStub.returns(nativeListener);
libUnderTest.NotificationsAndroid.setNotificationReceivedListener(userListener);
libUnderTest.NotificationsAndroid.clearNotificationReceivedListener();
expect(nativeListener.remove).to.have.been.calledOnce;
});
it("shouldn't fail if deregister without registering", () => {
libUnderTest.NotificationsAndroid.clearNotificationReceivedListener();
expect(deviceEventEmitterListenerStub).to.not.have.been.called;
});
});
describe("Notification token", () => {
it("should refresh notification token upon refreshing request by the user", () => {
expect(refreshTokenStub).to.not.have.been.called;
libUnderTest.NotificationsAndroid.refreshToken();
expect(refreshTokenStub).to.have.been.calledOnce;
});
});
describe("Initial notification API", () => {
it("should return initial notification data if available", (done) => {
expect(getInitialNotificationStub).to.not.have.been.called;
const rawNotification = {foo: "bar"};
getInitialNotificationStub.returns(Promise.resolve(rawNotification));
libUnderTest.PendingNotifications.getInitialNotification()
.then((notification) => {
expect(notification.getData()).to.equal(rawNotification);
done();
})
.catch((err) => done(err));
});
it("should return empty notification if not available", (done) => {
expect(getInitialNotificationStub).to.not.have.been.called;
getInitialNotificationStub.returns(Promise.resolve(null));
libUnderTest.PendingNotifications.getInitialNotification()
.then((notification) => {
expect(notification).to.be.undefined;
done();
})
.catch((err) => done(err));
});
});
describe("Local notification", () => {
const notification = {
title: "notification-title",
body: "notification-body"
};
it("should get published when posted manually", () => {
expect(postLocalNotificationStub).to.not.have.been.called;
const id = libUnderTest.NotificationsAndroid.localNotification(notification);
expect(id).to.not.be.undefined;
expect(postLocalNotificationStub).to.have.been.calledWith(notification, id);
});
it("should be called with a unique ID", () => {
expect(postLocalNotificationStub).to.not.have.been.called;
const id = libUnderTest.NotificationsAndroid.localNotification(notification);
const id2 = libUnderTest.NotificationsAndroid.localNotification(notification);
expect(id).to.not.be.undefined;
expect(id2).to.not.be.undefined;
expect(id).to.not.equal(id2);
});
it("should be cancellable with an ID", () => {
expect(cancelLocalNotificationStub).to.not.have.been.called;
libUnderTest.NotificationsAndroid.cancelLocalNotification(666);
expect(cancelLocalNotificationStub).to.have.been.calledWith(666);
});
});
});
"use strict";
let expect = require("chai").use(require("sinon-chai")).expect;
import proxyquire from "proxyquire";
import sinon from "sinon";
/* eslint-disable no-unused-vars */
describe("NotificationsIOS", () => {
let deviceEvents = [
"pushKitRegistered",
"remoteNotificationsRegistered",
"remoteNotificationsRegistrationFailed",
"notificationReceivedForeground",
"notificationReceivedBackground",
"notificationOpened"
];
/*eslint-disable indent*/
let deviceAddEventListener,
deviceRemoveEventListener,
nativeAppAddEventListener,
nativeAppRemoveEventListener,
nativeRequestPermissionsWithCategories,
nativeAbandonPermissions,
nativeRegisterPushKit,
nativeBackgroundTimeRemaining,
nativeConsumeBackgroundQueue,
nativeLocalNotification,
nativeCancelLocalNotification,
nativeCancelAllLocalNotifications,
nativeGetBadgesCount,
nativeSetBadgesCount,
nativeIsRegisteredForRemoteNotifications,
nativeCheckPermissions,
nativeRemoveAllDeliveredNotifications,
nativeRemoveDeliveredNotifications,
nativeGetDeliveredNotifications;
let NotificationsIOS, NotificationAction, NotificationCategory;
let someHandler = () => {};
let constantGuid = "some-random-uuid";
let identifiers = ["some-random-uuid", "other-random-uuid"];
/*eslint-enable indent*/
before(() => {
deviceAddEventListener = sinon.spy();
deviceRemoveEventListener = sinon.spy();
nativeAppAddEventListener = sinon.spy();
nativeAppRemoveEventListener = sinon.spy();
nativeRequestPermissionsWithCategories = sinon.spy();
nativeAbandonPermissions = sinon.spy();
nativeRegisterPushKit = sinon.spy();
nativeBackgroundTimeRemaining = sinon.spy();
nativeConsumeBackgroundQueue = sinon.spy();
nativeLocalNotification = sinon.spy();
nativeCancelLocalNotification = sinon.spy();
nativeCancelAllLocalNotifications = sinon.spy();
nativeGetBadgesCount = sinon.spy();
nativeSetBadgesCount = sinon.spy();
nativeIsRegisteredForRemoteNotifications = sinon.spy();
nativeCheckPermissions = sinon.spy();
nativeRemoveAllDeliveredNotifications = sinon.spy();
nativeRemoveDeliveredNotifications = sinon.spy();
nativeGetDeliveredNotifications = sinon.spy();
let libUnderTest = proxyquire("../index.ios", {
"uuid": {
v4: () => constantGuid
},
"react-native": {
NativeModules: {
RNNotifications: {
requestPermissionsWithCategories: nativeRequestPermissionsWithCategories,
abandonPermissions: nativeAbandonPermissions,
registerPushKit: nativeRegisterPushKit,
backgroundTimeRemaining: nativeBackgroundTimeRemaining,
consumeBackgroundQueue: nativeConsumeBackgroundQueue,
localNotification: nativeLocalNotification,
cancelLocalNotification: nativeCancelLocalNotification,
cancelAllLocalNotifications: nativeCancelAllLocalNotifications,
getBadgesCount: nativeGetBadgesCount,
setBadgesCount: nativeSetBadgesCount,
isRegisteredForRemoteNotifications: nativeIsRegisteredForRemoteNotifications,
checkPermissions: nativeCheckPermissions,
removeAllDeliveredNotifications: nativeRemoveAllDeliveredNotifications,
removeDeliveredNotifications: nativeRemoveDeliveredNotifications,
getDeliveredNotifications: nativeGetDeliveredNotifications
}
},
NativeAppEventEmitter: {
addListener: (...args) => {
nativeAppAddEventListener(...args);
return { remove: nativeAppRemoveEventListener };
}
},
DeviceEventEmitter: {
addListener: (...args) => {
deviceAddEventListener(...args);
return { remove: deviceRemoveEventListener };
}
},
"@noCallThru": true
}
});
NotificationsIOS = libUnderTest.default;
NotificationAction = libUnderTest.NotificationAction;
NotificationCategory = libUnderTest.NotificationCategory;
});
afterEach(() => {
deviceAddEventListener.reset();
deviceRemoveEventListener.reset();
nativeAppAddEventListener.reset();
nativeAppRemoveEventListener.reset();
nativeRequestPermissionsWithCategories.reset();
nativeAbandonPermissions.reset();
nativeRegisterPushKit.reset();
nativeBackgroundTimeRemaining.reset();
nativeConsumeBackgroundQueue.reset();
nativeLocalNotification.reset();
nativeCancelLocalNotification.reset();
nativeCancelAllLocalNotifications.reset();
nativeIsRegisteredForRemoteNotifications.reset();
nativeCheckPermissions.reset();
nativeRemoveAllDeliveredNotifications.reset();
nativeRemoveDeliveredNotifications.reset();
nativeGetDeliveredNotifications.reset();
});
after(() => {
deviceAddEventListener = null;
deviceRemoveEventListener = null;
nativeAppAddEventListener = null;
nativeAppRemoveEventListener = null;
nativeRequestPermissionsWithCategories = null;
nativeAbandonPermissions = null;
nativeRegisterPushKit = null;
nativeBackgroundTimeRemaining = null;
nativeConsumeBackgroundQueue = null;
nativeLocalNotification = null;
nativeCancelLocalNotification = null;
nativeCancelAllLocalNotifications = null;
nativeIsRegisteredForRemoteNotifications = null;
nativeCheckPermissions = null;
nativeRemoveAllDeliveredNotifications = null;
nativeRemoveDeliveredNotifications = null;
nativeGetDeliveredNotifications = null;
NotificationsIOS = null;
NotificationAction = null;
NotificationCategory = null;
});
describe("Add Event Listener", () => {
deviceEvents.forEach(event => {
it(`should subscribe the given handler to device event: ${event}`, () => {
NotificationsIOS.addEventListener(event, someHandler);
expect(deviceAddEventListener).to.have.been.calledWith(event, sinon.match.func);
});
});
it("should not subscribe to unknown device events", () => {
NotificationsIOS.addEventListener("someUnsupportedEvent", someHandler);
expect(deviceAddEventListener).to.not.have.been.called;
});
});
describe("Remove Event Listener", () => {
deviceEvents.forEach(event => {
it(`should unsubscribe the given handler from device event: ${event}`, () => {
NotificationsIOS.addEventListener(event, someHandler);
NotificationsIOS.removeEventListener(event, someHandler);
expect(deviceRemoveEventListener).to.have.been.calledOnce;
});
});
it("should not unsubscribe to unknown device events", () => {
let someUnsupportedEvent = "someUnsupportedEvent";
NotificationsIOS.addEventListener(someUnsupportedEvent, someHandler);
NotificationsIOS.removeEventListener(someUnsupportedEvent, someHandler);
expect(deviceRemoveEventListener).to.not.have.been.called;
});
});
describe("Notification actions handling", () => {
let someAction, someCategory;
let actionOpts = {
activationMode: "foreground",
title: "someAction",
behavior: "default",
identifier: "SOME_ACTION"
};
beforeEach(() => {
someAction = new NotificationAction(actionOpts, () => {});
someCategory = new NotificationCategory({
identifier: "SOME_CATEGORY",
actions: [someAction],
context: "default"
});
});
describe("register push notifications", () => {
it("should call native request permissions with array of categories", () => {
NotificationsIOS.requestPermissions([someCategory]);
expect(nativeRequestPermissionsWithCategories).to.have.been.calledWith([{
identifier: "SOME_CATEGORY",
actions: [actionOpts],
context: "default"
}]);
});
it("should call native request permissions with empty array if no categories specified", () => {
NotificationsIOS.requestPermissions();
expect(nativeRequestPermissionsWithCategories).to.have.been.calledWith([]);
});
it("should subscribe to 'notificationActionReceived' event once, with a single event handler", () => {
NotificationsIOS.requestPermissions([someCategory]);
expect(nativeAppAddEventListener).to.have.been.calledOnce;
expect(nativeAppAddEventListener).to.have.been.calledWith("notificationActionReceived", sinon.match.func);
});
});
describe("reset categories", () => {
it("should remove 'notificationActionReceived' event handler", () => {
NotificationsIOS.resetCategories();
expect(nativeAppRemoveEventListener).to.have.been.calledOnce;
});
});
describe("get badges count", () => {
it("should call native getBadgesCount", () => {
const callback = (count) => console.log(count);
NotificationsIOS.getBadgesCount(callback);
expect(nativeGetBadgesCount).to.have.been.calledWith(callback);
});
});
describe("set badges count", () => {
it("should call native setBadgesCount", () => {
NotificationsIOS.setBadgesCount(44);
expect(nativeSetBadgesCount).to.have.been.calledWith(44);
});
});
});
describe("register push kit for background notifications", function () {
it("should call native register push kit method", function () {
NotificationsIOS.registerPushKit();
expect(nativeRegisterPushKit).to.have.been.called;
});
});
describe("Abandon push notifications permissions", () => {
it("should call native abandon permissions method", () => {
NotificationsIOS.abandonPermissions();
expect(nativeAbandonPermissions).to.have.been.called;
});
});
describe("Get background remaining time", () => {
it("should call native background remaining time method", () => {
let someCallback = (time) => { };
NotificationsIOS.backgroundTimeRemaining(someCallback);
expect(nativeBackgroundTimeRemaining).to.have.been.calledWith(someCallback);
});
});
describe("Consume background queue which holds background notificiations and actions until js thread is ready", () => {
it("should call native consume background queue method", () => {
NotificationsIOS.consumeBackgroundQueue();
expect(nativeConsumeBackgroundQueue).to.have.been.called;
});
});
describe("Dispatch local notification", () => {
it("should return generated notification guid", () => {
expect(NotificationsIOS.localNotification({})).to.equal(constantGuid);
});
it("should call native local notification method with generated notification guid and notification object", () => {
let someLocalNotification = {
alertBody: "some body",
alertTitle: "some title",
alertAction: "some action",
soundName: "sound",
category: "SOME_CATEGORY",
userInfo: {
"key": "value"
}
};
NotificationsIOS.localNotification(someLocalNotification);
expect(nativeLocalNotification).to.have.been.calledWith(someLocalNotification, constantGuid);
});
});
describe("Cancel local notification", () => {
it("should call native cancel local notification method", () => {
NotificationsIOS.cancelLocalNotification(constantGuid);
expect(nativeCancelLocalNotification).to.have.been.calledWith(constantGuid);
});
});
describe("Cancel all local notifications", () => {
it("should call native cancel all local notifications method", () => {
NotificationsIOS.cancelAllLocalNotifications();
expect(nativeCancelAllLocalNotifications).to.have.been.calledWith();
});
});
describe("Is registered for remote notifications ", () => {
it("should call native is registered for remote notifications", () => {
NotificationsIOS.isRegisteredForRemoteNotifications();
expect(nativeIsRegisteredForRemoteNotifications).to.have.been.calledWith();
});
});
describe("Check permissions ", () => {
it("should call native check permissions", () => {
NotificationsIOS.checkPermissions();
expect(nativeCheckPermissions).to.have.been.calledWith();
});
});
describe("Remove all delivered notifications", () => {
it("should call native remove all delivered notifications method", () => {
NotificationsIOS.removeAllDeliveredNotifications();
expect(nativeRemoveAllDeliveredNotifications).to.have.been.calledWith();
});
});
describe("Remove delivered notifications", () => {
it("should call native remove delivered notifications method", () => {
NotificationsIOS.removeDeliveredNotifications(identifiers);
expect(nativeRemoveDeliveredNotifications).to.have.been.calledWith(identifiers);
});
});
describe("Get delivered notifications", () => {
it("should call native get delivered notifications method", () => {
const callback = (notifications) => console.log(notifications);
NotificationsIOS.getDeliveredNotifications(callback);
expect(nativeGetDeliveredNotifications).to.have.been.calledWith(callback);
});
});
});
"use strict";
import { expect } from "chai";
import IOSNotification from "../notification.ios";
describe("iOS Notification Object", () => {
let notification;
let someBadgeCount = 123, someSound = "someSound", someCategory = "some_notification_category", someThread = "thread-1";
describe("for a regular iOS push notification", () => {
let regularNativeNotifications = [
// basic example, without content-available = 1 (aka silent notification)
{
aps: {
alert: {
title: "some title",
body: "some body"
},
badge: someBadgeCount,
sound: someSound,
category: someCategory,
"thread-id": someThread
},
key1: "value1",
key2: "value2"
},
// another example, with content-available but also with alert object (should not be a silent notification)
{
aps: {
"content-available": 1,
alert: {
title: "some title",
body: "some body"
},
badge: someBadgeCount,
sound: someSound,
category: someCategory,
"thread-id": someThread
},
key1: "value1",
key2: "value2"
}
];
regularNativeNotifications.forEach(nativeNotification => {
beforeEach(() => {
notification = new IOSNotification(nativeNotification);
});
it("should return 'regular' type", function () {
expect(notification.getType()).to.equal("regular");
});
it("should return the alert object", () => {
expect(notification.getMessage()).to.deep.equal(nativeNotification.aps.alert);
});
it("should return the sound", () => {
expect(notification.getSound()).to.equal(someSound);
});
it("should return the badge count", () => {
expect(notification.getBadgeCount()).to.equal(someBadgeCount);
});
it("should return the category", () => {
expect(notification.getCategory()).to.equal(someCategory);
});
it("should return the thread", () => {
expect(notification.getThread()).to.equal("thread-1");
});
it("should return the custom data", () => {
expect(notification.getData()).to.deep.equal({ key1: "value1", key2: "value2" });
});
});
});
describe("for a managed iOS push notification (silent notification, with managedAps key and content-available = 1)", () => {
let managedNativeNotification = {
aps: {
"content-available": 1,
badge: someBadgeCount
},
managedAps: {
action: "CREATE",
notificationId: "1",
alert: {
title: "some title",
body: "some body"
},
sound: someSound,
category: someCategory
},
key1: "value1",
key2: "value2"
};
beforeEach(() => {
notification = new IOSNotification(managedNativeNotification);
});
it("should return 'managed' type", function () {
expect(notification.getType()).to.equal("managed");
});
it("should return the alert object", () => {
expect(notification.getMessage()).to.equal(managedNativeNotification.managedAps.alert);
});
it("should return the sound", () => {
expect(notification.getSound()).to.equal(someSound);
});
it("should return the badge count", () => {
expect(notification.getBadgeCount()).to.equal(someBadgeCount);
});
it("should return the category", () => {
expect(notification.getCategory()).to.equal(someCategory);
});
it("should return the custom data", () => {
expect(notification.getData()).to.deep.equal({ managedAps: managedNativeNotification.managedAps, key1: "value1", key2: "value2" });
});
});
});
{
"$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
}
}
{
"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/**/*"
]
}
const babelOptions = require('./babel.config')();
module.exports = function (wallaby) {
return {
env: {
type: 'node',
runner: 'node'
},
testFramework: 'jest',
files: [
'package.json',
'lib/src/**/*.js',
'lib/src/**/*.ts',
'lib/src/**/*.tsx',
'!lib/src/Notifications.ts',
'!lib/src/**/*.test.tsx',
'!lib/src/**/*.test.js',
'!lib/src/**/*.test.ts',
'integration/**/*.js',
'!integration/**/*.test.js'
],
tests: [
'lib/src/**/*.test.js',
'lib/src/**/*.test.ts',
'lib/src/**/*.test.tsx',
'integration/**/*.test.js'
],
compilers: {
'**/*.js': wallaby.compilers.babel(babelOptions),
'**/*.ts?(x)': wallaby.compilers.typeScript({
module: 'commonjs',
jsx: 'React'
})
},
setup: (w) => {
w.testFramework.configure(require('./package.json').jest);
}
};
};
This website was created with [Docusaurus](https://docusaurus.io/).
# What's In This Document
* [Get Started in 5 Minutes](#get-started-in-5-minutes)
* [Directory Structure](#directory-structure)
* [Editing Content](#editing-content)
* [Adding Content](#adding-content)
* [Full Documentation](#full-documentation)
# Get Started in 5 Minutes
1. Make sure all the dependencies for the website are installed:
```sh
# Install dependencies
$ yarn
```
2. Run your dev server:
```sh
# Start the site
$ yarn start
```
## Directory Structure
Your project file structure should look something like this
```
my-docusaurus/
docs/
doc-1.md
doc-2.md
doc-3.md
website/
blog/
2016-3-11-oldest-post.md
2017-10-24-newest-post.md
core/
node_modules/
pages/
static/
css/
img/
package.json
sidebar.json
siteConfig.js
```
# Editing Content
## Editing an existing docs page
Edit docs by navigating to `docs/` and editing the corresponding document:
`docs/doc-to-be-edited.md`
```markdown
---
id: page-needs-edit
title: This Doc Needs To Be Edited
---
Edit me...
```
For more information about docs, click [here](https://docusaurus.io/docs/en/navigation)
## Editing an existing blog post
Edit blog posts by navigating to `website/blog` and editing the corresponding post:
`website/blog/post-to-be-edited.md`
```markdown
---
id: post-needs-edit
title: This Blog Post Needs To Be Edited
---
Edit me...
```
For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog)
# Adding Content
## Adding a new docs page to an existing sidebar
1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`:
```md
---
id: newly-created-doc
title: This Doc Needs To Be Edited
---
My new content here..
```
1. Refer to that doc's ID in an existing sidebar in `website/sidebar.json`:
```javascript
// Add newly-created-doc to the Getting Started category of docs
{
"docs": {
"Getting Started": [
"quick-start",
"newly-created-doc" // new doc here
],
...
},
...
}
```
For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation)
## Adding a new blog post
1. Make sure there is a header link to your blog in `website/siteConfig.js`:
`website/siteConfig.js`
```javascript
headerLinks: [
...
{ blog: true, label: 'Blog' },
...
]
```
2. Create the blog post with the format `YYYY-MM-DD-My-Blog-Post-Title.md` in `website/blog`:
`website/blog/2018-05-21-New-Blog-Post.md`
```markdown
---
author: Frank Li
authorURL: https://twitter.com/foobarbaz
authorFBID: 503283835
title: New Blog Post
---
Lorem Ipsum...
```
For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog)
## Adding items to your site's top navigation bar
1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`:
`website/siteConfig.js`
```javascript
{
headerLinks: [
...
/* you can add docs */
{ doc: 'my-examples', label: 'Examples' },
/* you can add custom pages */
{ page: 'help', label: 'Help' },
/* you can add external links */
{ href: 'https://github.com/facebook/Docusaurus', label: 'GitHub' },
...
],
...
}
```
For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation)
## Adding custom pages
1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`:
1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element:
`website/siteConfig.js`
```javascript
{
headerLinks: [
...
{ page: 'my-new-custom-page', label: 'My New Custom Page' },
...
],
...
}
```
For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages).
# Full Documentation
Full documentation can be found on the [website](https://docusaurus.io/).
---
title: Blog Title
author: Blog Author
authorURL: http://twitter.com/
authorFBID: 100002976521003
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien.
<!--truncate-->
Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut.
Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum.
Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis.
---
title: New Blog Post
author: Blog Author
authorURL: http://twitter.com/
authorFBID: 100002976521003
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien.
<!--truncate-->
Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut.
Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum.
Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis.
---
title: Adding RSS Support - RSS Truncation Test
author: Eric Nakagawa
authorURL: http://twitter.com/ericnakagawa
authorFBID: 661277173
---
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
This should be truncated.
<!--truncate-->
This line should never render in XML.
---
title: Adding RSS Support
author: Eric Nakagawa
authorURL: http://twitter.com/ericnakagawa
authorFBID: 661277173
---
This is a test post.
A whole bunch of other information.
---
title: New Version 1.0.0
author: Eric Nakagawa
authorURL: http://twitter.com/ericnakagawa
authorFBID: 661277173
---
This blog post will test file name parsing issues when periods are present.
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
class Footer extends React.Component {
docUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
const docsUrl = this.props.config.docsUrl;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
return `${baseUrl}${docsPart}${langPart}${doc}`;
}
pageUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
return baseUrl + (language ? `${language}/` : '') + doc;
}
render() {
return (
<footer className="nav-footer" id="footer">
<section className="sitemap">
<a href={this.props.config.baseUrl} className="nav-home">
{this.props.config.footerIcon && (
<img
src={this.props.config.baseUrl + this.props.config.footerIcon}
alt={this.props.config.title}
width="66"
height="58"
/>
)}
</a>
<div>
<h5>Docs</h5>
<a href={this.docUrl('getting-started')}>
Getting Started (or other categories)
</a>
<a href={this.docUrl('installation-ios')}>
Guides
</a>
<a href={this.docUrl('general-api')}>
API Reference
</a>
</div>
<div>
{/* <h5>Community</h5> */}
{/* <a href={this.pageUrl('users.html', this.props.language)}>
User Showcase
</a>
<a
href="https://stackoverflow.com/questions/tagged/"
target="_blank"
rel="noreferrer noopener">
Stack Overflow
</a> */}
{/* <a href="https://discordapp.com/">Project Chat</a>
<a
href="https://twitter.com/"
target="_blank"
rel="noreferrer noopener">
Twitter
</a> */}
</div>
<div>
<h5>More</h5>
<a href={`${this.props.config.baseUrl}blog`}>Blog</a>
<a href="https://github.com/wix/react-native-notifications">GitHub</a>
<a
className="github-button"
href={this.props.config.repoUrl}
data-icon="octicon-star"
data-count-href="/facebook/docusaurus/stargazers"
data-show-count="true"
data-count-aria-label="# stargazers on GitHub"
aria-label="Star this project on GitHub">
Star
</a>
{this.props.config.twitterUsername && (
<div className="social">
<a
href={`https://twitter.com/${
this.props.config.twitterUsername
}`}
className="twitter-follow-button">
Follow @{this.props.config.twitterUsername}
</a>
</div>
)}
{this.props.config.facebookAppId && (
<div className="social">
<div
className="fb-like"
data-href={this.props.config.url}
data-colorscheme="dark"
data-layout="standard"
data-share="true"
data-width="225"
data-show-faces="false"
/>
</div>
)}
</div>
</section>
<section className="copyright">{this.props.config.copyright}</section>
</footer>
);
}
}
module.exports = Footer;
{
"_comment": "This file is auto-generated by write-translations.js",
"localized-strings": {
"next": "Next",
"previous": "Previous",
"tagline": "Documentation",
"docs": {
"advanced-ios": {
"title": "iOS Advanced API",
"sidebar_label": "iOS"
},
"android-api": {
"title": "Android Specific Commands",
"sidebar_label": "Android specific"
},
"general-api": {
"title": "General Commands",
"sidebar_label": "General"
},
"general-events": {
"title": "General",
"sidebar_label": "General"
},
"installation-android": {
"title": "Android Installation",
"sidebar_label": "Android Installation"
},
"installation-ios": {
"title": "iOS Installation",
"sidebar_label": "iOS Installation"
},
"ios-api": {
"title": "iOS Specific Commands",
"sidebar_label": "iOS specific"
},
"ios-events": {
"title": "iOS",
"sidebar_label": "iOS specific"
},
"localNotifications": {
"title": "Local Notifications",
"sidebar_label": "Local Notifications"
},
"notification-object": {
"title": "Notification object",
"sidebar_label": "Notification"
},
"notifications-events": {
"title": "Handling Notification Events",
"sidebar_label": "Events"
},
"subscription": {
"title": "Push Notifications Subscription",
"sidebar_label": "Subscription"
}
},
"links": {
"Docs": "Docs",
"API": "API"
},
"categories": {
"Installation": "Installation",
"Guides": "Guides",
"Advanced": "Advanced",
"Commands": "Commands",
"Events": "Events",
"Objects": "Objects"
}
},
"pages-strings": {
"Help Translate|recruit community translators for your project": "Help Translate",
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
}
}
{
"scripts": {
"examples": "docusaurus-examples",
"start": "docusaurus-start",
"build": "docusaurus-build",
"publish-gh-pages": "docusaurus-publish",
"write-translations": "docusaurus-write-translations",
"version": "docusaurus-version",
"rename-version": "docusaurus-rename-version"
},
"devDependencies": {
"docusaurus": "^1.12.0"
}
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
function Help(props) {
const {config: siteConfig, language = ''} = props;
const {baseUrl, docsUrl} = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const supportLinks = [
{
content: `Learn more using the [documentation on this site.](${docUrl(
'doc1.html',
)})`,
title: 'Browse Docs',
},
{
content: 'Ask questions about the documentation and project',
title: 'Join the community',
},
{
content: "Find out what's new with this project",
title: 'Stay up to date',
},
];
return (
<div className="docMainWrapper wrapper">
<Container className="mainContainer documentContainer postContainer">
<div className="post">
<header className="postHeader">
<h1>Need help?</h1>
</header>
<p>This project is maintained by a dedicated group of people.</p>
<GridBlock contents={supportLinks} layout="threeColumn" />
</div>
</Container>
</div>
);
}
module.exports = Help;
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
class HomeSplash extends React.Component {
render() {
const {siteConfig, language = ''} = this.props;
const {baseUrl, docsUrl} = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const SplashContainer = props => (
<div className="homeContainer">
<div className="homeSplashFade">
<div className="wrapper homeWrapper">{props.children}</div>
</div>
</div>
);
const Logo = props => (
<div className="projectLogo">
<img src={props.img_src} alt="Project Logo" />
</div>
);
const ProjectTitle = () => (
<h2 className="projectTitle">
{siteConfig.title}
<small>{siteConfig.tagline}</small>
</h2>
);
const PromoSection = props => (
<div className="section promoSection">
<div className="promoRow">
<div className="pluginRowBlock">{props.children}</div>
</div>
</div>
);
const Button = props => (
<div className="pluginWrapper buttonWrapper">
<a className="button" href={props.href} target={props.target}>
{props.children}
</a>
</div>
);
return (
<SplashContainer>
<Logo img_src={`${baseUrl}img/undraw_monitor.svg`} />
<div className="inner">
<ProjectTitle siteConfig={siteConfig} />
<PromoSection>
<Button href="#try">Try It Out</Button>
<Button href={docUrl('doc1.html')}>Example Link</Button>
<Button href={docUrl('doc2.html')}>Example Link 2</Button>
</PromoSection>
</div>
</SplashContainer>
);
}
}
class Index extends React.Component {
render() {
const {config: siteConfig, language = ''} = this.props;
const {baseUrl} = siteConfig;
const Block = props => (
<Container
padding={['bottom', 'top']}
id={props.id}
background={props.background}>
<GridBlock
align="center"
contents={props.children}
layout={props.layout}
/>
</Container>
);
const FeatureCallout = () => (
<div
className="productShowcaseSection paddingBottom"
style={{textAlign: 'center'}}>
<h2>Feature Callout</h2>
<MarkdownBlock>These are features of this project</MarkdownBlock>
</div>
);
const TryOut = () => (
<Block id="try">
{[
{
content:
'To make your landing page more attractive, use illustrations! Check out ' +
'[**unDraw**](https://undraw.co/) which provides you with customizable illustrations which are free to use. ' +
'The illustrations you see on this page are from unDraw.',
image: `${baseUrl}img/undraw_code_review.svg`,
imageAlign: 'left',
title: 'Wonderful SVG Illustrations',
},
]}
</Block>
);
const Description = () => (
<Block background="dark">
{[
{
content:
'This is another description of how this project is useful',
image: `${baseUrl}img/undraw_note_list.svg`,
imageAlign: 'right',
title: 'Description',
},
]}
</Block>
);
const LearnHow = () => (
<Block background="light">
{[
{
content:
'Each new Docusaurus project has **randomly-generated** theme colors.',
image: `${baseUrl}img/undraw_youtube_tutorial.svg`,
imageAlign: 'right',
title: 'Randomly Generated Theme Colors',
},
]}
</Block>
);
const Features = () => (
<Block layout="fourColumn">
{[
{
content: 'This is the content of my feature',
image: `${baseUrl}img/undraw_react.svg`,
imageAlign: 'top',
title: 'Feature One',
},
{
content: 'The content of my second feature',
image: `${baseUrl}img/undraw_operating_system.svg`,
imageAlign: 'top',
title: 'Feature Two',
},
]}
</Block>
);
const Showcase = () => {
if ((siteConfig.users || []).length === 0) {
return null;
}
const showcase = siteConfig.users
.filter(user => user.pinned)
.map(user => (
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
));
const pageUrl = page => baseUrl + (language ? `${language}/` : '') + page;
return (
<div className="productShowcaseSection paddingBottom">
<h2>Who is Using This?</h2>
<p>This project is used by all these people</p>
<div className="logos">{showcase}</div>
<div className="more-users">
<a className="button" href={pageUrl('users.html')}>
More {siteConfig.title} Users
</a>
</div>
</div>
);
};
return (
<div>
<HomeSplash siteConfig={siteConfig} language={language} />
<div className="mainContainer">
{/* <Features /> */}
{/* <FeatureCallout /> */}
{/* <LearnHow /> */}
{/* <TryOut /> */}
{/* <Description /> */}
{/* <Showcase /> */}
</div>
</div>
);
}
}
module.exports = Index;
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
class Users extends React.Component {
render() {
const {config: siteConfig} = this.props;
if ((siteConfig.users || []).length === 0) {
return null;
}
const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`;
const showcase = siteConfig.users.map(user => (
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
));
return (
<div className="mainContainer">
<Container padding={['bottom', 'top']}>
<div className="showcaseSection">
<div className="prose">
<h1>Who is Using This?</h1>
<p>This project is used by many folks</p>
</div>
<div className="logos">{showcase}</div>
<p>Are you using this project?</p>
<a href={editUrl} className="button">
Add your company
</a>
</div>
</Container>
</div>
);
}
}
module.exports = Users;
{
"docs": {
"Installation": ["installation-ios", "installation-android"],
"Guides": ["subscription", "notifications-events", "localNotifications"],
"Advanced": ["advanced-ios"]
},
"api": {
"Commands": ["general-api", "ios-api", "android-api"],
"Events": ["general-events", "ios-events"],
"Objects": ["notification-object"]
}
}
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// See https://docusaurus.io/docs/site-config for all the possible
// site configuration options.
// List of projects/orgs using your project for the users page.
const users = [
// {
// caption: 'User1',
// // You will need to prepend the image path with your baseUrl
// // if it is not '/', like: '/test-site/img/image.jpg'.
// image: '/img/undraw_open_source.svg',
// infoLink: 'https://www.facebook.com',
// pinned: true,
// },
];
const siteConfig = {
title: 'React native notifications', // Title for your website.
tagline: 'Documentation',
url: 'https://wix.github.io', // Your website URL
baseUrl: '/react-native-notifications/', // Base URL for your project */
// For github.io type URLs, you would set the url and baseUrl like:
// url: 'https://facebook.github.io',
// baseUrl: '/test-site/',
// Used for publishing and more
projectName: 'react-native-notifications',
organizationName: 'wix',
// For top-level user or org sites, the organization is still the same.
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
// organizationName: 'JoelMarcey'
// For no header links in the top nav bar -> headerLinks: [],
headerLinks: [
{doc: 'installation-ios', label: 'Docs'},
{doc: 'general-api', label: 'API'},
// {page: 'help', label: 'Help'},
// {blog: true, label: 'Blog'},
],
// If you have users set above, you add it here:
users,
/* path to images for header/footer */
// headerIcon: 'img/favicon.ico',
// footerIcon: 'img/favicon.ico',
// favicon: 'img/favicon.ico',
/* Colors for website */
colors: {
primaryColor: '#6a00ff',
secondaryColor: '#645f16',
},
/* Custom fonts for website */
/*
fonts: {
myFont: [
"Times New Roman",
"Serif"
],
myOtherFont: [
"-apple-system",
"system-ui"
]
},
*/
// This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
copyright: `Copyright © ${new Date().getFullYear()} Wix`,
highlight: {
// Highlight.js theme to use for syntax highlighting in code blocks.
theme: 'default',
},
// Add custom scripts here that would be placed in <script> tags.
scripts: ['https://buttons.github.io/buttons.js'],
// On page navigation for the current documentation page.
onPageNav: 'separate',
// No .html extensions for paths.
cleanUrl: true,
// Open Graph and Twitter card images.
ogImage: 'img/undraw_online.svg',
twitterImage: 'img/undraw_tweetstorm.svg',
// For sites with a sizable amount of content, set collapsible to true.
// Expand/collapse the links and subcategories under categories.
// docsSideNavCollapsible: true,
// Show documentation's last contributor's name.
// enableUpdateBy: true,
// Show documentation's last update time.
// enableUpdateTime: true,
// You may provide arbitrary config keys to be used as needed by your
// template. For example, if you need your repo's URL...
repoUrl: 'https://github.com/wix/react-native-notifications',
};
module.exports = siteConfig;
/* your custom css */
@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
}
@media only screen and (min-width: 1024px) {
}
@media only screen and (max-width: 1023px) {
}
@media only screen and (min-width: 1400px) {
}
@media only screen and (min-width: 1500px) {
}
\ No newline at end of file
<svg id="a594ac37-6d44-4297-8862-1cbd9c01c0b7" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1126.19355" height="855" viewBox="0 0 1126.19355 855"><title>code review</title><path d="M581.7675,778.43794C339.36866,759.99137,50.29677,572.17166,65.01385,378.78054S359.70017,5.35846,602.099,23.805,984.03089,403.667,969.31381,597.05809,824.16633,796.88451,581.7675,778.43794Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><ellipse cx="565.19355" cy="756" rx="506" ry="31" fill="#3f3d56"/><ellipse cx="565.19355" cy="755.5" rx="431" ry="20.5" opacity="0.1"/><ellipse cx="212.19355" cy="836.5" rx="212.19355" ry="18.5" fill="#3f3d56"/><ellipse cx="212.19355" cy="836.20161" rx="180.74194" ry="12.23387" opacity="0.1"/><rect x="196.19355" y="162" width="752" height="590" fill="#3f3d56"/><path d="M600.54135,265.12914H415.02363a2.25143,2.25143,0,1,1,0-4.50285H600.54135a2.25143,2.25143,0,0,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M803.6202,289.89486H511.83506a2.25143,2.25143,0,0,1,0-4.50286H803.6202a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M601.44192,314.66057H415.9242a2.25143,2.25143,0,1,1,0-4.50286H601.44192a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M803.6202,315.11086H618.10249a2.25143,2.25143,0,0,1,0-4.50286H803.6202a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M601.8922,339.42629H416.37449a2.25143,2.25143,0,0,1,0-4.50286H601.8922a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M602.34249,364.192H416.82477a2.25143,2.25143,0,0,1,0-4.50286H602.34249a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M686.54592,265.57943H633.4122a2.25143,2.25143,0,0,1,0-4.50286h53.13372a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M686.54592,339.42629H633.4122a2.25143,2.25143,0,0,1,0-4.50286h53.13372a2.25143,2.25143,0,1,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M686.54592,363.74171H633.4122a2.25143,2.25143,0,1,1,0-4.50285h53.13372a2.25143,2.25143,0,1,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M775.70249,363.74171H722.56877a2.25143,2.25143,0,0,1,0-4.50285h53.13372a2.25143,2.25143,0,1,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M467.70706,289.89486H414.57335a2.25143,2.25143,0,0,1,0-4.50286h53.13371a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M600.76649,629.86057H415.24877a2.25143,2.25143,0,0,1,0-4.50286H600.76649a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M599.86592,679.392H414.3482a2.25143,2.25143,0,1,1,0-4.50286H599.86592a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M600.3162,704.15771H414.79849a2.25143,2.25143,0,1,1,0-4.50285H600.3162a2.25143,2.25143,0,1,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M679.56649,679.84229H626.43277a2.25143,2.25143,0,0,1,0-4.50286h53.13372a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M679.56649,704.15771H626.43277a2.25142,2.25142,0,0,1,0-4.50285h53.13372a2.25142,2.25142,0,1,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M768.72306,679.84229H715.58935a2.25143,2.25143,0,1,1,0-4.50286h53.13371a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M803.84535,654.62629H512.0602a2.25143,2.25143,0,0,1,0-4.50286H803.84535a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M467.9322,654.62629H414.79849a2.25143,2.25143,0,0,1,0-4.50286H467.9322a2.25143,2.25143,0,0,1,0,4.50286Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M803.84535,729.37371H512.0602a2.25143,2.25143,0,1,1,0-4.50285H803.84535a2.25142,2.25142,0,0,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><path d="M467.9322,729.37371H414.79849a2.25143,2.25143,0,1,1,0-4.50285H467.9322a2.25143,2.25143,0,1,1,0,4.50285Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><polygon points="519.06 401.13 447.014 472.275 519.06 543.42 535.27 527.21 480.786 472.725 535.72 417.79 519.06 401.13" fill="#908820"/><polygon points="625.327 401.13 697.373 472.275 625.327 543.42 609.117 527.21 663.602 472.725 608.667 417.79 625.327 401.13" fill="#908820"/><circle cx="216.19355" cy="177" r="9" fill="#908820"/><circle cx="240.19355" cy="177" r="9" fill="#908820"/><circle cx="264.19355" cy="177" r="9" fill="#908820"/><rect x="196.19355" y="192" width="752" height="3" opacity="0.1"/><path d="M1163.09677,629.95357c0,63.77108-37.91089,86.03756-84.67635,86.03756s-84.67635-22.26648-84.67635-86.03756,84.67635-144.898,84.67635-144.898S1163.09677,566.18249,1163.09677,629.95357Z" transform="translate(-36.90323 -22.5)" fill="#f2f2f2"/><polygon points="1038.433 683.736 1039.3 630.365 1075.391 564.338 1039.436 621.993 1039.826 597.995 1064.699 550.226 1039.929 591.645 1039.929 591.645 1040.63 548.484 1067.265 510.453 1040.74 541.697 1041.178 462.556 1038.425 567.325 1038.651 563.003 1011.571 521.552 1038.217 571.3 1035.694 619.503 1035.619 618.223 1004.4 574.602 1035.524 622.743 1035.209 628.771 1035.152 628.862 1035.178 629.357 1028.776 751.653 1037.329 751.653 1038.355 688.485 1069.404 640.461 1038.433 683.736" fill="#3f3d56"/><path d="M159.52481,773.59144a43.12568,43.12568,0,0,1-8.17805,3.50253c-5.07883,1.38739-10.50718.8784-15.63716,2.06274-2.10034.4849-4.38629,1.49916-5.05424,3.54866-.48995,1.50331.04306,3.12668.55347,4.62317,5.66852,16.61991,9.08515,34.26625,18.12518,49.32052a74.64075,74.64075,0,0,0,13.99269,16.98766c2.05512,1.85228,4.38405,3.6643,7.13541,3.95491a7.24769,7.24769,0,0,0,7.37055-10.182c-.81056-1.8011-2.31727-3.19619-3.289-4.91569a18.757,18.757,0,0,1-1.81948-6.99071c-.89312-7.10831-1.7535-14.53764.70942-21.26517,1.6519-4.5122,4.68207-8.35894,7.20641-12.4475s4.62171-8.7719,4.06-13.544c-.38332-3.25659-1.97189-6.23685-3.65-9.054-1.68146-2.8227-5.00472-10.40837-8.38287-11.35035C169.16047,766.86441,162.583,772.02411,159.52481,773.59144Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M251.95981,831.00007a14.01882,14.01882,0,0,1-.66321,4.74531c-1.05756,2.79253-3.681,4.70608-5.03631,7.36685-1.885,3.70061-1.03063,8.17372.21823,12.13454.78295,2.48316,1.8099,5.067,3.91246,6.60271a12.89278,12.89278,0,0,0,5.95383,1.90719c18.03627,2.49619,36.35793,1.064,54.5586,1.587,3.31731.09532,6.82944.21147,9.72521-1.40969s4.686-5.6968,2.71651-8.36789c-1.76854-2.39854-5.26537-2.37621-8.24208-2.51756a33.912,33.912,0,0,1-20.913-8.61479,15.11172,15.11172,0,0,1-2.93335-3.37658,20.75292,20.75292,0,0,1-1.53436-3.51757,156.79876,156.79876,0,0,0-9.08354-20.11857c-.97877-1.81492-2.087-3.705-3.906-4.67631a10.32271,10.32271,0,0,0-4.23932-.92554c-4.53631-.294-16.28319-1.83785-19.791,1.68963C249.46137,816.76814,252.03167,826.86031,251.95981,831.00007Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><circle cx="242.9708" cy="376.72033" r="28.97496" fill="#fbbebe"/><path d="M253.709,423.14584c-1.72342,4.6139-4.64671,8.94711-8.90039,11.42995l40.0746,8.17466a42.45319,42.45319,0,0,1-.56181-17.869,10.30012,10.30012,0,0,0,.23975-4.409c-.75354-2.90746-4.02646-4.30693-6.95489-4.97449-7.31406-1.66731-13.7821-2.56141-20.57892-5.79173C254.9498,413.26279,255.24069,419.04525,253.709,423.14584Z" transform="translate(-36.90323 -22.5)" fill="#fbbebe"/><path d="M244.47385,423.72685a31.33845,31.33845,0,0,1,27.31312.71368c5.13776,2.70749,9.39191,6.79432,13.57151,10.82645,3.85264,3.71671,7.96244,7.97238,8.23588,13.31859.13245,2.58993-.67591,5.12286-1.08406,7.68385a25.76566,25.76566,0,0,0,13.78,26.46946c3.1836,5.30339,3.84065,11.7287,3.88948,17.91408.185,23.43592-7.20915,47.16094-1.93041,69.99538,1.23937,5.36117,3.16161,10.55391,4.2323,15.95129,1.38681,6.991,1.31835,14.17959,2.04391,21.26977a95.26625,95.26625,0,0,0,5.11322,22.49547c-1.744,2.7737-5.37357,3.83728-8.64228,3.61264s-6.3252-1.64311-9.24191-3.13567c-14.12327-7.22727-27.05918-16.75729-41.687-22.89955-9.67764-4.06367-19.98746-6.59488-29.37589-11.28823s-18.11988-12.14194-20.99049-22.238c-1.24124-4.36546-1.31765-8.96508-1.3854-13.50306-.28534-19.11376-.55046-38.43617,3.61-57.09382,3.669-16.45362,10.74145-32.1938,12.28438-48.98077.50223-5.46424.51295-11.31378,3.56594-15.87332C232.09135,432.52045,241.40863,430.85143,244.47385,423.72685Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M263.3948,399.62932c1.70735-2.22683,5.00024-2.53051,7.72786-1.87181s5.24265,2.06449,7.99738,2.59858a4.67544,4.67544,0,0,0,3.74325-.52575,5.44429,5.44429,0,0,0,1.61547-3.57028,68.16318,68.16318,0,0,0,.67089-17.38048,5.14652,5.14652,0,0,1,.15877-2.43744c.682-1.6434,2.7627-2.03456,4.46209-2.56176a13.31574,13.31574,0,0,0,9.01289-13.63308,5.42087,5.42087,0,0,0-1.6567-3.74055c-1.17523-.95765-2.82014-1.01649-4.33609-1.02867q-11.68307-.09382-23.36661.00565c-2.88278.02455-5.91029.09552-8.40937,1.53276-1.84769,1.06263-3.22171,2.773-4.88046,4.11154-4.32621,3.49111-10.17785,4.20145-15.44116,5.99071a15.30822,15.30822,0,0,0-6.76753,4.03565c-1.75035,1.99965-2.5919,4.97311-1.49261,7.39258a26.25205,26.25205,0,0,0,1.88685,2.9039c3.10463,4.96416,1.03778,11.40306,1.50139,17.23972.57892,7.28838,11.36495,19.11157,18.67879,20.83626C266.30761,422.31124,259.486,404.41359,263.3948,399.62932Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M247.0375,719.1826c.74512,5.32168,1.7007,10.66172,1.44525,16.02924-.35664,7.49365-3.05912,14.71381-3.55872,22.19929-.37315,5.59084.48989,11.24214-.28652,16.79137-.49715,3.55325-1.65585,6.97849-2.37315,10.49392a43.39491,43.39491,0,0,0,6.97307,33.26545,5.44788,5.44788,0,0,0,2.02339,1.961,5.7137,5.7137,0,0,0,2.50788.3318,179.9472,179.9472,0,0,0,25.17023-2.28749,2.41432,2.41432,0,0,0,1.61622-.71311,2.26567,2.26567,0,0,0,.351-.954c1.54379-7.936-1.97676-15.89332-2.872-23.92837-.95593-8.57968,1.10271-17.17733,3.14612-25.56477a113.54767,113.54767,0,0,1,3.46738-12.22773c.9877-2.69735,2.18054-5.32086,3.0366-8.06284a61.46035,61.46035,0,0,0,2.202-13.20261q.89161-10.57524.94476-21.19892a88.95779,88.95779,0,0,0-1.66511-20.01c-1.878-8.338-5.7238-16.08066-9.53591-23.73028a15.02852,15.02852,0,0,0-5.61784,2.67008,125.37991,125.37991,0,0,1-15.35,8.48057c-3.56655,1.65676-10.52195,2.83342-12.78554,6.28577-2.23228,3.40461-.792,10.35082-.62145,14.10572Q245.69419,709.58425,247.0375,719.1826Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M247.0375,719.1826c.74512,5.32168,1.7007,10.66172,1.44525,16.02924-.35664,7.49365-3.05912,14.71381-3.55872,22.19929-.37315,5.59084.48989,11.24214-.28652,16.79137-.49715,3.55325-1.65585,6.97849-2.37315,10.49392a43.39491,43.39491,0,0,0,6.97307,33.26545,5.44788,5.44788,0,0,0,2.02339,1.961,5.7137,5.7137,0,0,0,2.50788.3318,179.9472,179.9472,0,0,0,25.17023-2.28749,2.41432,2.41432,0,0,0,1.61622-.71311,2.26567,2.26567,0,0,0,.351-.954c1.54379-7.936-1.97676-15.89332-2.872-23.92837-.95593-8.57968,1.10271-17.17733,3.14612-25.56477a113.54767,113.54767,0,0,1,3.46738-12.22773c.9877-2.69735,2.18054-5.32086,3.0366-8.06284a61.46035,61.46035,0,0,0,2.202-13.20261q.89161-10.57524.94476-21.19892a88.95779,88.95779,0,0,0-1.66511-20.01c-1.878-8.338-5.7238-16.08066-9.53591-23.73028a15.02852,15.02852,0,0,0-5.61784,2.67008,125.37991,125.37991,0,0,1-15.35,8.48057c-3.56655,1.65676-10.52195,2.83342-12.78554,6.28577-2.23228,3.40461-.792,10.35082-.62145,14.10572Q245.69419,709.58425,247.0375,719.1826Z" transform="translate(-36.90323 -22.5)" opacity="0.1"/><path d="M207.86438,605.518c-3.84776,10.35941,1.80816,21.55375,6.722,31.45209,12.106,24.38615,20.51267,50.40745,28.87447,76.31727.62635,1.9408,1.2522,4.05818.55393,5.97428a7.82535,7.82535,0,0,1-2.82176,3.347c-5.76549,4.37915-13.10677,5.93873-19.97473,8.2298a90.18354,90.18354,0,0,0-18.94265,8.87995c-4.97678,3.08387-9.63891,6.64386-14.29189,10.1975l-14.812,11.31248a9.903,9.903,0,0,0-2.47578,2.39658c-1.4286,2.24131-.84344,5.16734-.13218,7.72829a131.43212,131.43212,0,0,0,7.25213,19.792,6.911,6.911,0,0,0,2.63,3.44664c2.31193,1.24241,5.13971-.405,6.90515-2.34714s3.29242-4.35894,5.78108-5.1927c2.48574-.83278,5.2216.17573,7.81767-.18871,4.19867-.58943,6.98621-4.45724,10.01273-7.42649,12.36533-12.13133,33.01009-11.25124,46.61244-21.97723a59.22781,59.22781,0,0,1,6.231-4.86753c2.29954-1.35929,4.90732-2.10533,7.29316-3.30673,7.25663-3.65411,11.81865-11.20854,14.26244-18.957s3.14074-15.92786,4.89686-23.86053c.90786-4.10092,2.10291-8.15769,2.50676-12.33845a66.09392,66.09392,0,0,0-.25308-11.6661L287.604,621.74782c-.3996-4.94331-.79987-9.89239-1.57574-14.79076-14.57664.5211-29.14284,1.10754-43.72778,1.28C230.69491,608.37423,218.87609,609.18554,207.86438,605.518Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M202.50573,619.721c1.97737,3.18011,6.79016,3.11442,10.10427,1.37092s5.8888-4.67259,9.21071-6.40118c4.46387-2.32282,9.74611-2.23547,14.77617-2.0934,7.96357.22492,16.02491.46432,23.68692,2.64671,7.92486,2.25727,15.63969,6.60332,23.834,5.7363,1.4259-.15087,2.98505-.55979,3.76106-1.76551a5.54448,5.54448,0,0,0,.61966-2.83582l.26794-7.159a6.46407,6.46407,0,0,0-.57-3.62533,6.08162,6.08162,0,0,0-2.71588-2.07944c-11.66338-5.64612-24.96458-6.69742-37.91511-7.14107q-9.87458-.33828-19.75748-.27827c-5.591.03409-12.303-.77486-17.73873.71906C202.77416,598.81994,198.8047,613.76883,202.50573,619.721Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/><path d="M275.74249,432.22556c-15.28217-.13537-29.131-10.22251-44.4118-10.46824a11.51447,11.51447,0,0,0-5.39263.93046c-2.12932,1.07263-3.52928,3.14668-5.14679,4.89829-4.51828,4.89285-11.0612,7.50531-15.703,12.28109-4.35234,4.47793-6.77287,11.14957-5.05456,17.15307s8.20353,10.60849,14.279,9.16523A36.80081,36.80081,0,0,1,228.64955,446.706a72.88717,72.88717,0,0,1,11.20533-5.88209c6.86792-3.11167,14.54778-6.28282,21.71787-3.95031a30.63793,30.63793,0,0,1,7.40918,4.04034l11.95388,8.03C279.54293,443.44994,278.29044,437.28837,275.74249,432.22556Z" transform="translate(-36.90323 -22.5)" fill="#908820"/><path d="M212.55243,460.87394c-2.0293,2.53158.894,6.19965.44656,9.41317-.29726,2.13512-2.05431,3.76539-2.786,5.79313-.75791,2.10039-.356,4.427-.64344,6.64132a28.48866,28.48866,0,0,1-1.2751,4.72964,84.97452,84.97452,0,0,0-2.68638,14.02574l-2.6749,19.97555a75.68111,75.68111,0,0,0-.846,9.07789,27.10744,27.10744,0,0,1-.45211,6.78677c-.59232,2.183-1.90386,4.09275-2.78616,6.17547s-1.29127,4.602-.09937,6.52435c-3.99144,3.32456-5.17826,9.85376-2.38732,14.235a7.89943,7.89943,0,0,1,1.23013,2.26518,5.81983,5.81983,0,0,1-.54176,3.34034l-2.7047,7.20665c-.69232,1.84469-.91128,4.65314,1.01352,5.07422a17.00617,17.00617,0,0,0-3.65133,5.76681,6.86617,6.86617,0,0,0,.97247,6.50559,8.28879,8.28879,0,0,1,1.69441,2.0563,5.47266,5.47266,0,0,1-.19586,3.24315,11.90573,11.90573,0,0,0,1.473,9.33345,8.37146,8.37146,0,0,0,8.41846,3.60852c5.41045-1.22031,10.27226-3.1467,15.72755-2.14571,11.746,2.15529,23.52979,4.31989,34.90618,7.952,12.984,4.1454,25.55606,10.22443,39.12586,11.50045a3.79783,3.79783,0,0,0,2.61662-.407,3.93069,3.93069,0,0,0,1.21-2.48763q2.097-9.83241,4.19405-19.66481a202.36327,202.36327,0,0,0,3.85525-22.02765c.47742-4.78125.61351-9.58923.7493-14.39234l1.08084-38.23106a38.19731,38.19731,0,0,0-2.2-16.61961l-6.21748-19.8483c-3.54813-11.32685-11.68773-21.37125-14.58576-32.8816-1.83417-7.285-3.18913-15.10292-8.10789-20.781-7.01237-8.09492-19.1447-9.354-29.76373-7.96222-4.79727.62876-9.63561,1.71382-13.78522,4.20176-4.79825,2.87682-8.33308,7.40861-11.75286,11.8363C218.30489,454.34281,215.43623,457.27636,212.55243,460.87394Z" transform="translate(-36.90323 -22.5)" fill="#908820"/><path d="M237.34735,573.32348c.87491,3.77256,2.71233,7.25107,3.837,10.95684,1.88437,6.20878,1.72443,12.89561,3.60511,19.10551.78937,2.60646,1.93305,5.109,2.49961,7.77276,1.32091,6.21057-.60773,12.6356-3.0035,18.51576s-5.31347,11.678-6.0912,17.97962c-.54778,4.43849.23913,9.45012,3.66018,12.33051,3.55169,2.99038,8.71308,2.78787,13.33894,2.39a14.01584,14.01584,0,0,0,4.84121-1.00807c3.53766-1.67361,5.02767-5.85984,5.85461-9.685,2.77128-12.81912,1.97957-26.10621,1.17422-39.19671l-2.10613-34.2342c-.40547-6.59081-.83988-13.31809-2.87081-19.64644-.73878-2.302-2.02646-6.95382-4.12917-8.42371-2.0826-1.45584-8.02994-1.52068-10.60853-1.64177C235.25108,547.97044,235.3867,564.856,237.34735,573.32348Z" transform="translate(-36.90323 -22.5)" fill="#fbbebe"/><path d="M250.75137,446.9235c-2.47072-1.29382-5.58357-.736-7.97179.70446a24.56772,24.56772,0,0,0-5.9719,5.79818c-5.33892,6.51687-10.99072,13.99464-10.19889,22.38194.29689,3.14472,1.501,6.12087,2.31737,9.17226,3.57944,13.37964-.37874,27.53015.07542,41.37287.26243,7.99873,2.00293,15.86638,3.7363,23.67945.35375,1.59451.92034,3.44035,2.46284,3.97727,1.15873.40334,2.41033-.11521,3.55715-.55127A28.371,28.371,0,0,1,264.721,556.731c2.865-1.52869,1.79145-6.36261,1.14913-9.54579-1.90842-9.45767.68218-19.21438,3.82837-28.3353s6.91058-18.23032,7.43185-27.86453c.59061-10.91607-3.02875-21.57071-6.59987-31.903-.77948-2.25528-1.63134-4.6202-3.43861-6.17831a13.66523,13.66523,0,0,0-5.20292-2.377C258.09582,449.40594,254.25539,448.75842,250.75137,446.9235Z" transform="translate(-36.90323 -22.5)" fill="#2f2e41"/></svg>
<svg id="ad7c1fe1-eb22-473c-bc85-8b4fe073a50d" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1094.72" height="788.69" viewBox="0 0 1094.72 788.69"><defs><linearGradient id="f137b292-0aa2-40b3-a442-9a256144b6d8" x1="2938.83" y1="215.4" x2="2938.83" y2="-91.93" gradientTransform="matrix(0.51, 0.86, -0.86, 0.51, -713.07, -2373.14)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient><linearGradient id="ab747304-1bb2-44ed-8687-72ce94e318d2" x1="1364.21" y1="820.42" x2="1364.21" y2="492.03" gradientTransform="matrix(-1, -0.04, -0.04, 1, 2349.24, 29.65)" xlink:href="#f137b292-0aa2-40b3-a442-9a256144b6d8"/></defs><title>monitor</title><g opacity="0.5"><rect x="960.72" y="367.69" width="134" height="134" rx="14.12" fill="#d0d2d5"/><path d="M1061.36,461.85h38s32,28,0,57h-38S1029.36,492.85,1061.36,461.85Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><rect x="1012.72" y="422.19" width="5" height="15" fill="#3f3d56"/><rect x="1037.72" y="420.19" width="5" height="18" fill="#3f3d56"/><path d="M1027.72,446.19h0a6,6,0,0,1,6,6v5a0,0,0,0,1,0,0h-12a0,0,0,0,1,0,0v-5A6,6,0,0,1,1027.72,446.19Z" fill="#3f3d56"/></g><path d="M674.41,294.11c3.17-.07,6.33.17,9.49,0,2.89-.14,5.76-.61,8.65-.9,4.94-.49,9.92-.45,14.88-.66,3.26-.14,6.7-.46,9.34-2.38,2.48-1.79,3.86-4.71,6.07-6.83,9-8.65,15.17-20.22,23.17-29.82,2-2.44,4.09-4.87,5.9-7.48,3.47-5,5.92-10.67,8.36-16.27a45.11,45.11,0,0,0,2.4-6.42c.45-1.76.68-3.58,1.28-5.3,1.24-3.52,4.22-6.6,4.58-10.31l-.42.06c3.64-4.63,3.08-12.7,4.88-18.63a80.28,80.28,0,0,1,4.17-10.29l6.69-14.57c1.15-2.5,2.35-5.08,4.34-7,2.41-2.33,5.77-3.49,8.19-5.81a12.71,12.71,0,0,0,3.14-5.11l.21.2c1.28,1.25,2.28,2.78,3.53,4.07,2.69,2.78,6.42,4.37,9.16,7.1,1.4,1.39,2.55,3.08,4.24,4.09a4.7,4.7,0,0,0,5.44-.67c4.91-4.32,12.23-4.72,17.54-8.53,1.47-1.06,2.75-2.35,4.13-3.51a56.32,56.32,0,0,1,10.67-6.7,26.61,26.61,0,0,1,6.59.94c3.08.85,6.09,2.26,9.29,2.2a13.4,13.4,0,0,0,7.81-3c1.63-1.26,3.12-3,3.36-5a6.42,6.42,0,0,0-2.4-5.4c-.22-.19-.45-.36-.68-.53A6.68,6.68,0,0,0,876,127a16,16,0,0,0-5.45-2.78,11.69,11.69,0,0,0-3.44-.78,15.7,15.7,0,0,0-5.34,1.25,51.34,51.34,0,0,1-10.47,2.2c-.63.06-1.91-.07-3-.08l-.37-1.06c-4.16,1.92-9,.34-13.56.35-2.22,0-4.43.39-6.64.53a59.92,59.92,0,0,1-7.34-.16L813,126a87.11,87.11,0,0,1-9.42-1c-.83-1.69-1.66-3.38-2.59-5a46.27,46.27,0,0,0-5.49-7.63,26.52,26.52,0,0,1,2.64-2l.18-.11c.23-.13.51-.27.82-.41A18.93,18.93,0,0,0,826.2,92.77a14.5,14.5,0,0,0,1.6-.73c.3-.16.6-.33.89-.52l.43-.29a8.34,8.34,0,0,0,.78-.67c.12-.12.24-.24.35-.37a4.78,4.78,0,0,0,.57-.84l0,0c.06-.12.12-.23.17-.35s0-.09.05-.14.07-.18.1-.26,0-.12.05-.18l.06-.24a1.29,1.29,0,0,0,0-.19c0-.08,0-.16,0-.23l0-.21a1.64,1.64,0,0,1,0-.22,1.7,1.7,0,0,0,0-.22c0-.07,0-.15,0-.22a1.62,1.62,0,0,0,0-.22V86.4a1.79,1.79,0,0,0,0-.23c0-.07,0-.14,0-.21l0-.24,0-.2c0-.09,0-.18,0-.26a1.16,1.16,0,0,0,0-.18,2.33,2.33,0,0,0,0-.28c0-.06,0-.11,0-.16s0-.2-.07-.3l0-.14-.09-.32,0-.12c0-.11-.07-.22-.1-.33l0-.1-.12-.36,0-.06c0-.17-.11-.35-.17-.52-.75-2.16-1.56-4.31-2.42-6.43a15.64,15.64,0,0,0-2.78-4.86c-1.77-1.88-4.31-2.82-6.43-4.32-2.5-1.76-4.37-4.25-6.49-6.47s-4.69-4.23-7.74-4.6A15.16,15.16,0,0,0,797.78,57c-18.37,6.78-33.41,20.14-49.88,30.74-6.41,4.13-13.59,8-21.18,7.47l6.7,10.64c.32.52.65,1,1,1.54s.7,1,1.07,1.5a12.07,12.07,0,0,0,3.51,3.22,7.69,7.69,0,0,0,1.08.52,8.88,8.88,0,0,0,1,.31c2.39.57,5,.09,7.42-.39.83-.16,1.66-.33,2.48-.52l1-.28.16,0c0,.06,0,.11,0,.16-.56,1-1.09,1.91-1.56,2.9a60.81,60.81,0,0,0-3.57,11.12c-1.89,7.5-3.79,15.16-3.29,22.86.17,2.6.61,5.26-.14,7.76-.36,1.23-1,2.49-.66,3.71a17.12,17.12,0,0,1,.92,2.18c.16,1.16-.76,2.16-1.42,3.12s-1,2.49-.06,3.16a5.39,5.39,0,0,0-3.57,4.45,3.79,3.79,0,0,1-.34,1.71,4.23,4.23,0,0,1-1,.93,5.39,5.39,0,0,0-1.87,3.36c-1.93,0-3.56,1.88-5.32,3a21.45,21.45,0,0,1-4.85,1.76c-4.83,1.54-9,4.71-12.77,8.05-2.1,1.83-4.22,3.85-5.14,6.47-.3.86-.46,1.76-.72,2.63a18.14,18.14,0,0,1-2.32,4.74,34.23,34.23,0,0,1-7,7.82,18.59,18.59,0,0,0-3.6,3.86c-1.53,2.21-4.64,2.87-6.28,5a23.75,23.75,0,0,1-2,3c-1.27,1.2-3.19,1.3-4.81,2-3.79,1.57-5.54,6-8.92,8.34a.94.94,0,0,1-.67.22c-.29,0-.46-.35-.71-.52-.49-.33-1.14-.08-1.72.07s-1.36.09-1.51-.49c-8.15-4.94-16.16-10.3-24.32-15.24l-3.72-2.25-19.9-12.06a1.48,1.48,0,0,0-1.57,0l-.62.43c-1-1.06-2.11-2-2.77-2.78-.27-.31-.54-.62-.82-.92a1.22,1.22,0,0,0,.53-.1,1,1,0,0,0,.43-1.3,3.26,3.26,0,0,0-.92-1.17,22.15,22.15,0,0,1-2.39-2.73,25.24,25.24,0,0,0-2.63-3.44,4.83,4.83,0,0,0-3.9-1.55,6.33,6.33,0,0,0-2.26.91,37.09,37.09,0,0,0-7.5,5.75.79.79,0,0,0-.33.62c0,.2.2.35.22.54a.7.7,0,0,1-.19.5,20.93,20.93,0,0,1-4.73,4.31q-3.81,2.85-7.45,5.92a10.42,10.42,0,0,0-3.48,4.25,8.31,8.31,0,0,0,.61,5.9c1.66,3.91,4.57,7.15,7.45,10.28a3.64,3.64,0,0,0,1.91,1.34,2.43,2.43,0,0,0,1.73-.42c1.77-1.16,2.06-3.56,2.19-5.66.05-1,.11-1.92.17-2.88a1.33,1.33,0,0,1,.11-.56,1.4,1.4,0,0,1,.61-.49l2.75-1.45c-.08-.13-.17-.26-.26-.39a11.21,11.21,0,0,1,6.49-.42c1.46.43,2.8,1.3,4.2,1.84a9.29,9.29,0,0,0,1,5.42c5.13.53,9.75,2.81,14.34,5.11A114.33,114.33,0,0,1,633,238.52q4.24,2.76,8.3,5.77c3.84,2.84,7.55,5.85,11.22,8.89a54.11,54.11,0,0,0,7.67,5.65c7.22,4.05,16.9,4.19,23.53-.86,8.36-6.37,19-9.68,28-15.19a31.6,31.6,0,0,0,7.25-5.57,4.94,4.94,0,0,1-.12.77c-.54,2.49-2.69,4.29-3.5,6.81-.63,2-.4,4.31-1.62,6-.93,1.29-2.67,2.21-2.72,3.8,0,.44.13.88.13,1.32a4.13,4.13,0,0,1-.48,1.59c-1,2.25-2.12,4.61-4.17,6-.8.55-1.82,1.05-2,2a1.89,1.89,0,0,1-.21.79c-.34.47-1.09.22-1.63.42-.75.28-.87,1.25-1.17,2a3.24,3.24,0,0,1-3.27,1.92c-6.83-.83-13.54,2.26-20.42,2.08-2.37-.07-4.73-.31-7.11-.33-7.3-.06-14.45,2-21.48,4a11.34,11.34,0,0,0-1.42,4.5,17.37,17.37,0,0,1-7.35.28,15.67,15.67,0,0,0-1.84-.25,2.77,2.77,0,0,0,.16-1.42,40.15,40.15,0,0,0-8.23,3.67c-1.71,1-3.52,2.4-3.68,4.37a6.57,6.57,0,0,0,.32,2.24l.93,3.53a3.72,3.72,0,0,0,.83,1.77c.31.3.72.48,1,.8a3.14,3.14,0,0,1,.54,1.27l.78,3.06a19.46,19.46,0,0,1,.62,3.11c.09,1,0,1.92.07,2.88a29.93,29.93,0,0,0,.69,4.7l1.11,5.54a18.91,18.91,0,0,0,1.31,4.5c.47,1,1.1,1.86,1.48,2.86.3.77.44,1.59.8,2.32a6.19,6.19,0,0,0,2.9,2.68,3.23,3.23,0,0,0,1.41.44c1.42,0,2.44-1.32,3.1-2.58a15.6,15.6,0,0,0,2.07-7.52c0-1.06-.21-2.11-.3-3.17-.24-3,.62-5.92.62-8.91a1.75,1.75,0,0,0-1.25-1.82c.09-2.54.08-5.23,1.42-7.4a11.15,11.15,0,0,1,2.44-2.65c.37,1.18.78,2.35,1.24,3.49C658.58,296.45,666.44,294.31,674.41,294.11ZM837,140.54c-.54.2-1.09.4-1.65.58-4.66,1.55-10,3.06-14.82,2a12.87,12.87,0,0,1-4.06-2l-.35-.22C822.91,139.71,830,140,837,140.54Z" transform="translate(-52.64 -55.65)" fill="url(#f137b292-0aa2-40b3-a442-9a256144b6d8)"/><path d="M864.06,130.25a15.45,15.45,0,0,1,5.31-1.23,11.65,11.65,0,0,1,3.42.79,16.08,16.08,0,0,1,5.44,2.8,6.51,6.51,0,0,1,2.41,5.4c-.23,2-1.71,3.73-3.33,5a13.27,13.27,0,0,1-7.76,3c-3.18.05-6.18-1.38-9.25-2.23a26.56,26.56,0,0,0-6.86-1,15,15,0,0,1-3.49-.22,3.74,3.74,0,0,1-2.68-2.06,5.28,5.28,0,0,1,0-3c.23-1.24.19-4.18,1.29-4.92.88-.6,4-.12,5.08-.21A50.14,50.14,0,0,0,864.06,130.25Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M792.67,110.12a46.24,46.24,0,0,1,7.75,10c3.52,6.14,5.76,13.28,11.06,18a37.32,37.32,0,0,0,4.47,3.23,12.9,12.9,0,0,0,4.06,2c4.76,1.07,10.1-.42,14.74-2a93.35,93.35,0,0,0,13.33-6,11,11,0,0,0,1.56,5.48c1.11,1.57,3.38,2.49,5,1.5a59.3,59.3,0,0,0-11.53,7.13c-1.37,1.16-2.64,2.44-4.1,3.49-5.27,3.79-12.56,4.17-17.43,8.47a4.69,4.69,0,0,1-5.41.65c-1.69-1-2.84-2.72-4.23-4.11-2.74-2.74-6.46-4.34-9.15-7.13-1.25-1.29-2.25-2.82-3.53-4.08-1.83-1.8-4.21-3-5.89-5-1.25-1.47-2-3.26-3.17-4.83-1-1.36-2.18-2.55-3.07-4-2.18-3.47-2.16-8-.91-11.89S790.34,113.51,792.67,110.12Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M792.67,110.12a46.24,46.24,0,0,1,7.75,10c3.52,6.14,5.76,13.28,11.06,18a37.32,37.32,0,0,0,4.47,3.23,12.9,12.9,0,0,0,4.06,2c4.76,1.07,10.1-.42,14.74-2a93.35,93.35,0,0,0,13.33-6,11,11,0,0,0,1.56,5.48c1.11,1.57,3.38,2.49,5,1.5a59.3,59.3,0,0,0-11.53,7.13c-1.37,1.16-2.64,2.44-4.1,3.49-5.27,3.79-12.56,4.17-17.43,8.47a4.69,4.69,0,0,1-5.41.65c-1.69-1-2.84-2.72-4.23-4.11-2.74-2.74-6.46-4.34-9.15-7.13-1.25-1.29-2.25-2.82-3.53-4.08-1.83-1.8-4.21-3-5.89-5-1.25-1.47-2-3.26-3.17-4.83-1-1.36-2.18-2.55-3.07-4-2.18-3.47-2.16-8-.91-11.89S790.34,113.51,792.67,110.12Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M614.94,202.47a10.36,10.36,0,0,0-2.82-2.55,8.54,8.54,0,0,0-5.27-.54c-4.46.68-8.6,2.72-12.52,5-3.16,1.81-6.3,3.82-8.56,6.67a6.85,6.85,0,0,0-1.51,3,6.18,6.18,0,0,0,.23,2.73,6.6,6.6,0,0,0,3.57,4.62c2.09.82,4.42-.05,6.52-.85,2.82-1.09,5.92-2.1,8.82-1.24,2.15.64,4,2.27,6.27,2.33,2.67.08,4.83-2.08,6.53-4.14,1.47-1.8,4.69-5.05,4.48-7.58S616.52,204.28,614.94,202.47Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M648.64,280.3a17.27,17.27,0,0,1-7.5.31c-1.52-.28-3.38-.64-4.35.55a3.58,3.58,0,0,0-.61,1.48c-2.49,10.19-.21,21,4,30.65a.94.94,0,0,0,.28.42c.27.2.64.06.95-.07a11.28,11.28,0,0,0,2.83-1.65c1.92-1.74,2.33-4.57,2.42-7.16s.05-5.33,1.39-7.55c1.68-2.75,5.19-4.09,6.57-7,1.11-2.37.53-5.18-.45-7.6-.33-.84-.46-3-1.24-3.41S649.53,280.06,648.64,280.3Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M614.65,201.45a1,1,0,0,0,.42-1.3,3.38,3.38,0,0,0-.92-1.18,20.69,20.69,0,0,1-2.38-2.74,27.68,27.68,0,0,0-2.63-3.44,4.85,4.85,0,0,0-3.89-1.56,6,6,0,0,0-2.25.9,36.36,36.36,0,0,0-7.45,5.72c-.17.17-.36.38-.32.62s.2.34.22.54a.69.69,0,0,1-.18.49,20.68,20.68,0,0,1-4.7,4.29q-3.78,2.84-7.4,5.9a10.3,10.3,0,0,0-3.45,4.23,8.41,8.41,0,0,0,.63,5.89c1.67,3.92,4.58,7.16,7.45,10.31a3.78,3.78,0,0,0,1.92,1.35,2.42,2.42,0,0,0,1.72-.42c1.75-1.15,2-3.55,2.15-5.64l.16-2.89a1.29,1.29,0,0,1,.11-.55,1.35,1.35,0,0,1,.61-.49l2.72-1.44a8.77,8.77,0,0,0-8-4.16,3,3,0,0,1-2.14-.29,1.76,1.76,0,0,1,0-2.44,5.38,5.38,0,0,1,2.36-1.31c7.17-2.63,13.65-6.85,20.61-10a5.29,5.29,0,0,1,2.46-.57C613.18,201.32,614,201.73,614.65,201.45Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M642.9,306.17c-.32.6-.43,1.3-.78,1.89s-1.11,1-1.7.7a1.75,1.75,0,0,1-.66-.89,37.36,37.36,0,0,1-2.45-22.06,13.91,13.91,0,0,1,1.07-3.52c.53-1.08,1.3-2.18,1.08-3.36a39.64,39.64,0,0,0-8.18,3.64c-1.7,1-3.49,2.39-3.65,4.35a6.84,6.84,0,0,0,.33,2.24l.94,3.53a3.65,3.65,0,0,0,.84,1.77c.3.3.71.49,1,.81a3,3,0,0,1,.54,1.26l.79,3.06a19.6,19.6,0,0,1,.63,3.12c.09,1,0,1.92.08,2.88a31,31,0,0,0,.7,4.69l1.13,5.54a19.26,19.26,0,0,0,1.32,4.5c.47,1,1.1,1.86,1.49,2.87.29.77.44,1.59.8,2.32a6.29,6.29,0,0,0,2.89,2.69,3.39,3.39,0,0,0,1.41.45c1.41.05,2.42-1.32,3.08-2.58a15.56,15.56,0,0,0,2-7.5c0-1.06-.22-2.11-.31-3.17-.25-3,.59-5.91.58-8.9C647.89,303.7,643.93,304.23,642.9,306.17Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M789.62,131.94a22.08,22.08,0,0,1-8-6.25,38.19,38.19,0,0,1-3-4.45,135.15,135.15,0,0,1-9.75-20.07c4.26.87,8.73,1.66,12.92.46a17.84,17.84,0,0,0,6-3.18,51.87,51.87,0,0,0,6.32-6.17,60.76,60.76,0,0,0,5,8c1.07,1.44,4.69,4.37,4.42,6.33s-4.27,2.94-5.89,3.87l-.17.11a22.68,22.68,0,0,0-6.15,5.73C788.18,120.78,787.09,127,789.62,131.94Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M797.72,110.44a18.74,18.74,0,0,1-9.85-12,51.87,51.87,0,0,0,6.32-6.17,60.76,60.76,0,0,0,5,8c1.07,1.44,4.69,4.37,4.42,6.33S799.34,109.51,797.72,110.44Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><circle cx="754.08" cy="37.53" r="18.73" fill="#fbbebe"/><path d="M765.75,209.85a8.74,8.74,0,0,1-8.62.21,16.59,16.59,0,0,1-3.72-3.13c-6.15-6.31-12-13-16.11-20.83a14.44,14.44,0,0,1-1.82-5.37,5.9,5.9,0,0,1,1.93-5.12,4.16,4.16,0,0,0,1-.93,3.75,3.75,0,0,0,.33-1.7,5.31,5.31,0,0,1,3.54-4.43c-1-.68-.6-2.19.05-3.16s1.56-2,1.4-3.12a17.12,17.12,0,0,0-.92-2.18c-.36-1.22.29-2.48.64-3.7.73-2.5.29-5.16.11-7.76-.53-7.7,1.34-15.34,3.19-22.83a61.24,61.24,0,0,1,3.52-11.1c.47-1,1-2,1.54-2.89a76.83,76.83,0,0,1,6.29-8.75,11.36,11.36,0,0,1,4.29-3.84,4.24,4.24,0,0,1,.5-.18,10.88,10.88,0,0,1,3.82-.29c3.38.21,6.93,1,9.34,3.38s3.19,5.72,4.28,8.94a24.63,24.63,0,0,0,1.26,3.15c1.59,3.16,4.18,5.68,6.73,8.13l2.77,2.67c1.39,1.34,3.8,1,5.11,2.41a6.48,6.48,0,0,1,1.17,2.24A34.75,34.75,0,0,1,799.92,141a18.75,18.75,0,0,1-.15,2.59,13.64,13.64,0,0,1-3.79,8c-2.4,2.31-5.74,3.45-8.13,5.77-2,1.92-3.16,4.49-4.3,7q-3.3,7.26-6.61,14.54a82.39,82.39,0,0,0-4.11,10.25C770.75,196.14,771.93,206,765.75,209.85Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M708,292.25c-4.94.2-9.89.14-14.81.61-2.87.28-5.73.75-8.61.87-3.14.14-6.29-.11-9.44-.05-7.93.17-15.75,2.28-23.38,4.46a44.4,44.4,0,0,1-3.31-16.57,10.57,10.57,0,0,1,0-1.13,11.37,11.37,0,0,1,1.41-4.58c7-2,14.1-4,21.37-3.9,2.37,0,4.72.28,7.08.35,6.85.21,13.51-2.86,20.31-2a3.2,3.2,0,0,0,3.25-1.91c.29-.74.41-1.71,1.16-2,.54-.2,1.28.05,1.62-.41a2.1,2.1,0,0,0,.21-.79c.2-1,1.21-1.45,2-2,2-1.38,3.13-3.74,4.13-6a4.07,4.07,0,0,0,.47-1.58c0-.44-.14-.88-.13-1.33,0-1.58,1.77-2.49,2.69-3.78,1.21-1.7,1-4,1.59-6,.8-2.52,2.93-4.31,3.46-6.79a4.94,4.94,0,0,0,.12-.77,31.32,31.32,0,0,1-7.21,5.54c-8.88,5.47-19.5,8.74-27.79,15.07-6.58,5-16.22,4.85-23.42.78a54.16,54.16,0,0,1-7.66-5.67c-3.66-3-7.36-6.07-11.19-8.93q-4-3-8.29-5.79a114.13,114.13,0,0,0-10.74-6.29c-4.58-2.32-9.19-4.62-14.29-5.17a10.53,10.53,0,0,1-.28-8.64A29.14,29.14,0,0,1,613,210.3a21.78,21.78,0,0,1,5.36-5.47,1.46,1.46,0,0,1,1.56,0L639.74,217l3.71,2.27c8.13,5,16.13,10.35,24.26,15.32.15.57.94.64,1.51.49s1.22-.39,1.71-.06c.24.16.41.46.71.52a1,1,0,0,0,.66-.22c3.36-2.31,5.08-6.75,8.85-8.3,1.61-.66,3.52-.76,4.78-2a22.71,22.71,0,0,0,2-3c1.63-2.13,4.72-2.77,6.23-5a18.35,18.35,0,0,1,3.57-3.84,33.83,33.83,0,0,0,7-7.79,18.08,18.08,0,0,0,2.29-4.72c.26-.87.42-1.77.71-2.63.91-2.62,3-4.63,5.09-6.45,3.79-3.32,7.88-6.47,12.68-8a21.44,21.44,0,0,0,4.83-1.75c1.93-1.18,3.7-3.37,5.91-2.86a5,5,0,0,1,2.19,1.38,69.61,69.61,0,0,1,7.45,8.53q5.35,6.81,10.69,13.63a15.75,15.75,0,0,0,4.19,4.15,11.49,11.49,0,0,0,7.75,1c-.35,3.71-3.3,6.77-4.52,10.29-.59,1.71-.82,3.53-1.26,5.28a43.25,43.25,0,0,1-2.37,6.41c-2.4,5.59-4.82,11.21-8.26,16.23-1.78,2.61-3.83,5-5.84,7.45-7.93,9.57-14,21.11-23,29.72-2.18,2.11-3.56,5-6,6.81C714.61,291.82,711.19,292.12,708,292.25Z" transform="translate(-52.64 -55.65)" fill="#434175"/><path d="M745.68,165.37a5.43,5.43,0,0,0-2.5.75c1.83-.64,4.06,1.07,5.75.26.79-.38.67-.49,0-.72C747.92,165.32,746.7,165.63,745.68,165.37Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><rect x="987.99" y="508.41" width="78" height="14" transform="translate(-165.92 317.79) rotate(-19.55)" fill="#d0d2d5"/><path d="M1012.57,535.77h71a7,7,0,0,1,7,7v0a7,7,0,0,1-7,7h-71a0,0,0,0,1,0,0v-14A0,0,0,0,1,1012.57,535.77Z" transform="translate(-173.65 327.59) rotate(-19.55)" fill="#d0d2d5"/><path d="M963.62,496.55h17.3a58,58,0,0,1,58,58v17.3a22.68,22.68,0,0,1-22.68,22.68H963.62a22.68,22.68,0,0,1-22.68-22.68V519.23a22.68,22.68,0,0,1,22.68-22.68Z" transform="translate(-178.14 307.13) rotate(-19.55)" fill="#3f3d56"/><path d="M744.78,175.13a3.88,3.88,0,0,0-2.11-.13,4.46,4.46,0,0,0-2.75,1.35,1.5,1.5,0,0,0,1.23,0,7.27,7.27,0,0,1,2-.22c.58,0,2.31.64,2.76.36C746.57,176.06,745.23,175.3,744.78,175.13Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M799.77,143.59a19.42,19.42,0,0,1-4.28-2.06c-3.51-2.23-6.46-5.26-9.93-7.54a55.25,55.25,0,0,0-5.43-3l-11.71-6a11.87,11.87,0,0,0-2.84-1.16c-1.91-.42-4.06.86-6,.59a7.23,7.23,0,0,1-4.88-3.37,17.57,17.57,0,0,1-2.2-5.67,29.09,29.09,0,0,1-.67-3.59,16,16,0,0,1-.07-3.79,9.48,9.48,0,0,1,3.3-6.46,12.09,12.09,0,0,1,4.64-2,19.37,19.37,0,0,1,3.21-.53,11.43,11.43,0,0,1,1.85,0c4.42.35,8.16,3.35,11.11,6.65,1.55,1.74,3,3.61,4.48,5.38a24.63,24.63,0,0,0,1.26,3.15c1.59,3.16,4.18,5.68,6.73,8.13l2.77,2.67c1.39,1.34,3.8,1,5.11,2.41a6.48,6.48,0,0,1,1.17,2.24A34.75,34.75,0,0,1,799.92,141,18.75,18.75,0,0,1,799.77,143.59Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M708.7,271.57l-60.26,8.87a11.37,11.37,0,0,1,1.41-4.58c7-2,14.1-4,21.37-3.9,2.37,0,4.72.28,7.08.35,6.85.21,13.51-2.86,20.31-2a3.2,3.2,0,0,0,3.25-1.91c.29-.74.41-1.71,1.16-2,.54-.2,1.28.05,1.62-.41a2.1,2.1,0,0,0,.21-.79c.2-1,1.21-1.45,2-2,2-1.38,3.13-3.74,4.13-6a4.07,4.07,0,0,0,.47-1.58c0-.44-.14-.88-.13-1.33,0-1.58,1.77-2.49,2.69-3.78,1.21-1.7,1-4,1.59-6,.8-2.52,2.93-4.31,3.46-6.79l.26-.34,10.26-4Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><ellipse cx="425" cy="668.67" rx="425" ry="33" fill="#908820" opacity="0.1"/><ellipse cx="800.72" cy="752.92" rx="275" ry="35.77" fill="#908820" opacity="0.1"/><path d="M527.89,697.6l.28-14.16q20-1.14,40-1.52c7.74-.14,15.58-.19,23.07,1.75,5.19,1.35,10.1,3.63,15.22,5.23,10.2,3.18,21,3.6,31.71,3.89,10.39.29,21.24.39,30.57-4.2,16.15-8,22.94-27.65,23.73-45.64s-2.79-36.12-.3-54" transform="translate(-52.64 -55.65)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="12"/><polygon points="511.81 669.66 333.62 667.38 334.14 662.81 342.76 587.42 499.24 587.42 510.76 662.81 511.64 668.52 511.81 669.66" fill="#d0d2d5"/><polygon points="511.64 668.52 422.71 668.52 333.62 667.38 334.14 662.81 510.76 662.81 511.64 668.52" opacity="0.1"/><rect x="303.92" y="663.95" width="236.45" height="5.71" fill="#d0d2d5"/><path d="M818.6,190.27a14.87,14.87,0,0,0-14.8-15H143.48a14.88,14.88,0,0,0-14.8,15V590.53H818.6Z" transform="translate(-52.64 -55.65)" fill="#3f3d56"/><path d="M128.68,586.53v46.89a14.8,14.8,0,0,0,14.8,14.8H803.8a14.8,14.8,0,0,0,14.8-14.8V586.53Z" transform="translate(-52.64 -55.65)" fill="#d0d2d5"/><rect x="104.6" y="144.8" width="636.23" height="359.81" fill="#2f2e41"/><path d="M477.07,630.43a15.43,15.43,0,0,0,12.13-5.89h0a15.28,15.28,0,0,0,1.2-1.77l-8.46-1.39,9.15.07a15.44,15.44,0,0,0,.29-12.22l-12.27,6.37,11.32-8.32A15.42,15.42,0,1,0,465,624.53h0A15.4,15.4,0,0,0,477.07,630.43Z" transform="translate(-52.64 -55.65)" fill="#908820"/><polygon points="439.85 592.56 510.94 663.95 500.03 592.56 439.85 592.56" opacity="0.1"/><path d="M797.82,197.52,836,146.93c2.28-3,4.63-6.13,7.76-8.29,9-6.22,22.19-2.54,29.72,5.43s10.69,19.06,13,29.78A316.11,316.11,0,0,1,893.64,249c-.32,13.29-1.51,26.72-5.74,39.33-6.42,19.18-19.43,35.33-29.26,53-15.83,28.47-22.46,66.44-2.42,92.13,6.53,8.37,15.42,14.77,21.45,23.51,6.81,9.87,9.46,21.94,12,33.66,3.79,17.7,7.59,35.51,8.29,53.6,2.26,58.49-31.09,116.93-82.59,144.75-35.75,19.32-79,24.92-110.09,51.12-11,9.23-20.48,22.8-17.78,36.86,2.2,11.41,12.22,20.08,23.22,23.85s22.93,3.46,34.53,2.8a636.33,636.33,0,0,0,83-10.25c23.23-4.44,46.42-8.27,66.67-20.49a14,14,0,0,0,5.05-4.53c1.31-2.19,1.53-4.84,1.71-7.39.55-7.51,1.07-15.3-1.58-22.35-3.09-8.24-10.14-14.48-13.52-22.6s-2.77-17.47-.58-26c2.7-10.54,7.61-20.36,12.55-30.05,4.81-9.44,9.69-18.85,15.35-27.8,15.94-25.16,37.72-46,59.27-66.55" transform="translate(-52.64 -55.65)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="12"/><path d="M861,125a15,15,0,0,1,5.31-1.23,12,12,0,0,1,3.42.78,16.1,16.1,0,0,1,5.44,2.81,6.49,6.49,0,0,1,2.41,5.4c-.23,2-1.71,3.72-3.33,5a13.22,13.22,0,0,1-7.76,3c-3.18,0-6.18-1.38-9.25-2.24a26.55,26.55,0,0,0-6.86-1,15,15,0,0,1-3.49-.23,3.73,3.73,0,0,1-2.68-2.05,5.38,5.38,0,0,1,0-2.95c.24-1.24.2-4.19,1.3-4.93.88-.59,4-.11,5.08-.2A51.46,51.46,0,0,0,861,125Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M847.13,126c-4.14,1.9-9,.32-13.5.31-2.21,0-4.41.37-6.61.5a59.44,59.44,0,0,1-7.31-.19l-7.32-.5a62.44,62.44,0,0,1-12.79-1.76c-3.58-4.22-9.39-6.08-13.71-9.52-3.46-2.76-5.94-6.52-8.89-9.83s-6.69-6.3-11.1-6.65a16.41,16.41,0,0,0-5.07.54,11.82,11.82,0,0,0-4.63,2,9.44,9.44,0,0,0-3.3,6.46,21.68,21.68,0,0,0,.73,7.38,17.62,17.62,0,0,0,2.2,5.68,7.34,7.34,0,0,0,4.89,3.37c1.93.26,4.07-1,6-.6a11.87,11.87,0,0,1,2.84,1.16l11.72,6a56.26,56.26,0,0,1,5.42,3c3.48,2.28,6.42,5.31,9.93,7.54s7.94,3.63,11.84,2.17c12-4.47,25.52-2.39,38.25-1.28-.08-.24.44-.46.59-.66a10.75,10.75,0,0,0,2-7.49C849,131,848,128.52,847.13,126Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M827,92.31a6.85,6.85,0,0,0,3-2.68c1.14-2.11.44-4.7-.34-7s-1.57-4.31-2.44-6.43a15.49,15.49,0,0,0-2.78-4.87c-1.77-1.89-4.3-2.84-6.41-4.33-2.5-1.77-4.37-4.28-6.48-6.49s-4.69-4.26-7.73-4.63A14.94,14.94,0,0,0,797,57.15c-18.26,6.71-33.19,20-49.54,30.54-6.36,4.11-13.5,7.94-21,7.39l6.71,10.66c1.71,2.73,3.63,5.62,6.63,6.79,2.65,1,5.61.5,8.39,0a22.61,22.61,0,0,0,6.39-1.92c4-2.17,6.57-6.68,10.87-8.25,3.21-1.17,6.79-.45,10,.67s6.36,2.64,9.74,3.1c6,.81,12-1.9,17-5.4,3-2.08,5.73-4.47,9-6a13.56,13.56,0,0,1,8-1.41C822.22,93.74,824,93.88,827,92.31Z" transform="translate(-52.64 -55.65)" fill="#2f2e41"/><path d="M830,89.63c1.12-2.07.46-4.61-.3-6.84a6,6,0,0,1-.5,3.77,7,7,0,0,1-3,2.68c-3,1.58-4.82,1.44-7.9.93a13.66,13.66,0,0,0-8,1.41c-3.25,1.57-6,4-9,6-5,3.5-11,6.2-17,5.39-3.39-.45-6.52-2-9.74-3.1s-6.81-1.84-10-.67c-4.29,1.57-6.83,6.09-10.86,8.25a22.71,22.71,0,0,1-6.39,1.92c-2.79.54-5.75,1.07-8.39,0-3-1.16-4.92-4.06-6.64-6.79l-4.75-7.55c-.38,0-.77,0-1.15,0l6.71,10.66c1.71,2.73,3.63,5.62,6.63,6.79,2.65,1,5.61.5,8.39,0a22.61,22.61,0,0,0,6.39-1.92c4-2.17,6.57-6.68,10.87-8.25,3.21-1.17,6.79-.45,10,.67s6.36,2.64,9.74,3.1c6,.81,12-1.9,17-5.4,3-2.08,5.73-4.47,9-6a13.56,13.56,0,0,1,8-1.41c3.08.5,4.9.64,7.89-.93A6.85,6.85,0,0,0,830,89.63Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M881.7,773.66a10,10,0,0,1,2.18-2.42c4.37-4.12,6.4-10.18,7.48-16.12s1.39-12,3.12-17.81a57.24,57.24,0,0,1,3.52-8.67c1-2.14,2.25-6.34,4.09-7.83a2.66,2.66,0,0,1,1.26-.61,14.65,14.65,0,0,1,3.62-4.82c2.86-2.59,6.35-4.65,8.26-8a17.64,17.64,0,0,1,1.93-3.29c1.29-1.43,3.33-2,4.52-3.51,1.55-1.94,1.2-4.71,1-7.19s0-5.43,2.14-6.72c1.28-.79,2.93-.72,4.26-1.41,2-1,2.79-3.53,2.75-5.78s-.74-4.44-.92-6.68c-.56-7.16,4.16-14.28,2.33-21.22-.69-2.63-2.26-4.92-3.43-7.36a17.2,17.2,0,0,1-1-2.54,35.57,35.57,0,0,1-9.73,2.35c5.51-12.36,4.11-26.73,6.68-40l-.33-.46a20.71,20.71,0,0,1-4.22-11.85c0-5,2.31-9.57,3.35-14.4.88-4.12.85-8.41,2-12.45a38.32,38.32,0,0,1,3.17-7.22c1.25-2.32,2.56-4.59,3.93-6.83q1.92-6.36,3.85-12.72c1.8-6,3.65-12,7.15-17.14,1.28-1.89,1.67-5.7,3.64-6.84a8.73,8.73,0,0,1,5.2-.91c2.78-1.56,5.22-3.81,5.57-6.89a6.68,6.68,0,0,0-.2-2l-.34,0c-4.41-.41-6.63-4.29-8.92-7.68-3.71-5.51-2.29-12.95,0-19.2a13,13,0,0,1,3-5.29c3.21-3,8.29-2.34,12.43-3.79a42.64,42.64,0,0,1,4.11-1.55c2.75-.65,5.63.26,8.15,1.52s4.87,2.92,7.51,3.92,5.9,1.79,6.55,4.5a4.91,4.91,0,0,1-.23,2.73,10.18,10.18,0,0,1-3.08,4.45,17.82,17.82,0,0,1-10.87,24.83c-.76,1.21-1.47,2.46-2.12,3.73-1.16,2.24-2.14,4.88-1.75,7.28a21.37,21.37,0,0,1,2,2.44c2.87,4,4.5,8.7,6,13.38a139.66,139.66,0,0,1,4.84,18.27c2.44,5.18,5.42,10.14,7.19,15.59a35.2,35.2,0,0,0,9.38-1.25l8.44-1.92c2.93-.66,6-1.38,8.3-3.32,1.22-1,2.17-2.33,3.41-3.33s3.73-2.15,5.09-1.33a4,4,0,0,1,.69.53c1.23-2.5,9.1,7.73,3.72,8.74l.35,1c.71,2.07,2.22,4.59,1.84,6.75L997,588.21a23.19,23.19,0,0,1-7.56,1.67,12.21,12.21,0,0,1-3-.4l0,1.92v.07c-.1,13.19,2,26.3,2.39,39.48.07,2.4,0,5-1.52,6.88-1.7,2.06-4.72,2.46-7.35,2a16.65,16.65,0,0,1-1.86-.41,69.45,69.45,0,0,0-1.92,10.1l-2.49,17.76a26.45,26.45,0,0,0-.39,5.54c.29,4.18,2.5,7.94,4.1,11.81s2.55,8.5.44,12.11c-.65,1.12-1.56,2.08-2.15,3.24a13.1,13.1,0,0,0-1,5.67q-.23,8.1-.44,16.18a1.89,1.89,0,0,1,1.26.26c2.08,1.61,1.85,9.33,2,11.72.16,4,0,8.08.22,12.12a71.1,71.1,0,0,0,2.68,16.13,10.29,10.29,0,0,0,2.29,4.61,9.82,9.82,0,0,0,4.06,2.11,46.58,46.58,0,0,0,15.42,2.1c2.11-.07,4.31-.27,6.27.53s3.54,3,2.83,5c-.61,1.68-2.46,2.48-4.13,3.11a64.81,64.81,0,0,0-11,5.06,29.31,29.31,0,0,1-4.26,2.41,21.58,21.58,0,0,1-4.38,1.08l-17.72,3c-3.63.63-8,1-10.31-2-1.25-1.62-1.43-3.82-1.37-5.87.1-4,.9-8.07.37-12.07a36.46,36.46,0,0,0-1.85-6.94l-5.32-15.72c-1.78-5.27-3.58-10.58-4.27-16.1-.25-2-.23-4.27,1.3-5.52a3.54,3.54,0,0,1,1.32-.68c.06-4.82.69-9.65.42-14.47-.07-1.2-.19-2.41-.29-3.61a16.82,16.82,0,0,0-2.48,2.92c-1.7,2.67-2.33,6.21-4.94,8-2.22,1.52-5.51,1.44-7.07,3.64a10.26,10.26,0,0,0-1.12,2.71,19.6,19.6,0,0,1-4,6.35,32.91,32.91,0,0,1-2.47,6.34q-2.21,4.65-4.4,9.3c-1.57,3.32-3.15,6.64-5,9.78a27,27,0,0,0-3.35,6.46c-1.41,4.94.88,10.36,4.53,14s8.44,5.73,13.16,7.68a3.62,3.62,0,0,1,1.35.8,2.58,2.58,0,0,1,.6,1.74c0,2.31-1.89,4.29-4.07,5a15.27,15.27,0,0,1-6.83.25c-12.78-1.55-25.52-4.67-36.82-10.88a13.22,13.22,0,0,1-4.4-3.39A5.18,5.18,0,0,1,881.7,773.66Z" transform="translate(-52.64 -55.65)" fill="url(#ab747304-1bb2-44ed-8687-72ce94e318d2)"/><path d="M935.72,549.41q-3.19,4.9-6,10.06a37.17,37.17,0,0,0-3.17,7.16c-1.17,4-1.13,8.28-2,12.36-1,4.8-3.34,9.38-3.34,14.29a20.5,20.5,0,0,0,4.22,11.77,56.63,56.63,0,0,0,8.82,9.1,187.62,187.62,0,0,0,12.48-61.21c.1-2.9.11-5.9-.94-8.6-.66-1.7-3.77-6.36-5.78-4.53-.73.67-.92,3.69-1.37,4.69A36.23,36.23,0,0,1,935.72,549.41Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M935.72,549.41q-3.19,4.9-6,10.06a37.17,37.17,0,0,0-3.17,7.16c-1.17,4-1.13,8.28-2,12.36-1,4.8-3.34,9.38-3.34,14.29a20.5,20.5,0,0,0,4.22,11.77,56.63,56.63,0,0,0,8.82,9.1,187.62,187.62,0,0,0,12.48-61.21c.1-2.9.11-5.9-.94-8.6-.66-1.7-3.77-6.36-5.78-4.53-.73.67-.92,3.69-1.37,4.69A36.23,36.23,0,0,1,935.72,549.41Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M1043.07,560.34c3.91-.65,8.25-.33,11.29,2.2a9.32,9.32,0,0,1,3.24,6.16c.26,2.78-1.35,6.06-4.13,6.28a13.17,13.17,0,0,1-2-.13,29.88,29.88,0,0,0-5.24.43,21.42,21.42,0,0,1-18.24-7.36c-2.38-2.94.76-4.29,3.59-4.89C1035.44,562.21,1039.21,561,1043.07,560.34Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M976.19,650.66l-2.48,17.62a26,26,0,0,0-.38,5.5c.29,4.15,2.49,7.88,4.09,11.72s2.56,8.43.45,12c-.65,1.12-1.57,2.06-2.15,3.21a13,13,0,0,0-1,5.64l-.68,25a158.71,158.71,0,0,1-23.18-.13A1.35,1.35,0,0,1,950,731a1.38,1.38,0,0,1-.29-.91c-.34-5.85.68-11.71.35-17.56-.12-2.14-.43-4.27-.46-6.42-.13-8.71,4.18-17.24,2.88-25.86a57.35,57.35,0,0,0-1.6-6.42c-4.11-15-3.24-30.77-2.34-46.25a1.18,1.18,0,0,1,.2-.7,1.23,1.23,0,0,1,.55-.3,28.48,28.48,0,0,1,25.78,4.91c1.09.88,3.84,1.73,4.56,2.77,1,1.47-.32,2.92-.89,4.48A56.89,56.89,0,0,0,976.19,650.66Z" transform="translate(-52.64 -55.65)" fill="#3f3d56"/><path d="M976.19,650.66l-2.48,17.62a26,26,0,0,0-.38,5.5c.29,4.15,2.49,7.88,4.09,11.72s2.56,8.43.45,12c-.65,1.12-1.57,2.06-2.15,3.21a13,13,0,0,0-1,5.64l-.68,25a158.71,158.71,0,0,1-23.18-.13A1.35,1.35,0,0,1,950,731a1.38,1.38,0,0,1-.29-.91c-.34-5.85.68-11.71.35-17.56-.12-2.14-.43-4.27-.46-6.42-.13-8.71,4.18-17.24,2.88-25.86a57.35,57.35,0,0,0-1.6-6.42c-4.11-15-3.24-30.77-2.34-46.25a1.18,1.18,0,0,1,.2-.7,1.23,1.23,0,0,1,.55-.3,28.48,28.48,0,0,1,25.78,4.91c1.09.88,3.84,1.73,4.56,2.77,1,1.47-.32,2.92-.89,4.48A56.89,56.89,0,0,0,976.19,650.66Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M929.82,645.34c1.17,2.43,2.74,4.7,3.43,7.31,1.83,6.89-2.88,14-2.32,21.06.18,2.23.87,4.4.92,6.63s-.77,4.71-2.75,5.73c-1.33.69-3,.62-4.25,1.4-2.11,1.28-2.35,4.22-2.14,6.67s.55,5.21-1,7.14c-1.19,1.49-3.23,2.06-4.51,3.47a17.3,17.3,0,0,0-1.94,3.27c-1.91,3.33-5.39,5.38-8.25,8s-5.28,6.46-4,10.08c.94,2.67,3.58,4.31,6.12,5.56a64.93,64.93,0,0,0,14.57,5.15,3.51,3.51,0,0,0,1.67.08,3.64,3.64,0,0,0,1.46-1c2.95-2.89,6-5.92,7.36-9.81a10.27,10.27,0,0,1,1.12-2.69c1.56-2.18,4.84-2.11,7.07-3.61,2.6-1.77,3.23-5.29,4.93-7.94,1.61-2.5,4.19-4.22,6-6.56,3-3.88,3.81-9.05,6-13.48.93-1.89,2.12-3.64,2.86-5.61a30.4,30.4,0,0,0,1.37-7.23c1.41-11.68,5-23.44,2.74-35-.5-2.55-1.27-5-1.59-7.62-.56-4.54.32-9.17-.35-13.7a3.73,3.73,0,0,0-1.51-2.9,4.18,4.18,0,0,0-1.72-.36,118.6,118.6,0,0,0-17.78.41c-3,.29-6.37.9-8.16,3.35-1.53,2.09-1.45,4.91-2,7.46-.6,3.13-2.19,4.85-3.86,7.31S928.59,642.81,929.82,645.34Z" transform="translate(-52.64 -55.65)" fill="#3f3d56"/><path d="M952.89,726.6c-1.58,0-3.33-.09-4.55.91-1.52,1.24-1.55,3.53-1.3,5.49.7,5.47,2.49,10.74,4.28,16l5.32,15.61a35.87,35.87,0,0,1,1.85,6.88c.53,4-.27,8-.37,12,0,2,.13,4.22,1.38,5.83,2.26,2.9,6.67,2.56,10.3,1.94l17.71-3a21.72,21.72,0,0,0,4.38-1.07,29.68,29.68,0,0,0,4.25-2.4,65.38,65.38,0,0,1,11-5c1.66-.62,3.52-1.42,4.12-3.09.71-2-.87-4.19-2.83-5s-4.15-.6-6.27-.53a46.72,46.72,0,0,1-15.4-2.09,9.61,9.61,0,0,1-4.06-2.09,10.12,10.12,0,0,1-2.3-4.57,71.27,71.27,0,0,1-2.68-16c-.2-4-.06-8-.22-12-.1-2.37.13-10-2-11.63-1.59-1.22-8.14,2.15-10.28,2.64A49.57,49.57,0,0,1,952.89,726.6Z" transform="translate(-52.64 -55.65)" fill="#2f2e41"/><path d="M894.54,737.72c-1.72,5.74-2,11.79-3.11,17.68s-3.1,11.91-7.47,16a9.69,9.69,0,0,0-2.17,2.4,5.06,5.06,0,0,0,.75,5.23,13.13,13.13,0,0,0,4.39,3.37c11.29,6.16,24,9.26,36.81,10.8a15.36,15.36,0,0,0,6.82-.24c2.18-.74,4.11-2.7,4.07-5a2.53,2.53,0,0,0-.6-1.72,3.73,3.73,0,0,0-1.35-.79c-4.72-1.94-9.52-4.06-13.16-7.63s-5.94-9-4.53-13.86a27.38,27.38,0,0,1,3.34-6.41,106.05,106.05,0,0,0,5-9.7l4.4-9.23c1.28-2.69,2.59-5.47,2.8-8.46a40.57,40.57,0,0,1-16.21-2.59,25.54,25.54,0,0,1-6.67-4c-1.7-1.42-3.25-4.06-5.54-2.21-1.84,1.48-3,5.64-4.09,7.76A59.48,59.48,0,0,0,894.54,737.72Z" transform="translate(-52.64 -55.65)" fill="#2f2e41"/><path d="M959,508.54c-.44,3.83-4.16,6.37-7.71,7.88l22.46,7.18c-1.45-2.78-.27-6.18,1.17-9a50.81,50.81,0,0,1,4-6.4c.22-.31.47-.71.31-1.06a1.12,1.12,0,0,0-.63-.49c-4.62-2.06-9.38-4.19-14.43-4.94-1.46-.22-4.22-.8-5.31.69S959.24,506.77,959,508.54Z" transform="translate(-52.64 -55.65)" fill="#fbbebe"/><path d="M959,508.54c-.44,3.83-4.16,6.37-7.71,7.88l22.46,7.18c-1.45-2.78-.27-6.18,1.17-9a50.81,50.81,0,0,1,4-6.4c.22-.31.47-.71.31-1.06a1.12,1.12,0,0,0-.63-.49c-4.62-2.06-9.38-4.19-14.43-4.94-1.46-.22-4.22-.8-5.31.69S959.24,506.77,959,508.54Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><circle cx="919.45" cy="438.38" r="17.62" fill="#fbbebe"/><path d="M987.31,639c-1.7,2-4.71,2.44-7.34,2-6.15-1-11.19-5.53-17.2-7.16-7.38-2-15.2.57-22.16,3.71s-13.9,6.95-21.52,7.55c6.89-15.36,2.95-33.85,9.53-49.34,1.62-3.78,3.87-7.44,4.23-11.53.45-5.09-2.09-10-2.48-15.07-.36-4.51,1-9,2.29-13.29l4.83-15.85c1.8-5.9,3.65-11.91,7.14-17,1.28-1.87,1.68-5.65,3.64-6.79,4.28-2.46,10.77.65,15.41.82s8.87,3.47,11.58,7.2c2.87,3.94,4.5,8.63,6,13.27a125.22,125.22,0,0,1,5.34,21.23c.15,1.08.28,2.16.38,3.25,1,10.27-.46,20.6-.54,30.91,0,0,0,0,0,.08-.09,13.08,2,26.09,2.4,39.17C988.9,634.59,988.84,637.2,987.31,639Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M986.44,593c-3.45-.87-6.58-3.09-9.25-5.53a39,39,0,0,1-6.14-6.95,50.28,50.28,0,0,1-5.29-11.16,129.34,129.34,0,0,1-7-32.28c-.26-3-.34-6.21,1.45-8.59a7.23,7.23,0,0,1,8.74-2.13c2.37,1.16,3.9,3.49,5.31,5.71,2.82,4.43,5.65,8.9,7.46,13.82.8,2.18,1.39,4.43,2.17,6.62a61,61,0,0,0,2.74,6.33c.15,1.08.28,2.16.38,3.25C987.94,572.31,986.52,582.64,986.44,593Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M960.25,526.55c-1.78,2.37-1.7,5.62-1.45,8.59a129.28,129.28,0,0,0,7,32.27,49.43,49.43,0,0,0,5.29,11.16,38.67,38.67,0,0,0,6.14,6.95c3.4,3.11,7.54,5.88,12.15,5.92a23.42,23.42,0,0,0,7.56-1.65l37.51-12.74c.38-2.15-1.13-4.64-1.84-6.7l-2-5.94c-.57-1.63-1.24-3.4-2.72-4.29s-3.85.33-5.09,1.32-2.19,2.29-3.4,3.3c-2.32,1.92-5.37,2.63-8.3,3.29l-8.43,1.9a35.6,35.6,0,0,1-9.38,1.24c-2.35-7.17-6.82-13.49-9.35-20.61-.78-2.18-1.38-4.43-2.18-6.61-1.81-4.92-4.64-9.39-7.45-13.82-1.41-2.23-2.94-4.56-5.31-5.71A7.25,7.25,0,0,0,960.25,526.55Z" transform="translate(-52.64 -55.65)" fill="#f86d70"/><path d="M966.47,498a8.9,8.9,0,0,1,1.06-3.29,2.79,2.79,0,0,1,3-1.33c1.76.55,2.35,3.28,4.19,3.37,1.24.06,2.13-1.2,2.52-2.39s.6-2.52,1.53-3.36a5.61,5.61,0,0,1,2.18-1c4.27-1.35,8.68-3.85,10.1-8.1a4.9,4.9,0,0,0,.23-2.7c-.65-2.69-4-3.5-6.55-4.48s-5-2.62-7.5-3.88-5.4-2.16-8.15-1.52a40.67,40.67,0,0,0-4.11,1.54c-4.14,1.43-9.22.77-12.43,3.75a13,13,0,0,0-3,5.25c-2.25,6.21-3.66,13.6.05,19.06,2.28,3.37,4.51,7.22,8.91,7.62C963.62,507,965.66,502.32,966.47,498Z" transform="translate(-52.64 -55.65)" fill="#2f2e41"/><g opacity="0.1"><path d="M974.29,492.24c.39-1.18.6-2.52,1.52-3.36a5.83,5.83,0,0,1,2.18-1c4.28-1.35,8.68-3.84,10.11-8.1a4.89,4.89,0,0,0,.22-2.7,3.39,3.39,0,0,0-.53-1.19c1.63.7,3.05,1.65,3.45,3.31a4.9,4.9,0,0,1-.23,2.7c-1.42,4.25-5.83,6.75-10.1,8.1a5.61,5.61,0,0,0-2.18,1c-.93.84-1.14,2.18-1.53,3.36s-1.28,2.45-2.52,2.39-1.84-1.19-2.63-2.14C973.14,494.47,973.92,493.33,974.29,492.24Z" transform="translate(-52.64 -55.65)"/><path d="M955.57,504.4c5.14.47,7.17-4.2,8-8.52a8.9,8.9,0,0,1,1.06-3.29,2.78,2.78,0,0,1,3-1.32c.95.29,1.56,1.23,2.21,2a3,3,0,0,0-2.26,1.4,8.9,8.9,0,0,0-1.06,3.29c-.81,4.32-2.85,9-8,8.52a8.2,8.2,0,0,1-5.28-2.73A7.45,7.45,0,0,0,955.57,504.4Z" transform="translate(-52.64 -55.65)"/></g><path d="M223.42,705.94l19.14-11.26a46.29,46.29,0,0,0-9.79-11.7l-36.11,11.23L225,677.57a46,46,0,0,0-68.1,40.35c0,25.4,20.59,26.29,46,26.29s46-.89,46-26.29a45.71,45.71,0,0,0-4-18.66Z" transform="translate(-52.64 -55.65)" fill="#908820"/><path d="M228.29,730.64a35.81,35.81,0,0,0-6.78-5.33c-3.48-2.31-7.07-4.67-11.17-5.45-3.6-.69-7.31-.11-11-.31a25.65,25.65,0,0,1-16.81-7.71c-2.61-2.7-4.67-6-7.81-8.09-3.39-2.22-7.69-2.7-11.71-2.15a31.2,31.2,0,0,0-3.46.71,45.9,45.9,0,0,0-2.72,15.61c0,25.4,20.59,26.29,46,26.29,12.33,0,23.52-.21,31.78-3.33l-1.38-2.58A36.08,36.08,0,0,0,228.29,730.64Z" transform="translate(-52.64 -55.65)" opacity="0.1"/><path d="M636.6,770.25l-25.39-14.93a61.14,61.14,0,0,1,13-15.52l47.89,14.89-37.53-22.06a61,61,0,0,1,90.31,53.5c0,33.69-27.31,34.86-61,34.86s-61-1.17-61-34.86a60.84,60.84,0,0,1,5.23-24.75Z" transform="translate(-52.64 -55.65)" fill="#908820"/><path d="M630.13,803a47.57,47.57,0,0,1,9-7.07c4.61-3.06,9.38-6.18,14.81-7.22,4.77-.91,9.69-.14,14.54-.42a34.09,34.09,0,0,0,22.3-10.21c3.46-3.59,6.19-8,10.36-10.73,4.49-3,10.19-3.59,15.52-2.85a38.27,38.27,0,0,1,4.6.94,60.88,60.88,0,0,1,3.6,20.69c0,33.69-27.31,34.86-61,34.86-16.35,0-31.19-.27-42.15-4.41l1.83-3.42C625.45,809.59,627.39,806,630.13,803Z" transform="translate(-52.64 -55.65)" opacity="0.1"/></svg>
<svg id="ada6f7ae-8394-4767-8700-18704e9e9034" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="974" height="805.02" viewBox="0 0 974 805.02"><title>note list</title><path d="M1087,816.24v5a19.11,19.11,0,0,1-20.91,19c-39.15-3.72-78.12-10.64-117.4-9.43-72.42,2.21-145.44,31.83-215.54,13.53-19.77-5.16-39.32-14.13-59.66-12.2-18.25,1.73-34.49,12.05-52.17,16.89C567.55,863.79,512.8,826.49,457.06,828c-36.74,1-71.92,18.89-108.62,16.9-21.27-1.14-41.53-8.92-62.28-13.71-51.66-11.94-104.86-5.27-158.09-1.13A14,14,0,0,1,113,816.13v-4.28a14,14,0,0,1,14-14l940.92-.76A19.11,19.11,0,0,1,1087,816.24Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><path d="M1087,816.24v5a19.11,19.11,0,0,1-20.91,19c-39.15-3.72-78.12-10.64-117.4-9.43-72.42,2.21-145.44,31.83-215.54,13.53-19.77-5.16-39.32-14.13-59.66-12.2-18.25,1.73-34.49,12.05-52.17,16.89C567.55,863.79,512.8,826.49,457.06,828c-36.74,1-71.92,18.89-108.62,16.9-21.27-1.14-41.53-8.92-62.28-13.71-51.66-11.94-104.86-5.27-158.09-1.13A14,14,0,0,1,113,816.13v-4.28a14,14,0,0,1,14-14l940.92-.76A19.11,19.11,0,0,1,1087,816.24Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M1087,807.1v5a20.39,20.39,0,0,1-.11,2.08,19.11,19.11,0,0,1-20.8,17c-39.15-3.73-78.12-10.65-117.4-9.44-72.42,2.22-145.44,31.84-215.54,13.53-19.77-5.16-39.32-14.13-59.66-12.2-18.25,1.73-34.49,12.05-52.17,16.9-53.77,14.72-108.52-22.58-164.26-21.08-36.74,1-71.92,18.89-108.62,16.91-21.27-1.15-41.53-8.92-62.28-13.72-51.65-11.93-104.86-5.26-158.09-1.12a14,14,0,0,1-14.86-11.5A13.7,13.7,0,0,1,113,807v-4.28a14,14,0,0,1,14-14l235.83-.19,481.81-.4,223.28-.18A19.11,19.11,0,0,1,1087,807.1Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><path d="M856.59,791c0,2.21-4.36,4.33-12.4,6.32C812,805.27,721,811,613.59,811c-105.67,0-195.58-5.55-229-13.31-9-2.09-14-4.34-14-6.69,0-.82.6-1.63,1.78-2.43l481.81-.4C855.77,789.1,856.59,790,856.59,791Z" transform="translate(-113 -47.49)" opacity="0.1"/><g opacity="0.5"><rect x="552" width="402" height="121" rx="19.03" fill="#36334a"/><rect x="601" y="26" width="93" height="21" fill="#908820"/><rect x="601" y="60" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="601" y="84" width="114" height="11" fill="#908820" opacity="0.3"/></g><g opacity="0.5"><rect x="48" y="98.51" width="402" height="121" rx="19.03" fill="#36334a"/><rect x="97" y="124.51" width="93" height="21" fill="#36334a"/><rect x="97" y="158.51" width="285" height="11" fill="#36334a"/><rect x="97" y="182.51" width="114" height="11" fill="#36334a"/><rect x="48" y="98.51" width="402" height="121" rx="19.03" fill="#36334a"/><rect x="97" y="124.51" width="93" height="21" fill="#908820"/><rect x="97" y="158.51" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="97" y="182.51" width="114" height="11" fill="#908820" opacity="0.3"/></g><rect x="667.58" y="211.54" width="10.31" height="85.02" rx="2.29" fill="#3f3d56"/><rect x="324.76" y="144.93" width="5.79" height="27.99" rx="1.5" fill="#3f3d56"/><rect x="324.55" y="196.17" width="6.52" height="48.72" rx="1.69" fill="#3f3d56"/><rect x="324.65" y="261.94" width="6.21" height="49.14" rx="1.61" fill="#3f3d56"/><rect x="328.04" y="52.43" width="345.1" height="701.37" rx="35.69" fill="#3f3d56"/><rect x="467.3" y="73.38" width="48.19" height="9.79" rx="2.54" fill="#e6e8ec"/><circle cx="528.33" cy="78.27" r="5.55" fill="#e6e8ec"/><path d="M764.83,145.6v610A27.41,27.41,0,0,1,737.42,783H489.76a27.41,27.41,0,0,1-27.41-27.4v-610a27.41,27.41,0,0,1,27.41-27.41h37.06V123a22.58,22.58,0,0,0,22.57,22.57h126A22.57,22.57,0,0,0,697.91,123v-4.76h39.51A27.41,27.41,0,0,1,764.83,145.6Z" transform="translate(-113 -47.49)" fill="#36334a"/><rect x="358" y="129" width="31" height="4" fill="#3f3d56"/><rect x="358" y="136" width="31" height="4" fill="#3f3d56"/><rect x="358" y="143" width="31" height="4" fill="#3f3d56"/><rect x="358" y="129" width="31" height="4" fill="#3f3d56"/><rect x="358" y="136" width="31" height="4" fill="#3f3d56"/><rect x="358" y="143" width="31" height="4" fill="#3f3d56"/><path d="M750.66,186.49h-.91l-.35-.29a7.61,7.61,0,0,0,1.78-4.89,7.44,7.44,0,1,0-7.4,7.48,7.73,7.73,0,0,0,4.88-1.78l.34.29v.92l5.74,5.75,1.72-1.72Zm-6.88,0a5.18,5.18,0,1,1,5.16-5.18A5.15,5.15,0,0,1,743.78,186.49Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><rect x="358" y="200" width="93" height="21" fill="#908820"/><rect x="358" y="234" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="358" y="258" width="114" height="11" fill="#908820" opacity="0.3"/><rect x="358" y="444" width="93" height="21" fill="#908820"/><rect x="358" y="478" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="358" y="502" width="114" height="11" fill="#908820" opacity="0.3"/><rect x="358" y="566" width="93" height="21" fill="#908820"/><rect x="358" y="600" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="358" y="624" width="114" height="11" fill="#908820" opacity="0.3"/><circle cx="592" cy="676" r="34" fill="#908820"/><path d="M722.33,720.81v16.43a1.25,1.25,0,0,1-1.25,1.25h-33.5a1.25,1.25,0,0,1-1.25-1.25V711.07a1.25,1.25,0,0,1,1.25-1.25h23.31" transform="translate(-113 -47.49)" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><line x1="579.67" y1="672" x2="602.33" y2="672" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><line x1="580" y1="676.67" x2="602.67" y2="676.67" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><line x1="580.33" y1="681.33" x2="603" y2="681.33" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><line x1="605" y1="661" x2="605" y2="672.33" fill="none" stroke="#f0f" stroke-miterlimit="10"/><line x1="610.67" y1="666.67" x2="599.33" y2="666.67" fill="none" stroke="#f0f" stroke-miterlimit="10"/><line x1="605" y1="661" x2="605" y2="672.33" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><line x1="610.67" y1="666.67" x2="599.33" y2="666.67" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10"/><path d="M1001.6,792.66c3-5.51-.4-12.27-4.29-17.18s-8.61-10-8.51-16.29c.15-9,9.7-14.31,17.33-19.09a84,84,0,0,0,15.56-12.51,22.8,22.8,0,0,0,4.78-6.4c1.58-3.52,1.54-7.52,1.44-11.37q-.51-19.26-1.91-38.49" transform="translate(-113 -47.49)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="4"/><path d="M1040.51,670.63a14,14,0,0,0-7-11.5l-3.14,6.22.1-7.53a14.22,14.22,0,0,0-4.63-.56,14,14,0,1,0,14.68,13.37Z" transform="translate(-113 -47.49)" fill="#57b894"/><path d="M1015.48,765.62a14,14,0,1,1,.68-11.3l-8.77,7.13,9.65-2.23A14,14,0,0,1,1015.48,765.62Z" transform="translate(-113 -47.49)" fill="#57b894"/><path d="M1008.55,738.37a14,14,0,0,1-4.45-27.53l-.08,5.78,3.18-6.29h0a14,14,0,0,1,14.67,13.36,13.84,13.84,0,0,1-.6,4.79A14,14,0,0,1,1008.55,738.37Z" transform="translate(-113 -47.49)" fill="#57b894"/><path d="M1042.62,715.7a14,14,0,1,1,6.21-26.27l-2.48,6.8,5.1-4.9A14,14,0,0,1,1056,701a13.79,13.79,0,0,1-.35,3.87A14,14,0,0,1,1042.62,715.7Z" transform="translate(-113 -47.49)" fill="#57b894"/><path d="M1038.62,674.37c-3.24.35-6.39,1.36-9.64,1.56s-6.82-.57-8.88-3.1c-1.1-1.36-1.66-3.08-2.59-4.57a10,10,0,0,0-3.54-3.33,14,14,0,1,0,26.24,9.32Q1039.42,674.28,1038.62,674.37Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M1042.62,715.7a14,14,0,0,1-13.35-20,10.37,10.37,0,0,1,2.82,2.82c1,1.51,1.61,3.26,2.78,4.64,2.19,2.57,5.92,3.41,9.31,3.26s6.66-1.12,10-1.43c.47,0,.94-.07,1.42-.08A14,14,0,0,1,1042.62,715.7Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M1008.55,738.37a14,14,0,0,1-13.46-19.76,11.48,11.48,0,0,1,3,2.85c1.09,1.54,1.77,3.32,3.05,4.74,2.37,2.63,6.35,3.56,9.93,3.48s6.83-.93,10.28-1.2A14,14,0,0,1,1008.55,738.37Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M1015.48,765.62a14,14,0,0,1-25.59-11.45,13.84,13.84,0,0,1,3.08,2.75c1.34,1.62,2.22,3.47,3.76,5,2.87,2.82,7.5,4,11.63,4.09A60,60,0,0,0,1015.48,765.62Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M980.43,786.31s11.08-.34,14.42-2.72,17-5.21,17.86-1.4,16.65,19,4.15,19.06-29.06-1.94-32.4-4S980.43,786.31,980.43,786.31Z" transform="translate(-113 -47.49)" fill="#656380"/><path d="M1017.08,799.93c-12.51.1-29.06-1.95-32.39-4-2.54-1.55-3.55-7.09-3.89-9.65h-.37s.7,8.94,4,11,19.89,4.07,32.4,4c3.61,0,4.85-1.31,4.78-3.21C1021.14,799.19,1019.77,799.9,1017.08,799.93Z" transform="translate(-113 -47.49)" opacity="0.2"/><path d="M755,378.52v82.94a19,19,0,0,1-19,19H462.35v-121H736A19,19,0,0,1,755,378.52Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><rect x="208" y="295" width="402" height="121" rx="19.03" fill="#f2f2f2"/><rect x="208" y="295" width="402" height="121" rx="19.03" fill="#f2f2f2"/><rect x="257" y="321" width="93" height="21" fill="#908820"/><rect x="257" y="355" width="285" height="11" fill="#908820" opacity="0.3"/><rect x="257" y="379" width="114" height="11" fill="#908820" opacity="0.3"/><circle cx="92.34" cy="246.84" r="35.75" fill="#3f3d56"/><path d="M184.6,572.43c3.69,25.89-.81,52.29-7.82,77.48-1.31,4.69-2.72,9.42-5.17,13.63s-5.86,7.83-8.74,11.79c-6,8.26-9.52,18-12.81,27.64a779.65,779.65,0,0,0-22.82,81.32,5.57,5.57,0,0,0,0,3.47c.87,2,3.51,2.26,5.68,2.25l28.06-.15c1-1.92-.85-4-2-5.84-4-6.22-.16-14.3,3.37-20.81a223.74,223.74,0,0,0,19.78-50c1.92-7.33,3.48-14.82,6.6-21.73,3.77-8.38,9.68-15.59,14.48-23.43,5.58-9.11,9.65-19,13.69-28.93,5.64-13.78,11.33-27.73,13.55-42.45,2.09-13.82,1.06-27.9-.3-41.81a2.64,2.64,0,0,0-3.06-3l-37.65-1.94c-7.76-.4-9.6-2.1-8.42,5.52C181.84,561.13,183.78,566.67,184.6,572.43Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><path d="M184.6,572.43c3.69,25.89-.81,52.29-7.82,77.48-1.31,4.69-2.72,9.42-5.17,13.63s-5.86,7.83-8.74,11.79c-6,8.26-9.52,18-12.81,27.64a779.65,779.65,0,0,0-22.82,81.32,5.57,5.57,0,0,0,0,3.47c.87,2,3.51,2.26,5.68,2.25l28.06-.15c1-1.92-.85-4-2-5.84-4-6.22-.16-14.3,3.37-20.81a223.74,223.74,0,0,0,19.78-50c1.92-7.33,3.48-14.82,6.6-21.73,3.77-8.38,9.68-15.59,14.48-23.43,5.58-9.11,9.65-19,13.69-28.93,5.64-13.78,11.33-27.73,13.55-42.45,2.09-13.82,1.06-27.9-.3-41.81a2.64,2.64,0,0,0-3.06-3l-37.65-1.94c-7.76-.4-9.6-2.1-8.42,5.52C181.84,561.13,183.78,566.67,184.6,572.43Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M192,509.48c-7.65,11-15.67,23.66-13.13,36.83,1.93,10,9.55,17.75,16.74,25,28.74,28.86,57,60.92,66.78,100.47,2.52,10.22,3.74,20.71,5.91,31,3.94,18.7,11,36.58,18,54.35l18.72,47.38a28.36,28.36,0,0,0,27.1-18c-2.46-.19-5.67-2.57-7.11-4.57-8-11.07-9.64-25.31-11.69-38.81-2.27-14.92-5.3-29.72-8.34-44.51L291.58,633.2c-1.55-7.55-3.1-15.11-5.12-22.54-7.82-28.83-22.64-56-24.93-85.81-.34-4.38-.55-9.19-3.52-12.44-3.12-3.43-8.24-4-12.87-4.27C226.41,506.9,210.25,505.07,192,509.48Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><path d="M141.47,783.62a29.86,29.86,0,0,0-6.22-.8,12.92,12.92,0,0,0-10.16,5.55,25.25,25.25,0,0,0-2.73,6l-2.71,7.78c-.8,2.28-1.58,4.91-.39,7A7.42,7.42,0,0,0,122.9,812c6.56,2.92,13.95,3.08,21.1,3.78,6.94.68,13.82,1.9,20.67,3.19,2.36.44,4.71.89,7.1,1.13,5.34.54,10.72,0,16.06-.48l5.51-.53a19.84,19.84,0,0,0,5.42-1,6.83,6.83,0,0,0,4-3.59c1.36-3.37-1.56-7.36-5.13-8-1.45-.29-2.95-.16-4.42-.35a20.88,20.88,0,0,1-4.84-1.45c-7.05-2.77-14.17-5.57-20.37-9.93-1.72-1.21-3.52-2.9-3.33-5a10.19,10.19,0,0,1,.89-2.59,9.46,9.46,0,0,0,.08-6.69c-.34-.92-1-1.89-1.93-2-2.64-.32-2.32,3.53-2.71,5-.53,2-1.49,2.18-3.37,2C152.23,784.91,146.83,784.64,141.47,783.62Z" transform="translate(-113 -47.49)" fill="#f2f2f2"/><path d="M299.34,810.45a17.65,17.65,0,0,0,3.82,10.75,8.36,8.36,0,0,0,2.72,2.19,10.92,10.92,0,0,0,4.95.79,40.05,40.05,0,0,0,13.89-2.81,39.7,39.7,0,0,1,4.8-1.81,53.88,53.88,0,0,1,6.24-.9c8.27-1.19,15.53-6.07,23.52-8.54a49.45,49.45,0,0,0,7-2.3c3.16-1.49,5.72-4,8.23-6.43a19.89,19.89,0,0,0,3.78-4.52c1.47-2.74,1.56-6,1.62-9.1a2.59,2.59,0,0,0-.24-1.41,2.1,2.1,0,0,0-.9-.71c-2.43-1.2-5.31-.6-8-.13-9.72,1.73-19.67,1.41-29.54,1.09a4.29,4.29,0,0,1-4.54-2.59l-3.75-6c-1.07-1.72-2.77-3.69-4.71-3.1-1.55.47-2.18,2.37-2.12,4s.56,3.25.25,4.84a6.61,6.61,0,0,1-3.2,4.16,20.44,20.44,0,0,1-5,1.92c-5.52,1.56-11.94,3.33-16.21,7.39C298.86,800.15,299.15,806.6,299.34,810.45Z" transform="translate(-113 -47.49)" fill="#f2f2f2"/><path d="M297.4,413.75a23.17,23.17,0,0,0,5.46-5.94,24.21,24.21,0,0,0,2.33-8.21l1.57-9.92c.87-5.47,1.89-11.26,5.54-15.43s10.92-5.66,14.63-1.54c1.62,1.79,2.26,4.25,2.66,6.64a48.34,48.34,0,0,1-11.82,40c-4.49,4.92-10,8.83-14.75,13.48-4.14,4-7.73,8.59-11.55,12.94A159.42,159.42,0,0,1,260,473.27c.14-7.44,1.09-15.42,0-22.81-.81-5.34-3-8.22,2.17-11.71C274.17,430.59,286.28,423.21,297.4,413.75Z" transform="translate(-113 -47.49)" fill="#fbbebe"/><path d="M196.56,340.57c-.39,5.32-2.6,10.76-7,13.83q18.69,6.54,36.87,14.42c-2.34-2.25-3.05-5.7-3.16-8.94-.31-8.47,2.62-16.8,6.9-24.1a5.85,5.85,0,0,0,1.13-3.18c-.14-2.36-2.94-3.45-5.24-4a128.86,128.86,0,0,0-23.92-3.47c-3.45-.11-7.54-1.2-6.76,3.19C196.12,332.42,196.87,336.42,196.56,340.57Z" transform="translate(-113 -47.49)" fill="#fbbebe"/><circle cx="107.66" cy="266.7" r="29.23" fill="#fbbebe"/><path d="M249.6,393.14a35.36,35.36,0,0,0-3.71-6.22c-5.3-7.61-10.74-15.21-17.54-21.51a109.19,109.19,0,0,0-16.65-12.23,37.79,37.79,0,0,0-10.42-5,40.77,40.77,0,0,0-6.69-.91l-7.59-.62a6.21,6.21,0,0,0-2,.06c-2.06.52-3,3.08-2.56,5.15s2,3.75,3.38,5.32c.34,15.49-6.55,30.77-7.48,46.24-1.63,27,14.88,52.21,16.2,79.28a8.55,8.55,0,0,1-.38,3.63c-.22.56-.55,1.07-.8,1.62-.78,1.7-.75,3.63-.89,5.5a27.94,27.94,0,0,1-8.23,17.62,12.23,12.23,0,0,0,4.64,3.54c3.19,1.39,7,1.51,10.39,2.48,8.84,2.58,17.9,5.18,27.09,4.63a23.9,23.9,0,0,0,2.09-7.87c1.3-.81,2.8.92,3,2.44s-.09,3.29,1,4.39,2.47.93,3.8.76c5.61-.71,11-2.6,16.5-3.87s11.38-1.88,16.73-.06c1.08-1.37.62-3.4-.26-4.9s-2.13-2.82-2.63-4.49a10.08,10.08,0,0,1-.32-2.95c0-8.55,1.76-17.19.09-25.58-.49-2.46-1.27-4.86-1.77-7.33-1-4.79-.85-9.73-.72-14.62s-.46-10.18-.56-15.14a48.29,48.29,0,0,0-.4-6.13c-.34-2.13-1-4.21-1.18-6.36-.54-5.46,1.66-10.88,1.33-16.36C262.51,405.33,252.84,400.81,249.6,393.14Z" transform="translate(-113 -47.49)" fill="#ff6584"/><path d="M183.63,389.22c-7.08,18.78-10.54,38.7-14,58.48l-8,46.46c-2.27,13.13-4.55,26.34-4.46,39.66,0,3.74.24,7.55-.74,11.16-1.28,4.74-4.46,8.69-6.87,13a42.78,42.78,0,0,0-4.6,28.93c.82,4.15,3.06,8.86,7.27,9.34,3.46.38,6.45-2.37,8.47-5.21,4.64-6.51,6.94-14.37,9-22.11a500.11,500.11,0,0,0,13.87-77.09,75.06,75.06,0,0,1,1.39-9.8,69.54,69.54,0,0,1,3.38-9.76q4.59-11.38,9.19-22.76c7.07-17.53,14.18-35.14,23.89-51.36-9.6-2.76-19.59-6-29.19-8.73C189.46,388.61,186.49,389.1,183.63,389.22Z" transform="translate(-113 -47.49)" fill="#fbbebe"/><path d="M200.27,403.46c-6.72-1.45-13.67-1-20.53-1.17a4.43,4.43,0,0,1-2.76-.72c-1.39-1.19-.8-3.42-.1-5.11l3.32-8a11.64,11.64,0,0,1,2.66-4.42c1.69-1.5,4.07-1.87,6.31-2.14,7.29-.84,14.79-1.12,21.86.85s13.72,6.45,16.88,13.08c1.32,2.76,1.93,6.24.18,8.75-2.6,3.73-8.43,7.9-12.78,5.86C210.1,408,206.11,404.73,200.27,403.46Z" transform="translate(-113 -47.49)" fill="#ff6584"/><path d="M236,268.79a29.52,29.52,0,0,0-16.28,4.69,35.75,35.75,0,0,0-35.1-8.22A20.43,20.43,0,1,0,161,288.91,35.75,35.75,0,1,0,229.57,309a32,32,0,0,0,6.41.65c14.1,0,25.54-9.15,25.54-20.43S250.08,268.79,236,268.79Z" transform="translate(-113 -47.49)" fill="#3f3d56"/><path d="M241.09,299.43a31.1,31.1,0,0,1-6.41-.65A35.57,35.57,0,0,1,230,309.07a32.11,32.11,0,0,0,6,.58c13.18,0,24-8,25.39-18.24C256.7,296.29,249.35,299.43,241.09,299.43Z" transform="translate(-113 -47.49)" opacity="0.1"/><path d="M200.23,325a35.8,35.8,0,0,1-34.18-46.28,20.43,20.43,0,0,1-16.89-20.12,20.09,20.09,0,0,1,.33-3.66,20.43,20.43,0,0,0,11.46,34,35.75,35.75,0,0,0,68.29,21.18A35.68,35.68,0,0,1,200.23,325Z" transform="translate(-113 -47.49)" opacity="0.1"/></svg>
<svg id="f9ea829a-01ef-4860-a3a7-641ca853f294" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1082.19" height="847.94" viewBox="0 0 1082.19 847.94"><defs><linearGradient id="0bbd7947-ca61-424a-a753-fb567ef3345a" x1="276.42" y1="818" x2="276.42" y2="60" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient><linearGradient id="7762d21b-e061-4c19-a9a4-793dedfcbc4f" x1="891.13" y1="675.02" x2="891.13" y2="214.93" gradientTransform="matrix(-1, 0, 0, 1, 1114, 0)" xlink:href="#0bbd7947-ca61-424a-a753-fb567ef3345a"/></defs><title>online</title><path d="M968.1,62.19c37.27,12.37,51.62,19.46,88.51,32.93,15.11,5.52,30.64,11.41,42.27,22.52,12.21,11.67,18.87,27.95,23.58,44.17,10.1,34.82,12.19,74.1-5.42,105.79-16.11,29-47.49,49.78-54.54,82.18-5.2,23.89,4.26,48.17,8.9,72.17a184.8,184.8,0,0,1-6.35,93.79c-9.69,28.64-28.59,56.43-57.16,66.3-33.23,11.47-69.43-3.86-104.57-2.76-39.43,1.24-75.24,23.14-105.67,48.24S739.76,681.76,706,702.07c-34.43,20.69-73.83,31.26-112.65,41.6-21.59,5.75-43.41,11.53-65.74,12.27-24.69.82-49.33-4.61-72.37-13.54-42.71-16.54-80.4-45.17-109.34-80.67-16.84-20.65-31.41-47.31-24.05-72.91,5.35-18.6,21-32,33-47.17,31.94-40.17,38.93-98.64,17.36-145.21-10.93-23.6-28.17-43.58-43.46-64.62s-29.13-44.43-31.5-70.33,9.64-54.65,33.65-64.64c29.45-12.24,71.25,3.84,90.26-21.78,7.2-9.71,8.12-22.5,10.32-34.38C443.19,77.71,503,27.15,567,26.05c49.92-.86,97,24.71,146.85,27.07C763.94,55.49,814.81,34.39,863.31,47,881.1,51.69,897.48,60.69,915.1,66s38,6.31,53.18-4.07" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><rect x="492.93" y="182.79" width="437.49" height="30.27" fill="#908820" opacity="0.1"/><rect x="492.93" y="313.49" width="437.49" height="30.27" fill="#908820" opacity="0.1"/><rect x="492.93" y="449.69" width="437.49" height="30.27" fill="#908820" opacity="0.1"/><rect x="655.27" y="104.38" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="655.27" y="116.76" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="655.27" y="159.41" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="696.55" y="104.38" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="696.55" y="116.76" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="696.55" y="159.41" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="737.82" y="104.38" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="737.82" y="116.76" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="737.82" y="159.41" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="779.09" y="104.38" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="779.09" y="116.76" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="779.09" y="159.41" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="840.74" y="397.3" width="27.51" height="78.42" transform="translate(-163.91 403.24) rotate(-26.63)" fill="#908820" opacity="0.1"/><rect x="829.95" y="412.24" width="27.51" height="5.5" transform="translate(-155.41 396.12) rotate(-26.63)" fill="#908820" opacity="0.1"/><rect x="849.07" y="450.36" width="27.51" height="5.5" transform="translate(-170.47 408.73) rotate(-26.63)" fill="#908820" opacity="0.1"/><rect x="785.97" y="236.45" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="785.97" y="248.83" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="785.97" y="291.48" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="827.24" y="236.45" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="827.24" y="248.83" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="827.24" y="291.48" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="868.51" y="236.45" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="868.51" y="248.83" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="868.51" y="291.48" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="531.46" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="531.46" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="531.46" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="572.73" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="572.73" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="572.73" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="614" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="614" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="614" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="655.27" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="655.27" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="655.27" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="696.55" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="696.55" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="696.55" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="737.82" y="371.27" width="27.51" height="78.42" fill="#908820" opacity="0.1"/><rect x="737.82" y="383.65" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><rect x="737.82" y="426.3" width="27.51" height="5.5" fill="#908820" opacity="0.1"/><g opacity="0.1"><path d="M756,312.39s-8.34-2.83-12.36,4.52a13.41,13.41,0,0,0,.18,12.91c2.64,4.77,8.07,11,18.37,10.41,8.73-.47,11.64-7.13,12.43-13.33a13.7,13.7,0,0,0-18.19-14.66Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M745.56,330.48a13.41,13.41,0,0,1-.18-12.91c4-7.35,12.36-4.52,12.36-4.52l.43-.15a13.57,13.57,0,0,1,9.8.22,13.58,13.58,0,0,0-11.57-1.1l-.43.15s-8.34-2.83-12.36,4.52a13.41,13.41,0,0,0,.18,12.91,20,20,0,0,0,9.43,8.93A20.68,20.68,0,0,1,745.56,330.48Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M751.67,315.92s3.09,2.43,10.61,0" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.2"/><path d="M751,298.25s5.3,14.36,4.86,18.12l4.2-.44s-7.07-12.59-5.3-17.68Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M751,298.25s5.3,14.36,4.86,18.12l4.2-.44s-7.07-12.59-5.3-17.68Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.2"/><path d="M772.65,299.45s-8.35-4.27-12.08-.74-4.31,12.54-4.31,12.54S769.87,313.12,772.65,299.45Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M772.65,299.45s-8.35-4.27-12.08-.74-4.31,12.54-4.31,12.54S769.87,313.12,772.65,299.45Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M756.26,311.25s2.19-10.46,16.39-11.8" transform="translate(-58.9 -26.03)" fill="#908820"/></g><g opacity="0.1"><path d="M932.07,448.59s-8.34-2.83-12.36,4.52a13.41,13.41,0,0,0,.18,12.91c2.64,4.77,8.07,11,18.37,10.41,8.73-.47,11.64-7.13,12.43-13.33a13.7,13.7,0,0,0-18.19-14.66Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M921.66,466.68a13.41,13.41,0,0,1-.18-12.91c4-7.35,12.36-4.52,12.36-4.52l.43-.15a13.57,13.57,0,0,1,9.8.22,13.58,13.58,0,0,0-11.57-1.1l-.43.15s-8.34-2.83-12.36,4.52a13.41,13.41,0,0,0,.18,12.91,20,20,0,0,0,9.43,8.93A20.68,20.68,0,0,1,921.66,466.68Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M927.76,452.12s3.09,2.43,10.61,0" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.2"/><path d="M927.1,434.45s5.3,14.36,4.86,18.12l4.2-.44s-7.07-12.59-5.3-17.68Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M927.1,434.45s5.3,14.36,4.86,18.12l4.2-.44s-7.07-12.59-5.3-17.68Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.2"/><path d="M948.74,435.65s-8.35-4.27-12.08-.74-4.31,12.54-4.31,12.54S946,449.32,948.74,435.65Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M948.74,435.65s-8.35-4.27-12.08-.74-4.31,12.54-4.31,12.54S946,449.32,948.74,435.65Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M932.36,447.44s2.19-10.46,16.39-11.8" transform="translate(-58.9 -26.03)" fill="#908820"/></g><path d="M92.56,775S225.1,902.19,591.1,868.19s512-12,512-12,66-64-23-93-313-17-383-17-215-35-215-35Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1039.46,807.24s-21.72-110.07,23-185.51c18.77-31.66,27.41-68.35,23.45-104.94a285.89,285.89,0,0,0-10.46-51.23" transform="translate(-58.9 -26.03)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/><path d="M1103.36,417.85c-1.21,13.13-28.49,49-28.49,49s-20.28-40.27-19.08-53.41a23.88,23.88,0,1,1,47.56,4.37Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1136,494.22c-8.15,10.37-50.57,25.68-50.57,25.68s4.86-44.83,13-55.2A23.88,23.88,0,1,1,1136,494.22Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1122.85,618.39c-12.27,4.84-56.61-3.36-56.61-3.36s26.83-36.24,39.1-41.08a23.88,23.88,0,1,1,17.51,44.44Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1091.75,710.1c-11.28,6.84-56.37,6.26-56.37,6.26s20.32-40.26,31.59-47.1a23.88,23.88,0,0,1,24.77,40.84Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1035.25,517.86c8.58,10,51.6,23.53,51.6,23.53s-6.74-44.59-15.32-54.6a23.88,23.88,0,1,0-36.28,31.07Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M1000.25,625.58c11.18,7,56.28,7,56.28,7s-19.78-40.53-31-47.52a23.88,23.88,0,0,0-25.32,40.5Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M980.59,734.95c9.84,8.78,54.28,16.42,54.28,16.42s-12.64-43.28-22.49-52.07a23.88,23.88,0,1,0-31.8,35.64Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M905.94,186l-14.77-6.14,19.76-47.55,14.77,6.14ZM921,128.86a9.31,9.31,0,1,1,12.14-5.08A9.34,9.34,0,0,1,921,128.86Zm40.21,80.08-14.74-6.12,9.62-23.15c2.29-5.52,5.12-12.64-2.45-15.78s-11.35,2.31-13.92,8.51l-9.78,23.54-14.75-6.13,19.76-47.55,14.16,5.89-2.7,6.49.21.09c3.52-2.92,10-4.86,17.16-1.87,14.94,6.21,13.6,17.19,8.29,30l-10.85,26.12Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M595.58,121.14l-5.87,15v61.28h20.86v11.07h11.74l11.07-11.07h17l22.81-22.81V121.14Zm69.76,49.54-13,13H631.43L620.36,194.8V183.73H602.75V129h62.58Zm-13-26.73v22.79h-7.82V144Zm-20.86,0v22.79h-7.82V144Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/><path d="M493.89,695.71l-.47-40.27v-1.18l-6.35-540.39c0-.66,0-1.31-.06-2a53.87,53.87,0,0,0-2.15-12.74c-.1-.33-.19-.65-.29-1s-.2-.61-.31-.92-.26-.77-.4-1.15-.21-.55-.31-.83-.31-.8-.47-1.2l-.33-.78c-.17-.41-.35-.81-.53-1.21l-.35-.74q-.29-.6-.59-1.2l-.37-.72q-.31-.59-.64-1.18l-.39-.7c-.23-.39-.46-.78-.69-1.16l-.41-.67c-.25-.39-.5-.78-.76-1.16L477.6,84c-.28-.41-.57-.81-.86-1.21l-.37-.51q-.63-.85-1.3-1.67A55.29,55.29,0,0,0,428.68,60.1l-274,16.12a55.49,55.49,0,0,0-18.42,4.29C96.48,92.68,58,119.1,58.92,166.86L69.23,725c.85,46.12,39.53,83,84.46,90.81a55.35,55.35,0,0,0,28.09.75l269.61-62.4a54.65,54.65,0,0,0,42.55-53.71Z" transform="translate(-58.9 -26.03)" fill="url(#0bbd7947-ca61-424a-a753-fb567ef3345a)"/><path d="M63.57,173.93,73.66,726.16c.94,51.45,48.94,91.34,99.71,91.34h0L150.66,85.33l-2,.51C106.65,96.45,62.64,122.86,63.57,173.93Z" transform="translate(-58.9 -26.03)" fill="#535461"/><path d="M63.57,173.93,73.66,726.16c.94,51.45,48.94,91.34,99.71,91.34h0L150.66,85.33l-2,.51C106.65,96.45,62.64,122.86,63.57,173.93Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M489.29,701.87A54,54,0,0,1,447.65,755L183.8,816.75a53.93,53.93,0,0,1-66.22-51.56l-.8-45.88v-.46l-2.5-141.19-7.59-430.05-.16-8.59a53.94,53.94,0,0,1,50.73-54.78L425.43,68.29a53.89,53.89,0,0,1,57,50c.09,1.05.13,2.13.14,3.21l6.21,534.69,0,2.76.45,38.25Z" transform="translate(-58.9 -26.03)" fill="#908820"/><path d="M489.29,701.87A54,54,0,0,1,447.65,755L183.8,816.75a53.93,53.93,0,0,1-66.22-51.56l-.8-45.88v-.46l-2.5-141.19-7.59-430.05-.16-8.59a53.94,53.94,0,0,1,50.73-54.78L425.43,68.29a53.89,53.89,0,0,1,57,50c.09,1.05.13,2.13.14,3.21l6.21,534.69,0,2.76.45,38.25Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M489.24,697.2,249,722.77,230.6,109.67,470.82,88.53a53.65,53.65,0,0,1,11.74,33Z" transform="translate(-58.9 -26.03)" fill="#908820"/><polygon points="429.89 632.92 57.87 692.82 55.37 551.64 185.24 534.27 429.89 632.92" fill="#908820"/><polygon points="429.89 632.92 57.87 692.82 55.37 551.64 185.24 534.27 429.89 632.92" fill="#fff" opacity="0.1"/><path d="M489.29,701.87A54,54,0,0,1,447.65,755L183.8,816.75a53.93,53.93,0,0,1-66.22-51.56l-.8-45.88,372-63.13,0,2.76.45,38.25Z" transform="translate(-58.9 -26.03)" fill="#535461"/><path d="M482.41,118.29,106.68,147.61l-.16-8.59a53.94,53.94,0,0,1,50.73-54.78L425.43,68.29a53.89,53.89,0,0,1,57,50Z" transform="translate(-58.9 -26.03)" fill="#535461"/><circle cx="262.48" cy="711.22" r="26.31" fill="#fff"/><polygon points="271.69 78.69 226.96 81.01 226.96 78.38 271.69 76.06 271.69 78.69" fill="#fff"/><rect x="17.66" y="180.71" width="16" height="51.88" rx="8" ry="8" fill="#fff"/><circle cx="215.12" cy="81.01" r="2.63" fill="#fff"/><path d="M134.24,288.67a11.36,11.36,0,0,0,6.7,6.25c1.33.43,2.73.58,4.08.91,6.45,1.61,11,7.19,16.9,10.27a3.25,3.25,0,0,0,1.56.48l.2,0a1.92,1.92,0,0,0,.37,0,3.13,3.13,0,0,0,1.65-.69l.26-.2a19.78,19.78,0,0,1,.29,8c-2.34,0-17.49.93-15.42,18.83,2.25,19.38,24.72,46.52,24.72,46.52s15.73,16.06,10.67,70.89l-.56,26.58s6-.91,13.75-1.82q.27.53.55,1a41.39,41.39,0,0,0,8.3,10.26l12.79,30.39s1.1,2,2.72,5.1c.15,6.44-3.85,12.07-3.85,12.07-24.72,7.2-34.83,33.23-34.83,33.23s-14,8.36-17,17.67c-5.89,4.77-5.49,17.78-5.49,17.78s20.79,11.08,36,17.17a25,25,0,0,0,5.47,1.5l-3.38,7h0c-3.52,1.27-3.78,18.67-3.78,18.67s37.08,26.58,48.31,28.25,20.79-6.09,9-11.63c-9.5-4.46-20.1-21.5-23.86-28,4.7-6.45,19.54-27.35,20.49-35.73C252,589.5,266,581.19,266,581.19c20.22-21.6,6.74-47.08,5.06-60.37,0-.25-.07-.53-.12-.83a145.4,145.4,0,0,0-4.75-17c2.21-13.94,4-35.71,5-50.08,4.46,1.44,8.83,3.13,8.83,3.13,8.2-16.65-19.66-85.2-27.68-104.2,4.68-1.55,12.74-4,24.31-6.56,19.66-4.43,20.79-14.4,20.79-14.4l15.17-41c-1.29.58-3.27.55-5.59.15,0-.34,0-.71,0-1.11.12-3.89.49-10.1,1.63-11.23,0,0-3.73,0-8.26-.4l0-.55,2-25.07s-2.81-27.69-12.36-23.82c-7.67,3.11-6.64,28-5.94,37.69a16.81,16.81,0,0,0,2.13,7l.64,1.14.44.78a9.44,9.44,0,0,1-1.21-.69v8.58c-1.06-.51-1.69-.82-1.69-.82.56,9.42-11.24,41-11.24,41s-20.79-2.22-27.53,2.22c-4.94,3.25-11.4-3-14.49-6.61l14.49-31.6a41.43,41.43,0,0,1-5.17-1.26c.06-.38.14-.79.21-1.21.74-4,2.06-9.56,3.84-10.26a63,63,0,0,0-8-3.66c.06-.32.13-.66.21-1,1.05-5.54,2.7-17.51-.65-25.77-4.49-11.08-6.18-32.68-14.61-27.69-6.69,4-7,27.79-6.85,37.46a16.61,16.61,0,0,0,1.92,7.51l3.25,6.15.45.85-.45.08v7.57c-1.42-.9-2.25-1.47-2.25-1.47-.28,10.65-7.33,24.84-14.22,36.26l-3.5-2.52-.86-.62a2.14,2.14,0,0,1,0-.33s-1.44-.41-3.72-.87c-2.83-5.37-4.41-11.85-5.29-17.23-.09-.56-.17-1.11-.25-1.64a22.35,22.35,0,0,0,2-9.17,22.81,22.81,0,0,0-19.26-22.4c.69-.77,1-1,1.2-1h.15a4.17,4.17,0,0,0,2.54-.68c1.78-.87,3.79-2.16,3.78-4.11,0-1.43-1.14-2.61-2.34-3.42-2.9-1.94-3-5.29-5.68-5a3.58,3.58,0,0,0-1.53.2c-2.47.81-4.62,2.36-7,3.34a16.64,16.64,0,0,1-5.28,1.2,21.85,21.85,0,0,0-3.46.27,10.16,10.16,0,0,0-2.61,1.06c-5.08,2.77-10.93,7-12.56,12.47-1.53,5.14.59,11.81-3.35,15.51a14.5,14.5,0,0,0-2,1.89,6.56,6.56,0,0,0-1,2.52A15.11,15.11,0,0,0,134.24,288.67ZM237,555c.21,2.65-.29,4.53-1.84,5.14-8.43,3.32-18,33.78-18,33.78l-6.39,13.19a170,170,0,0,1-14.53-12.29l.69-4.77s18-13.29,19.1-17.17C216.92,569.75,231.45,559,237,555Z" transform="translate(-58.9 -26.03)" fill="url(#7762d21b-e061-4c19-a9a4-793dedfcbc4f)"/><path d="M249.5,348.37s38.2,88.42,28.93,107.52c0,0-15.28-6-17.47-4.91s-24-114.07-24-114.07Z" transform="translate(-58.9 -26.03)" fill="#c4c8e2"/><path d="M249.5,348.37s38.2,88.42,28.93,107.52c0,0-15.28-6-17.47-4.91s-24-114.07-24-114.07Z" transform="translate(-58.9 -26.03)" opacity="0.05"/><path d="M228.76,630s13.1,24.56,24.56,30,2.18,13.1-8.73,11.46-46.94-27.84-46.94-27.84.25-17.15,3.67-18.4S228.76,630,228.76,630Z" transform="translate(-58.9 -26.03)" fill="#4c495a"/><path d="M194.37,590.15s18.56,18.56,26.74,20.19-7.1,12.55-21.83,6.55S164.35,600,164.35,600s-.55-17.47,8.73-19.1S194.37,590.15,194.37,590.15Z" transform="translate(-58.9 -26.03)" fill="#4c495a"/><path d="M270.78,438.42S267,505,262,514.29s-23.47,37.66-23.47,37.66-21.29,15.28-22.38,19.1S197.65,588,197.65,588l-1.09,7.64s-28.93-1.64-27.29-11.46S186.19,565,186.19,565,196,539.39,220,532.3c0,0,7.64-10.92,1.09-19.65,0,0-16.37-40.93-25.65-45.85s3.82-33.29,3.82-33.29Z" transform="translate(-58.9 -26.03)" fill="#5f5d7e"/><path d="M270.78,438.42S267,505,262,514.29s-23.47,37.66-23.47,37.66-21.29,15.28-22.38,19.1S197.65,588,197.65,588l-1.09,7.64s-28.93-1.64-27.29-11.46S186.19,565,186.19,565,196,539.39,220,532.3c0,0,7.64-10.92,1.09-19.65,0,0-16.37-40.93-25.65-45.85s3.82-33.29,3.82-33.29Z" transform="translate(-58.9 -26.03)" opacity="0.05"/><path d="M217.3,591.79l-17.47,36.57c2.73,9.28,28.93,6,28.93,6S249,607.07,250,597.25s14.74-18,14.74-18c19.65-21.29,6.55-46.39,4.91-59.49,0-.25-.07-.52-.12-.81-1.45-8.78-10.28-34.06-17.15-53-4.63-12.75-8.38-22.62-8.38-22.62s-36.57,1.64-44.21,14.74c-3.5,6-1.93,12.24.79,17.25a40.65,40.65,0,0,0,8.07,10.11l12.43,30s21.83,39.84,13.64,43.12S217.3,591.79,217.3,591.79Z" transform="translate(-58.9 -26.03)" fill="#5f5d7e"/><path d="M282.47,268.17a16.74,16.74,0,0,0,2.07,6.95l.62,1.12,3.08,5.55h9.82l.21-2.58,2-24.71s-2.73-27.29-12-23.47C280.8,234.09,281.79,258.61,282.47,268.17Z" transform="translate(-58.9 -26.03)" fill="#fdc2cc"/><path d="M285.17,276.24l3.08,5.55h9.82l.21-2.58C293.71,278.8,288.26,278,285.17,276.24Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M284.43,276.33v12.55l20.19,5.46s0-1.3.05-3.13c.11-3.83.47-10,1.58-11.06C306.26,280.15,289.88,280.15,284.43,276.33Z" transform="translate(-58.9 -26.03)" fill="#eaeaf3"/><path d="M284.43,283.69v5.19l20.19,5.46s0-1.3.05-3.13C298,290,288.25,285.54,284.43,283.69Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M166.64,304.4c4,11.51-1.2,16.68-1.2,16.68s49.12,3.27,37.11-5.46c-7-5.1-10.12-16.35-11.48-24.82A84.65,84.65,0,0,1,190,280.15s-39.25-.58-31.11,10.37A52.55,52.55,0,0,1,166.64,304.4Z" transform="translate(-58.9 -26.03)" fill="#fdc2cc"/><path d="M158.9,290.52c4,5.38,5.31,6.17,6.65,10.06a21.94,21.94,0,0,0,3.72.31c9.36,0,18.47-1.93,21.81-10.09A84.65,84.65,0,0,1,190,280.15S150.75,279.57,158.9,290.52Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><circle cx="111.45" cy="254.12" r="22.38" fill="#fdc2cc"/><path d="M282.79,284s21.29,10.92,27.29,8.19l-14.74,40.39s-1.09,9.82-20.19,14.19-28.38,8.19-28.38,8.19l-35.48-42,18,4.91s9.28,13.1,15.83,8.73,26.74-2.18,26.74-2.18S283.34,293.25,282.79,284Z" transform="translate(-58.9 -26.03)" fill="#c4c8e2"/><path d="M282.79,284s21.29,10.92,27.29,8.19l-14.74,40.39s-1.09,9.82-20.19,14.19-28.38,8.19-28.38,8.19l-35.48-42,18,4.91s9.28,13.1,15.83,8.73,26.74-2.18,26.74-2.18S283.34,293.25,282.79,284Z" transform="translate(-58.9 -26.03)" opacity="0.05"/><path d="M199.83,309.73s-28.85-8.25-31.89,7.08c0,0,13.33-6.5,32.44,5,1.43.86,2.83,1.78,4.2,2.74,16.81,11.78,28.55,30,33.27,50.71L250,428.67,251.13,443s17.47,3.45,20.74-1.72,1.09-22.41-3.82-28.15S255.5,382.7,255.5,382.7l-4.78-14.33A95.85,95.85,0,0,0,232,335.59h0l-1.15-1.39a71.66,71.66,0,0,0-26.61-19.9C201.47,313.07,199.45,311.54,199.83,309.73Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M199.83,308.64s-28.85-8.25-31.89,7.08c0,0,13.33-6.5,32.44,5,1.43.86,2.83,1.78,4.2,2.74,16.81,11.78,28.55,30,33.27,50.71L250,427.57l1.09,14.36s17.47,3.45,20.74-1.72,1.09-22.41-3.82-28.15-12.55-30.45-12.55-30.45l-4.78-14.33A95.85,95.85,0,0,0,232,334.49h0l-1.15-1.39a71.66,71.66,0,0,0-26.61-19.9C201.47,312,199.45,310.45,199.83,308.64Z" transform="translate(-58.9 -26.03)" fill="#eaeaf3"/><path d="M272.39,418.66l-.24,9.4-7.64-4.91s-13.61-49.53-23.2-63c-1.13-1.59-1.68-4-2.73-4.37l-39-37.66v-8.73s19.92,14.74,21.56,17.47c.7,1.16,11.16,6.88,17.47,15.83a197.85,197.85,0,0,1,15,25.92L266.69,398Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M199.56,308.8v8.46l39.57,37.93c1.05.4,1.6,2.78,2.73,4.37,9.59,13.51,23.2,63,23.2,63l7.64,4.91.24-9.4-5.7-20.62L254.14,368a197.8,197.8,0,0,0-15-25.92c-6.3-9-16.77-14.67-17.47-15.83-1.41-2.35-16.8-13.62-21-16.69Z" transform="translate(-58.9 -26.03)" fill="#d39999"/><rect x="163.19" y="153.16" width="78" height="78" rx="11.17" ry="11.17" fill="#908820"/><rect x="163.19" y="153.16" width="78" height="78" rx="11.17" ry="11.17" fill="#fff" opacity="0.1"/><path d="M261.1,201.84a16.36,16.36,0,1,0,16.36,16.36A16.33,16.33,0,0,0,261.1,201.84Zm0,27a10.63,10.63,0,1,1,10.63-10.63,10.65,10.65,0,0,1-10.63,10.63Zm20.84-27.66a3.82,3.82,0,1,1-3.82-3.82A3.81,3.81,0,0,1,281.95,201.17ZM292.78,205c-.24-5.11-1.41-9.64-5.15-13.37s-8.26-4.9-13.37-5.15c-5.27-.3-21.06-.3-26.32,0-5.1.24-9.62,1.41-13.37,5.14s-4.9,8.26-5.15,13.37c-.3,5.27-.3,21.06,0,26.32.24,5.11,1.41,9.64,5.15,13.37s8.26,4.9,13.37,5.15c5.27.3,21.06.3,26.32,0,5.11-.24,9.64-1.41,13.37-5.15s4.9-8.26,5.15-13.37c.3-5.27.3-21,0-26.31ZM286,237a10.77,10.77,0,0,1-6.06,6.06c-4.2,1.67-14.17,1.28-18.81,1.28s-14.62.37-18.81-1.28a10.77,10.77,0,0,1-6.06-6.06c-1.67-4.2-1.28-14.17-1.28-18.81s-.37-14.62,1.28-18.81a10.77,10.77,0,0,1,6.06-6.06c4.2-1.67,14.17-1.28,18.81-1.28s14.62-.37,18.81,1.28a10.77,10.77,0,0,1,6.06,6.06c1.67,4.2,1.28,14.17,1.28,18.81S287.64,232.81,286,237Z" transform="translate(-58.9 -26.03)" opacity="0.2"/><path d="M177.81,256.39c1.73-.85,3.68-2.13,3.67-4.05A4.45,4.45,0,0,0,179.2,249c-3.12-2.12-2.88-6-6.46-4.77-2.4.8-4.49,2.32-6.83,3.29a16,16,0,0,1-5.13,1.19,20.93,20.93,0,0,0-3.37.27,9.79,9.79,0,0,0-2.54,1,21.92,21.92,0,0,0-10.31,12.86c-1.48,5.07-1.33,11.07-5.15,14.71a14.19,14.19,0,0,0-1.9,1.86,6.51,6.51,0,0,0-1,2.49,15.09,15.09,0,0,0,.75,9.06,11.1,11.1,0,0,0,6.51,6.16c1.29.42,2.65.57,4,.9,6.27,1.58,10.71,7.08,16.42,10.12a3.13,3.13,0,0,0,1.52.48,3,3,0,0,0,1.6-.68c2.21-1.58,4-4,4-6.74a13.63,13.63,0,0,0-1.46-4.86,47.09,47.09,0,0,1-2.73-8.79,4.29,4.29,0,0,1-.06-2.13c.41-1.32,1.88-1.94,2.9-2.88,1.94-1.8,2.17-4.84,1.45-7.38s-2.21-4.78-3.33-7.17a5.65,5.65,0,0,1-.66-2.33c0-3.19,4.31-4.92,6.13-7C176,255.78,174.44,258.06,177.81,256.39Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M177.26,256.39c1.73-.85,3.68-2.13,3.67-4.05a4.45,4.45,0,0,0-2.28-3.37c-3.12-2.12-2.88-6-6.46-4.77-2.4.8-4.49,2.32-6.83,3.29a16,16,0,0,1-5.13,1.19c-1.13.07-4.77-1.65-5.87-1.4a9.79,9.79,0,0,0-2.54,1,21.92,21.92,0,0,0-10.31,12.86c-1.48,5.07-.45,11.4-4.27,15-.64.61.23,2.47-.29,3.19a6.51,6.51,0,0,0-1,2.49,15.09,15.09,0,0,0,.75,9.06,11.1,11.1,0,0,0,6.51,6.16c1.29.42,2.65.57,4,.9,6.27,1.58,10.71,7.08,16.42,10.12a3.13,3.13,0,0,0,1.52.48,3,3,0,0,0,1.6-.68c2.21-1.58,4-4,4-6.74a13.63,13.63,0,0,0-1.46-4.86,47.09,47.09,0,0,1-2.73-8.79,4.29,4.29,0,0,1-.06-2.13c.41-1.32,1.88-1.94,2.9-2.88,1.94-1.8,2.17-4.84,1.45-7.38s-2.21-4.78-3.33-7.17a5.65,5.65,0,0,1-.66-2.33c0-3.19,4.31-4.92,6.13-7C175.45,255.78,173.89,258.06,177.26,256.39Z" transform="translate(-58.9 -26.03)" fill="#865a61"/><path d="M200.62,475.32c10-1.18,22.6-2.34,28.14-1.42,8.91,1.48,21.41-6.46,23.66-7.95-4.63-12.75-8.38-22.62-8.38-22.62s-36.57,1.64-44.21,14.74C196.33,464.07,197.9,470.31,200.62,475.32Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M152.89,334.18c2.18,19.1,24,45.85,24,45.85s15.28,15.83,10.37,69.86l-.55,26.2s32.2-4.91,42-3.27,24-8.19,24-8.19,5.46-94.42-18-114.07a138.64,138.64,0,0,0-9.43-12.83c-6.3-7.63-14.87-16.22-22.88-18.34a14.32,14.32,0,0,0-3.16-.49s-24.56-13.64-31.11-3.27C168.17,315.62,150.71,315.08,152.89,334.18Z" transform="translate(-58.9 -26.03)" fill="#c4c8e2"/><path d="M252.5,436s-8.73-3.82-23.47-2.73-41-12-41-12" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M236.67,557.68s-.55,1.64,6.55,3.82,9.28,4.37,9.28,4.37" transform="translate(-58.9 -26.03)" opacity="0.05"/><path d="M215,255.94a16.56,16.56,0,0,0,1.87,7.4L220,269.4l2.18,4.2,13.64,1.09s.28-1.12.63-3c1-5.46,2.63-17.25-.63-25.4-4.37-10.92-6-32.2-14.19-27.29C215.17,222.92,214.86,246.41,215,255.94Z" transform="translate(-58.9 -26.03)" fill="#fdc2cc"/><path d="M220,269.23v.17l2.18,4.2,13.64,1.09s.28-1.12.63-3C231.6,269.81,225.2,268.08,220,269.23Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M220,270.32v9.82l19.65,10.37s.22-1.78.64-4.08c.71-3.95,2-9.43,3.73-10.11C244,276.33,229.85,268.14,220,270.32Z" transform="translate(-58.9 -26.03)" fill="#eaeaf3"/><path d="M166.89,359.53a23.12,23.12,0,0,0,17.65,5.65c34.55-3.51,35.49-15.72,35.49-15.72l5.31-11.74c-6.3-7.63-14.87-16.22-22.88-18.34-5.73,9.23-10.8,15.89-10.8,15.89s-25.65-19.65-33.29-1.09C153.9,345,160.55,353.94,166.89,359.53Z" transform="translate(-58.9 -26.03)" opacity="0.05"/><path d="M220,276.34v3.81l19.65,10.37s.22-1.78.64-4.08C231.85,283.72,223.47,278.58,220,276.34Z" transform="translate(-58.9 -26.03)" opacity="0.1"/><path d="M217.84,276.33s14.74,10.37,27.29,12.55l-25.65,56.76s-.94,12.21-35.49,15.72a23.13,23.13,0,0,1-17.65-5.65c-6.33-5.6-13-14.55-8.53-25.35,7.64-18.56,33.29,1.09,33.29,1.09S217.3,297.07,217.84,276.33Z" transform="translate(-58.9 -26.03)" fill="#c4c8e2"/><path d="M428.2,189c0,.68,0,1.35,0,2,0,20.59-15.67,44.32-44.32,44.32a44,44,0,0,1-23.92-7,32.22,32.22,0,0,0,3.76.19,31.2,31.2,0,0,0,19.34-6.65,15.6,15.6,0,0,1-14.56-10.8,19.64,19.64,0,0,0,2.94.24,16.47,16.47,0,0,0,4.1-.53,15.58,15.58,0,0,1-12.49-15.29v-.19a15.69,15.69,0,0,0,7,2,15.6,15.6,0,0,1-4.82-20.83,44.28,44.28,0,0,0,32.12,16.3,17.58,17.58,0,0,1-.39-3.57,15.59,15.59,0,0,1,27-10.66,30.67,30.67,0,0,0,9.89-3.76,15.53,15.53,0,0,1-6.85,8.58,31.23,31.23,0,0,0,9-2.41A33.48,33.48,0,0,1,428.2,189Z" transform="translate(-58.9 -26.03)" opacity="0.2"/><path d="M369.64,351.15v58.71a3.64,3.64,0,0,1-3.64,3.64H349.17V387.95h8.57L359,378h-9.87v-6.36c0-2.89.8-4.85,4.94-4.85h5.27v-8.91a70.91,70.91,0,0,0-7.69-.4c-7.6,0-12.82,4.64-12.82,13.17V378h-8.6v10h8.6v25.56H307.28a3.65,3.65,0,0,1-3.64-3.64V351.15a3.65,3.65,0,0,1,3.64-3.64H366A3.65,3.65,0,0,1,369.64,351.15Z" transform="translate(-58.9 -26.03)" opacity="0.2"/><path d="M680.72,272.9a13.31,13.31,0,0,0-9.89-8.88c-8.38-1.76-41.45.11-41.45.11s-33.08,1.87-41.2,4.56a13.31,13.31,0,0,0-8.82,9.94c-1.74,8.43-.76,25.75-.76,25.75s1,17.32,3.66,25.5a13.11,13.11,0,0,0,9.88,8.73c8.38,1.76,41.45-.11,41.45-.11s33.08-1.87,41.2-4.56a13.11,13.11,0,0,0,8.83-9.79c1.74-8.43.76-25.75.76-25.75S683.4,281.08,680.72,272.9Zm-59.16,44.84-1.78-31.46,28.54,14.16-26.76,17.29Z" transform="translate(-58.9 -26.03)" fill="#908820" opacity="0.1"/></svg>
<svg id="a58ef234-55dd-4456-b368-278f056cc5c7" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1028" height="598.99741" viewBox="0 0 1028 598.99741"><title>open source</title><circle cx="332.00497" cy="292.00357" r="292.00357" fill="#f2f2f2"/><path d="M1114,668.4987a13.68982,13.68982,0,0,1-.16992,2.12c-7.14014,43.76-234.46008,78.88-513.83008,78.88s-506.69-35.12-513.83-78.88a13.67509,13.67509,0,0,1-.17-2.12c0-.2,0-.41.02-.61,0-.11005.01-.21.01-.32a.48944.48944,0,0,0,.01-.12c.04-.45.1-.88.18-1.32,3.26-17.89,43.35-34.33,108.74-47.5,53.48-10.77,123.89-19.36,204.93-24.76,61.5-4.1,129.12-6.37,200.11-6.37,57.12,0,112.06,1.47,163.38,4.17h.02q16.11.855,31.74,1.87c71.33,4.62,134.63,11.69,185.63,20.54,31.37,5.45,58.1,11.58,79.18994,18.22h.01c32.46,10.23,51.6001,21.7,53.81006,33.83.08.44.13989.87.17993,1.32a.48659.48659,0,0,0,.01.12c0,.11.01.21.01.32C1114,668.08867,1114,668.29869,1114,668.4987Z" transform="translate(-86 -150.5013)" fill="#3f3d56"/><ellipse cx="501" cy="571.99741" rx="165" ry="24" opacity="0.1"/><path d="M1059.96,632.29869c-5.77,1.65-12.27,3.18-19.3999,4.58-29.36011,5.78-69.42005,9.33-113.53,9.33-49.28,0-93.45-4.43-123.38-11.44-24.84-5.81994-39.88-13.38995-39.88-21.69995,0-7.30005,11.63995-14.06,31.37-19.53,71.33,4.62,134.63,11.69,185.63,20.54C1012.14,619.52867,1038.87,625.65868,1059.96,632.29869Z" transform="translate(-86 -150.5013)" opacity="0.1"/><path d="M618,621.2487c0,15.13-94.92,27.4-212,27.4s-212-12.27-212-27.4a4.372,4.372,0,0,1,.96-2.62c53.48-10.77,123.89-19.36,204.93-24.76q3.045-.015,6.11-.01C523.08,593.85869,618,606.1187,618,621.2487Z" transform="translate(-86 -150.5013)" opacity="0.1"/><rect x="416.47191" y="334.97493" width="41.68539" height="134.58427" fill="#908820"/><rect x="279.50562" y="271.85134" width="41.68539" height="197.70787" fill="#908820"/><rect x="143.73034" y="271.85134" width="41.68539" height="197.70787" fill="#908820"/><rect x="211.61798" y="271.85134" width="41.68539" height="134.58427" fill="#908820"/><rect x="347.39326" y="271.85134" width="41.68539" height="39.30337" fill="#908820"/><rect x="347.39326" y="334.97493" width="41.68539" height="134.58427" fill="#8a8b8c"/><rect x="416.47191" y="271.85134" width="109.57303" height="39.30337" fill="#908820"/><path d="M901.062,410.53031q42.74823,0,72.77372,29.83923A97.98265,97.98265,0,0,1,995.66753,473.208a107.34579,107.34579,0,0,1,.09551,78.40916,93.53335,93.53335,0,0,1-21.73946,32.294,104.70367,104.70367,0,0,1-33.84136,22.5609,101.42451,101.42451,0,0,1-39.11384,7.82277,99.01466,99.01466,0,0,1-38.66173-7.73362A104.84511,104.84511,0,0,1,807.00731,551.165a101.7643,101.7643,0,0,1,.08913-77.59409A102.30668,102.30668,0,0,1,829.565,440.18488Q858.67354,410.53668,901.062,410.53031Zm.36932,18.3773q-34.93023,0-58.76787,24.37889A87.59486,87.59486,0,0,0,824.197,480.66778a80.78885,80.78885,0,0,0,0,63.31126,86.24851,86.24851,0,0,0,45.5771,45.30965,82.68682,82.68682,0,0,0,63.41-.08914,87.99456,87.99456,0,0,0,27.74106-18.37412q23.83128-23.28683,23.82809-58.40491a83.65859,83.65859,0,0,0-6.18627-32.02018,81.465,81.465,0,0,0-18.0016-26.92917Q935.98906,428.9092,901.43132,428.90761Zm-1.27673,66.59064-13.64925,7.09685a14.0061,14.0061,0,0,0-5.36482-6.36774,12.08742,12.08742,0,0,0-5.91563-1.818q-13.63969,0-13.64606,18.01115,0,8.18574,3.45449,13.09526,3.45768,4.9143,10.19157,4.91589,8.91644,0,12.55719-8.73335l12.55082,6.36774a29.99,29.99,0,0,1-26.74451,16.01168q-13.64448,0-22.01646-8.3704-8.36721-8.36721-8.36721-23.28364,0-14.55666,8.45954-23.10535,8.45795-8.5487,21.37969-8.55187Q891.97045,480.75694,900.15459,495.49825Zm58.76469,0-13.46459,7.09685a13.99175,13.99175,0,0,0-5.368-6.36774,12.37253,12.37253,0,0,0-6.09393-1.818q-13.64446,0-13.64924,18.01115,0,8.18574,3.45767,13.09526,3.45291,4.9143,10.19157,4.91589,8.90688,0,12.54764-8.73335l12.73548,6.36774a31.27658,31.27658,0,0,1-11.27409,11.73575,29.39766,29.39766,0,0,1-15.46405,4.27593q-13.83072,0-22.09923-8.3704-8.2908-8.36721-8.28444-23.28364,0-14.55666,8.46273-23.10535,8.45319-8.5487,21.37333-8.55187Q950.91184,480.75694,958.91928,495.49825Z" transform="translate(-86 -150.5013)" fill="#908820"/><path d="M839.41688,660.83685c0,48.73748-67.93425,33.66253-151.11288,33.66253s-150.10359,15.075-150.10359-33.66253,67.925-59.33747,151.10359-59.33747S839.41688,612.09937,839.41688,660.83685Z" transform="translate(-86 -150.5013)" opacity="0.1"/><path d="M839.41688,648.83685c0,48.73748-67.93425,33.66253-151.11288,33.66253s-150.10359,15.075-150.10359-33.66253,67.925-59.33747,151.10359-59.33747S839.41688,600.09937,839.41688,648.83685Z" transform="translate(-86 -150.5013)" fill="#3f3d56"/><path d="M825.54156,561.34329l3.43359,14.16355a.88462.88462,0,0,1-.01719.478l-15.57547,48.67335a.88465.88465,0,0,1-1.72156-.16974l-2.15469-18.96122a.88471.88471,0,0,1,.03787-.374l14.29657-43.87567A.88465.88465,0,0,1,825.54156,561.34329Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><polygon points="738.278 412.038 741.449 424.19 727.711 468.572 724.541 454.835 738.278 412.038" fill="#f2f2f2"/><path d="M755.06392,628.58381c.52836,1.58507,54.949-.52835,55.47732-1.05671a11.4687,11.4687,0,0,0,1.16235-2.11342c.49669-1.05671.95107-2.11342.95107-2.11342l-2.11342-17.10815L756.649,604.27946s-1.34734,14.49808-1.62209,21.13422A15.85321,15.85321,0,0,0,755.06392,628.58381Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><polygon points="717.673 458.005 718.729 472.799 694.953 472.799 694.953 458.005 717.673 458.005" opacity="0.1"/><polygon points="687.028 462.76 687.17 462.732 686.5 469.1 673.291 469.1 673.291 462.76 687.028 462.76" opacity="0.1"/><path d="M755.06392,628.58381c.52836,1.58507,54.949-.52835,55.47732-1.05671a11.4687,11.4687,0,0,0,1.16235-2.11342H755.0269A15.85321,15.85321,0,0,0,755.06392,628.58381Z" transform="translate(-86 -150.5013)" opacity="0.1"/><path d="M671.05541,573.10649h14.79395l36.45652-14.26559s25.88942-10.56711,23.776,9.5104S740.27,615.37493,740.27,615.37493s-11.62382-5.28356-17.96409-3.69849-2.11342-32.22968-2.11342-32.22968-51.77883,24.8327-56.534,21.66257-5.81191-25.36106-5.81191-25.36106Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M671.05541,573.10649h14.79395l36.45652-14.26559s25.88942-10.56711,23.776,9.5104S740.27,615.37493,740.27,615.37493s-11.62382-5.28356-17.96409-3.69849-2.11342-32.22968-2.11342-32.22968-51.77883,24.8327-56.534,21.66257-5.81191-25.36106-5.81191-25.36106Z" transform="translate(-86 -150.5013)" opacity="0.1"/><path d="M694.36337,526.77716l17.90376,25.72347,41.74008,34.87146s38.56994,8.982,33.28639,15.32231-37.51323-6.34027-37.51323-6.34027S701.7,561.48268,700.115,558.8409s-19.0208-27.47448-19.0208-27.47448Z" transform="translate(-86 -150.5013)" fill="#a0616a"/><path d="M694.36337,526.77716l17.90376,25.72347,41.74008,34.87146s38.56994,8.982,33.28639,15.32231-37.51323-6.34027-37.51323-6.34027S701.7,561.48268,700.115,558.8409s-19.0208-27.47448-19.0208-27.47448Z" transform="translate(-86 -150.5013)" opacity="0.1"/><circle cx="605.66127" cy="324.33109" r="23.77599" fill="#a0616a"/><path d="M690.0762,495.9666s-7.92533,13.73724-9.51039,21.13422-23.776-14.79395-23.776-14.79395l-2.90595-7.66115s20.87-9.77458,19.285-17.69991S690.0762,495.9666,690.0762,495.9666Z" transform="translate(-86 -150.5013)" fill="#a0616a"/><path d="M683.73594,506.00536l11.62382,22.71928s-2.64178,11.09546-6.86862,12.15217-22.19093-13.73724-22.19093-13.73724Z" transform="translate(-86 -150.5013)" fill="#d0cde1"/><path d="M694.30305,537.17833l21.66257,28.53119,51.77883,38.56994s37.51323,7.92534,29.05955,13.73724-32.758-5.28355-32.758-5.28355-52.30718-28.00284-63.40265-39.62666-31.70132-31.173-31.70132-31.173Z" transform="translate(-86 -150.5013)" fill="#a0616a"/><path d="M665.2435,581.56018l6.34026,10.03875,37.83932-11.9045a29.75135,29.75135,0,0,1,24.7708,2.98851c6.60444,4.1608,10.83129,10.76524,1.32089,20.53981-19.0208,19.54915-31.70133,8.982-31.70133,8.982s-63.931,30.11625-74.49811,15.3223-11.09546-22.19093-11.09546-22.19093S658.90323,579.97511,665.2435,581.56018Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M744.49681,616.43164s16.90737,11.09546-3.17013,16.379-34.87146-2.11343-34.87146-2.11343-17.43573,0-17.43573-8.982,5.81191-10.03875,5.81191-10.03875l17.96409,2.64178S734.45806,608.50631,744.49681,616.43164Z" transform="translate(-86 -150.5013)" fill="#d0cde1"/><path d="M711.16583,468.8514a17.728,17.728,0,0,0,4.98235,1.78469,4.548,4.548,0,0,0,4.59533-1.96886,6.18073,6.18073,0,0,0,.522-2.7952c.06365-2.90132-.14619-6.03778-1.98475-8.28309-1.1701-1.429-2.87816-2.32977-4.08936-3.72406a17.59821,17.59821,0,0,1-2.06717-3.37767c-2.35552-4.42406-5.84318-8.67589-10.69025-9.9513a23.18347,23.18347,0,0,0-6.24-.47707l-12.20625.17221a21.25081,21.25081,0,0,0-5.872.632c-3.92885,1.1879-6.78945,4.49179-9.40936,7.65139a47.85117,47.85117,0,0,0-5.39639,7.44763,26.133,26.133,0,0,0-3.09771,12.80437,11.97989,11.97989,0,0,0,.43881,3.38808,18.80129,18.80129,0,0,0,1.33168,2.925c2.28907,4.49613,3.64824,10.1121,1.07032,14.44908,4.47812-1.82926,8.90892-4.22774,11.863-8.05834,1.3215-1.71364,2.35787-3.7197,4.119-4.9772s4.63076-1.36658,5.76072.479a5.163,5.163,0,0,1,.58258,2.41835,11.084,11.084,0,0,0,.66762,4.08133,3.05833,3.05833,0,0,0,3.32224,1.8956c2.30039-.648,2.0801-4.17861,3.85194-5.78243,1.3456-1.218,3.42157-.98516,5.15028-1.53807a7.64334,7.64334,0,0,0,4.38653-4.32052c.65453-1.46807.64508-5.3098,1.88365-6.10481C706.19386,466.62453,709.72609,468.20958,711.16583,468.8514Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M651.99646,492.13947a6.27057,6.27057,0,0,1,3.26389.00831c3.39162.91054,10.99473,3.321,12.62493,6.989,2.11342,4.7552,9.51039,12.15217,9.51039,12.15217s10.03876,10.03875,7.92534,16.90737-10.03876,14.794-10.03876,14.794,2.11342,32.758-8.45368,41.21172-14.794,2.64178-14.794,10.56711-32.758,32.758-39.0983,17.43573c0,0,3.17014-28.5312,2.11343-38.56995C614.00826,563.74087,615.53292,502.01108,651.99646,492.13947Z" transform="translate(-86 -150.5013)" fill="#d0cde1"/><path d="M668.942,505.477s32.22968,23.776,26.41777,33.81475c0,0-19.54915,9.5104-23.776,8.982s-20.60586-19.02079-24.8327-20.60586S640.4108,497.55167,668.942,505.477Z" transform="translate(-86 -150.5013)" fill="#d0cde1"/><path d="M579.81065,526.78274c-5.01266,1.69284-9.5539,5.65228-10.58039,10.84253-.47582,2.40592-.18409,4.907-.58451,7.32661-.92865,5.61161-5.33358,9.89722-9.39638,13.878s-8.2584,8.60617-8.55492,14.28636c-.3247,6.22,4.1862,12.047,3.40563,18.22633-.90687,7.17909-8.48947,11.831-10.36389,18.82016-1.25164,4.667.28211,9.6942,2.8055,13.81484,4.80858,7.85231,13.0279,13.18323,21.78085,16.041s18.07506,3.45905,27.28222,3.55552c11.21079.11746,23.38277-.845,31.806-8.244a25.48043,25.48043,0,0,0,8.459-16.5882c.8002-8.57064-2.77695-17.42641-.30951-25.67311,1.55287-5.19,5.44709-9.93,4.86592-15.316-.56823-5.26625-5.16773-8.99907-8.32312-13.25346-2.9461-3.97222-4.7338-8.66319-6.48587-13.28794l-5.99481-15.82383c-1.17848-3.1107-2.47257-6.38232-5.09885-8.424-4.91665-3.8221-12.82242-3.07115-18.57152-2.28285C590.61855,525.412,584.93966,525.0506,579.81065,526.78274Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M581.68806,572.87747s2.65748,8.63681-17.27362,18.60236,1.99311,20.59548,1.99311,20.59548l23.25295,5.315s23.91732-3.32185,26.5748-7.30808,7.30808-17.27362,5.97934-19.26673a31.0614,31.0614,0,0,0-3.32186-3.98622s-15.94488-2.65748-15.28051-9.96555S581.68806,572.87747,581.68806,572.87747Z" transform="translate(-86 -150.5013)" fill="#ffb9b9"/><path d="M581.68806,572.87747s2.65748,8.63681-17.27362,18.60236,1.99311,20.59548,1.99311,20.59548l23.25295,5.315s23.91732-3.32185,26.5748-7.30808,7.30808-17.27362,5.97934-19.26673a31.0614,31.0614,0,0,0-3.32186-3.98622s-15.94488-2.65748-15.28051-9.96555S581.68806,572.87747,581.68806,572.87747Z" transform="translate(-86 -150.5013)" opacity="0.1"/><circle cx="508.64328" cy="414.73592" r="21.25984" fill="#ffb9b9"/><path d="M592.318,610.08219s-25.60367-1.87245-26.08923-19.53858c0,0-9.12238-1.05689-11.77986,1.60059s-11.2943,4.65059-11.2943,11.29429,14.61615,26.57481,14.61615,26.57481,7.97244,26.5748,4.65059,31.22539-3.98622,16.60925-3.98622,16.60925,33.2185-2.65748,38.53346,0,26.57481,2.65748,27.90355,0-1.32874-35.876-1.32874-35.876,5.97933-7.97244,4.65059-17.27362L644.13885,606.096s-5.97933-17.938-15.28051-17.938c0,0-12.623-3.32185-12.95522-1.66092S622.21464,606.76034,592.318,610.08219Z" transform="translate(-86 -150.5013)" fill="#ff6584"/><path d="M532.52467,668.54676s5.97933,9.30118,21.25985,11.29429,17.938-3.32185,16.60925-4.65059-17.27362-7.30807-17.27362-7.30807l-9.30119-7.30807Z" transform="translate(-86 -150.5013)" fill="#ffb9b9"/><path d="M641.48137,660.57432l-17.27362,9.96555s-22.58859-1.32874-13.95178,5.315,24.5817,3.98622,24.5817,3.98622l15.28051-7.30807Z" transform="translate(-86 -150.5013)" fill="#ffb9b9"/><path d="M555.11326,677.84794s-15.94489,39.19784,3.32185,45.17717,72.41634,4.65059,84.375-3.32185,15.28051-18.60236,13.2874-21.92421-15.28051-12.623-24.58169-13.2874S555.11326,677.84794,555.11326,677.84794Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M555.11326,677.84794s-15.94489,39.19784,3.32185,45.17717,72.41634,4.65059,84.375-3.32185,15.28051-18.60236,13.2874-21.92421-15.28051-12.623-24.58169-13.2874S555.11326,677.84794,555.11326,677.84794Z" transform="translate(-86 -150.5013)" opacity="0.05"/><path d="M630.37766,680.28318s56.28087-20.37323,55.6165,4.20847-35.876,31.22539-35.876,31.22539l-10.62992,1.32874-58.46457,1.99311s1.99311-10.62992-2.65748-11.95866c0,0,15.28051-3.98622,15.94488-4.65059s38.53347-13.95177,41.19095-14.61614,10.62992-5.97934,16.60925-5.315c0,0,6.6437-5.315,13.95177-3.98622l-20.59547,1.32874Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M630.37766,680.28318s56.28087-20.37323,55.6165,4.20847-35.876,31.22539-35.876,31.22539l-10.62992,1.32874-58.46457,1.99311s1.99311-10.62992-2.65748-11.95866c0,0,15.28051-3.98622,15.94488-4.65059s38.53347-13.95177,41.19095-14.61614,10.62992-5.97934,16.60925-5.315c0,0,6.6437-5.315,13.95177-3.98622l-20.59547,1.32874Z" transform="translate(-86 -150.5013)" opacity="0.1"/><polygon points="534.434 533.081 541.53 549.271 584.714 533.99 588.036 525.354 576.741 523.36 554.035 527.347 544.187 530.004 534.434 533.081" fill="#2f2e41"/><polygon points="534.434 533.081 541.53 549.271 584.714 533.99 588.036 525.354 576.741 523.36 554.035 527.347 544.187 530.004 534.434 533.081" opacity="0.1"/><path d="M528.53845,703.094s12.623,15.94488,27.23918,19.9311,22.58858-1.32874,22.58858-1.32874L573.71562,703.094l-1.99311-11.95866L563.0857,685.156l-17.27363-7.97245-20.59547-6.6437s-8.63681-1.32874-8.63681,0,7.97244,21.25985,7.97244,21.25985Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M543.819,711.73082s2.65748,19.26673,5.97934,20.59547,13.2874-1.32874,15.94488-2.65748,11.69054-7.45919,11.69054-7.45919S545.81207,710.40208,543.819,711.73082Z" transform="translate(-86 -150.5013)" fill="#f2f2f2"/><polygon points="518.277 563.223 530.9 565.216 529.571 580.496 511.964 576.22 518.277 563.223" fill="#ffb9b9"/><path d="M612.18567,717.97655s-5.91592,7.04167.06341,11.02789l3.98622,3.32185s13.28741,2.65748,14.61615,3.98622,6.6437-1.99311,6.6437-1.99311.66437-19.9311,0-21.25984-8.63681-2.65748-9.30118-1.32874-8.63682,3.32185-13.28741,1.99311Z" transform="translate(-86 -150.5013)" fill="#f2f2f2"/><path d="M559.09948,682.49853s-46.50591-27.23917-53.814-17.27362-15.28051,23.91733,5.97933,34.54725,87.69685,32.55413,87.69685,32.55413,9.96555-15.28051,9.96555-19.26673c0,0-35.21161-15.94488-41.19094-17.27362s-29.89666-16.60926-33.21851-17.938-7.97244-5.315-7.97244-5.315Z" transform="translate(-86 -150.5013)" fill="#2f2e41"/><path d="M545.81207,600.11664l-2.65748,1.99311s-5.97933,15.28052-6.6437,23.91733-8.63681,35.21161-7.97244,38.53346,1.32874,5.97933,5.315,9.30118c0,0,5.315-11.95866,14.61615-9.96555l11.29429-41.19094Z" transform="translate(-86 -150.5013)" fill="#ff6584"/><path d="M638.82389,600.11664l5.315,5.97933s3.32185,7.97245,3.32185,9.96556,6.6437,31.22539,7.30807,32.55413,3.32185,17.938,0,21.92421-9.30118,6.6437-9.30118,6.6437,3.98622-13.95177-8.63681-14.61614l1.32874-5.315-11.95866-39.8622Z" transform="translate(-86 -150.5013)" fill="#ff6584"/><path d="M547.14082,645.85762v34.9949a8.41342,8.41342,0,0,0,8.2888,8.41251l73.18511,1.08423a8.41344,8.41344,0,0,0,8.53185-8.08916l1.34611-34.999a8.41344,8.41344,0,0,0-8.28529-8.73591l-74.53123-1.08016A8.41342,8.41342,0,0,0,547.14082,645.85762Z" transform="translate(-86 -150.5013)" fill="#3f3d56"/><ellipse cx="509.89011" cy="401.732" rx="22.88412" ry="12.32222" fill="#2f2e41"/><circle cx="505.9858" cy="513.72706" r="5.31496" fill="#f2f2f2"/></svg>
<svg id="a91a722e-5259-4ef2-8c31-2e8a1147b313" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1100" height="806.74" viewBox="0 0 1100 806.74"><defs><linearGradient id="f0793c8d-2cc7-4dcf-96d3-b23677fc2573" x1="459.23" y1="847.99" x2="459.23" y2="385.75" gradientTransform="translate(250.84)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient></defs><title>operating system</title><ellipse cx="629.72" cy="780.56" rx="122.56" ry="26.18" fill="#908820" opacity="0.1"/><path d="M1120.55,730.47c-24.35,20.2-59,28.56-92.51,33.84l-5.64.86c-58.57,8.74-118.32,10.52-177.79,12.28-165.87,4.91-332.17,9.81-497.6-1.51-73.16-5-148.13-13.76-212.25-43.7l-1.67-.79c-.83-.39-1.66-.78-2.49-1.19A224.11,224.11,0,0,1,98,711.17c-18.31-13.06-33.43-29-41.42-47.91-12.52-29.66-5.9-63,8.4-92s35.8-55.12,54.34-82.43c5.44-8,10.6-16.3,15.4-24.81,30-53.37,44.88-115.13,13.43-166.56a228.58,228.58,0,0,0-13.43-19c-7.67-10-15.47-20-20.74-31.07-12.29-25.76-9.65-54.73-5.17-82.25,6-36.59,17.05-76,51.38-98.86,35.67-23.74,86.3-22.54,130.57-14,68.83,13.19,132.36,40.8,196.49,65.7S618.6,165.56,689.11,168c62.26,2.17,134.89-14,176.23-56.08,40.15-40.85,115.6-43.94,170-19.27,51.91,23.54,85.48,73.36,83,123.12-3.16,62.1-54.93,111.83-95.9,164.7-6.81,8.79-13.33,17.68-19.28,26.72-19.42,29.53-33.15,65-19.78,96.88,7.5,17.85,21.81,31.47,39.06,43.36,38.65,26.63,92,44.55,116,81.83C1159.11,661.42,1151.45,704.82,1120.55,730.47Z" transform="translate(-50 -46.63)" fill="#908820" opacity="0.1"/><ellipse cx="972.5" cy="704.85" rx="57.01" ry="10.96" fill="#908820"/><ellipse cx="971.55" cy="700.19" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="689.57" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="678.94" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="668.32" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="657.7" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="647.08" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="971.55" cy="636.46" rx="6.64" ry="8.69" fill="#3f3d56"/><path d="M1046.4,610.41a31,31,0,0,0,2.47-3.64l-17.44-2.86,18.86.14a31.78,31.78,0,0,0,.61-25.2L1025.59,592l23.34-17.15a31.8,31.8,0,1,0-52.51,35.58,31.55,31.55,0,0,0-3.63,5.8L1015.43,628l-24.14-8.1a31.79,31.79,0,0,0,5.13,29.86,31.8,31.8,0,1,0,50,0,31.78,31.78,0,0,0,0-39.32Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M989.61,630.07a31.68,31.68,0,0,0,6.81,19.66,31.8,31.8,0,1,0,50,0C1050.66,644.32,989.61,626.5,989.61,630.07Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M184.73,720.54c0,5.59-21.81,10.21-50,10.87l-1.67,0c-.83-.39-1.66-.78-2.49-1.19A224.11,224.11,0,0,1,98,711.17c6.85-.8,14.64-1.34,23-1.52l1.13,0c1.82,0,3.67-.06,5.54-.06,1.22,0,2.43,0,3.62,0l1.18,0c.76,0,1.51,0,2.25,0C162.92,710.32,184.73,715,184.73,720.54Z" transform="translate(-50 -46.63)" fill="#908820"/><ellipse cx="76.76" cy="669.25" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="658.63" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="648.01" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="637.39" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="626.77" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="616.15" rx="6.64" ry="8.69" fill="#3f3d56"/><ellipse cx="76.76" cy="605.52" rx="6.64" ry="8.69" fill="#3f3d56"/><path d="M151.61,579.47a32.28,32.28,0,0,0,2.48-3.63L136.64,573l18.87.14a31.8,31.8,0,0,0,.6-25.19l-25.3,13.13,23.34-17.16a31.8,31.8,0,1,0-52.52,35.58,32.45,32.45,0,0,0-3.63,5.8L120.64,597,96.5,588.93a31.87,31.87,0,0,0,5.13,29.86,31.8,31.8,0,1,0,50,0,31.78,31.78,0,0,0,0-39.32Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M94.83,599.13a31.67,31.67,0,0,0,6.8,19.66,31.8,31.8,0,1,0,50,0C155.87,613.38,94.83,595.56,94.83,599.13Z" transform="translate(-50 -46.63)" opacity="0.1"/><g opacity="0.2"><path d="M355.84,601.92c0,.94-.1,1.86-.2,2.76Q344,600,332.72,594.75a18.59,18.59,0,0,1-10.6-5.17c-2.19-1.1-4.37-2.24-6.54-3.39a26.82,26.82,0,0,1,1.66-3c4.2-6.66,10.6-10.83,17.65-10.6s13.15,4.78,16.93,11.66A34.37,34.37,0,0,1,355.84,601.92Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><path d="M357,567a34.56,34.56,0,0,1-5.13,17.32c-4.21,6.64-10.6,10.81-17.65,10.58-.49,0-1-.06-1.45-.1a18.59,18.59,0,0,1-10.6-5.17,25.27,25.27,0,0,1-4.88-6.37,35.82,35.82,0,0,1,1.1-35c4.21-6.66,10.6-10.83,17.65-10.6s13.15,4.78,16.93,11.66A34.47,34.47,0,0,1,357,567Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><ellipse cx="336.18" cy="531.28" rx="28.63" ry="21.87" transform="translate(-255.43 803.92) rotate(-88.19)" fill="#3f3d56"/><ellipse cx="337.29" cy="496.31" rx="28.63" ry="21.87" transform="translate(-219.41 771.16) rotate(-88.19)" fill="#3f3d56"/><ellipse cx="338.39" cy="461.34" rx="28.63" ry="21.87" transform="translate(-183.39 738.39) rotate(-88.19)" fill="#3f3d56"/><path d="M264.12,219.46a105.78,105.78,0,0,1-7.76-12.24l57.72-7.62L252,198.1a104.92,104.92,0,0,1,.62-83l82,45.85L259.47,102A104.75,104.75,0,1,1,428.69,224.65,105.13,105.13,0,0,1,440,244.11l-75.77,36.38,80.33-24.17a104.85,104.85,0,0,1-20,97.78A104.75,104.75,0,1,1,260,348.91a104.73,104.73,0,0,1,4.08-129.45Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M449.05,290.08a104.29,104.29,0,0,1-24.44,64A104.75,104.75,0,1,1,260,348.91C246.57,330.66,449.42,278.33,449.05,290.08Z" transform="translate(-50 -46.63)" opacity="0.1"/></g><g opacity="0.2"><path d="M848.18,527.14c0,.67-.07,1.32-.14,2q-8.31-3.29-16.31-7.06a13.31,13.31,0,0,1-7.55-3.68c-1.55-.79-3.11-1.6-4.65-2.41a19.78,19.78,0,0,1,1.18-2.13c3-4.74,7.54-7.7,12.56-7.54s9.36,3.4,12.05,8.3A24.5,24.5,0,0,1,848.18,527.14Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><path d="M849,502.25a24.56,24.56,0,0,1-3.65,12.33c-3,4.72-7.54,7.69-12.56,7.53l-1-.07a13.31,13.31,0,0,1-7.55-3.68,18.32,18.32,0,0,1-3.47-4.54,25.51,25.51,0,0,1,.78-24.88c3-4.74,7.55-7.71,12.56-7.55s9.37,3.4,12.05,8.3A24.55,24.55,0,0,1,849,502.25Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><ellipse cx="834.19" cy="476.87" rx="20.37" ry="15.56" transform="translate(281.27 1248.98) rotate(-88.19)" fill="#3f3d56"/><ellipse cx="834.98" cy="451.98" rx="20.37" ry="15.56" transform="translate(306.9 1225.67) rotate(-88.19)" fill="#3f3d56"/><ellipse cx="835.76" cy="427.1" rx="20.37" ry="15.56" transform="translate(332.53 1202.35) rotate(-88.19)" fill="#3f3d56"/><path d="M782.91,255a73.57,73.57,0,0,1-5.52-8.71l41.08-5.42-44.21-1.07a74.67,74.67,0,0,1,.43-59.07L833,213.32,779.6,171.39A74.55,74.55,0,1,1,900,258.65a74.71,74.71,0,0,1,8.06,13.86L854.17,298.4l57.17-17.2a74.55,74.55,0,0,1-14.22,69.58A74.54,74.54,0,1,1,780,347.09,74.55,74.55,0,0,1,782.91,255Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M914.52,305.22a74.2,74.2,0,0,1-17.4,45.56A74.54,74.54,0,1,1,780,347.09C770.42,334.09,914.78,296.86,914.52,305.22Z" transform="translate(-50 -46.63)" opacity="0.1"/></g><g opacity="0.2"><path d="M635,449.09c0,.67-.08,1.32-.15,2q-8.31-3.29-16.31-7.06a13.28,13.28,0,0,1-7.54-3.68c-1.56-.79-3.11-1.6-4.65-2.41a19,19,0,0,1,1.17-2.13c3-4.73,7.55-7.7,12.56-7.54s9.36,3.4,12.05,8.3A24.5,24.5,0,0,1,635,449.09Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><path d="M635.74,424.2a24.56,24.56,0,0,1-3.65,12.33c-3,4.72-7.54,7.69-12.56,7.53-.35,0-.68,0-1-.07a13.28,13.28,0,0,1-7.54-3.68,18.11,18.11,0,0,1-3.48-4.54,25.48,25.48,0,0,1,.79-24.88c3-4.74,7.54-7.71,12.56-7.55s9.36,3.4,12,8.3A24.54,24.54,0,0,1,635.74,424.2Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><ellipse cx="620.97" cy="398.82" rx="20.37" ry="15.56" transform="matrix(0.03, -1, 1, 0.03, 152.77, 960.28)" fill="#3f3d56"/><ellipse cx="621.75" cy="373.93" rx="20.37" ry="15.56" transform="translate(178.4 936.96) rotate(-88.19)" fill="#3f3d56"/><ellipse cx="622.54" cy="349.05" rx="20.37" ry="15.56" transform="translate(204.04 913.64) rotate(-88.19)" fill="#3f3d56"/><path d="M569.68,176.91a74.87,74.87,0,0,1-5.52-8.71l41.08-5.42L561,161.71a74.62,74.62,0,0,1,.44-59.07l58.33,32.63L566.38,93.34A74.54,74.54,0,1,1,686.8,180.6a74.76,74.76,0,0,1,8.07,13.86L641,220.35l57.16-17.2a74.55,74.55,0,0,1-14.22,69.58A74.54,74.54,0,1,1,566.78,269a74.57,74.57,0,0,1,2.9-92.13Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M701.29,227.17a74.2,74.2,0,0,1-17.4,45.56A74.54,74.54,0,1,1,566.78,269C557.19,256.05,701.55,218.81,701.29,227.17Z" transform="translate(-50 -46.63)" opacity="0.1"/></g><path d="M1022.4,547.44V765.17c-58.57,8.74-118.32,10.52-177.79,12.28-165.87,4.91-332.17,9.81-497.6-1.51-73.16-5-148.13-13.76-212.25-43.7V464c.24-185-.76,0,0-185.56V211.85a14.1,14.1,0,0,1,14.1-14.1H1008.3a14.1,14.1,0,0,1,14.1,14.1V380.48C1023,547,1022,381,1022.4,547.44Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><rect x="344.15" y="217.75" width="368.86" height="28.56" rx="5.22" fill="#908820" opacity="0.3"/><path d="M737.94,279.85h-.61l-.23-.19a5.1,5.1,0,0,0,1.19-3.25,4.94,4.94,0,1,0-4.92,5,5.09,5.09,0,0,0,3.24-1.18l.22.19V281l3.81,3.82,1.14-1.14Zm-4.57,0a3.44,3.44,0,1,1,3.43-3.44A3.42,3.42,0,0,1,733.37,279.85Z" transform="translate(-50 -46.63)" fill="#444053"/><rect x="222.84" y="493.2" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="222.84" y="522.95" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="222.84" y="508.08" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M214.57,569.21a5.74,5.74,0,0,0-5.8,5.69,3.88,3.88,0,0,1-3.87,3.79,10,10,0,0,0,7.73,3.8,7.67,7.67,0,0,0,7.75-7.59A5.75,5.75,0,0,0,214.57,569.21Zm26.53-17.76-2.6-2.55a1.94,1.94,0,0,0-2.72,0l-17.34,17,5.32,5.22,17.32-17A1.86,1.86,0,0,0,241.1,551.45Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="222.84" y="345.66" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="222.84" y="375.41" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="222.84" y="360.53" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M241.18,424.78,226,409.5a10.82,10.82,0,0,0-2.51-11.58,11,11,0,0,0-12.35-2.18l7.18,7.22-5,5L206,400.78a10.86,10.86,0,0,0,2.17,12.41,10.66,10.66,0,0,0,11.51,2.52L234.83,431a1.61,1.61,0,0,0,2.34,0l3.84-3.86a1.51,1.51,0,0,0,.17-2.35Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="509.6" y="493.2" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="509.6" y="522.95" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="509.6" y="508.08" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M509.44,572.63a10,10,0,0,1-9.89,0c-3.87,1.19-6.24,5.09-7.12,8.54h24.12C515.68,577.72,513.32,573.82,509.44,572.63Zm-4.94-18.37a8.69,8.69,0,1,0,8.69,8.68A8.69,8.69,0,0,0,504.5,554.26Zm0,15.3a6.71,6.71,0,0,1-6.07-4.14h12.14A6.71,6.71,0,0,1,504.5,569.56Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M522.09,568a10.07,10.07,0,0,1-8.4.69,12,12,0,0,1-2.3,2.86,11.24,11.24,0,0,1,4.9,4.33H529.2A10.72,10.72,0,0,0,522.09,568Zm-4.94-18.38a8.71,8.71,0,0,0-7.33,4,10.78,10.78,0,0,1,5.15,7.14h8.25a6.72,6.72,0,0,1-6.07,4.12,6,6,0,0,1-2.08-.38,11.65,11.65,0,0,1-.54,2.07,8.35,8.35,0,0,0,2.62.4,8.69,8.69,0,0,0,0-17.38Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="222.84" y="603.86" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="222.84" y="633.61" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="222.84" y="618.74" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M226.57,654.25h-9.82a3.28,3.28,0,0,0-3.32,3.24v.7h-.62a3.28,3.28,0,0,0-3.32,3.24v23.63a3.34,3.34,0,0,0,3.32,3.32h17.08a3.27,3.27,0,0,0,3.23-3.32v-.62h.71a3.27,3.27,0,0,0,3.23-3.32V664.75Zm0,3.66,6.84,6.84h-6.84Zm3.94,27.15a.67.67,0,0,1-.62.7H212.81a.73.73,0,0,1-.69-.7V661.43a.67.67,0,0,1,.69-.62h.62v21c0,1.82.86,2.66,2.67,2.66h14.41Zm3.93-3.94a.66.66,0,0,1-.61.7H216.75a.73.73,0,0,1-.69-.7V657.49a.67.67,0,0,1,.69-.62h7.19v10.51h10.5Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="509.6" y="345.66" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="509.6" y="375.41" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="509.6" y="360.53" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M507.32,400H495.94a3.51,3.51,0,0,0-3.5,3.51v19.25a3.5,3.5,0,0,0,3.5,3.5H525.7a3.5,3.5,0,0,0,3.5-3.5V407.42a3.51,3.51,0,0,0-3.5-3.5H510.82l-3.5-3.94Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="792.79" y="493.2" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="792.79" y="522.95" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="792.79" y="508.08" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><path d="M812.93,555.91l-6.77-.32-.4-4.41a1.7,1.7,0,0,0-1.88-1.52l-24.27,2a1.67,1.67,0,0,0-1.59,1.79l1.73,19.35a1.7,1.7,0,0,0,1.88,1.49l1.23-.09-.19,3.76a1.87,1.87,0,0,0,1.83,1.93l27.09,1.28a1.88,1.88,0,0,0,2-1.74l1.14-21.6A1.86,1.86,0,0,0,812.93,555.91Zm-29.12.44-.58,11.06-1.44,2-1.32-14.61v-.09a.89.89,0,0,1,.78-.77l21.42-1.75a.81.81,0,0,1,.87.64s0,0,0,0,0,0,0,0l.21,2.52-18-.86A1.9,1.9,0,0,0,783.81,556.35Zm27.4,19.44-7-8.17-3.07,2.82-5.67-6.63-10.07,10.72.86-16.33v0a.94.94,0,0,1,1-.74l23.9,1.16a.87.87,0,0,1,.85.83s0,0,0,0Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M806.89,565.42a2.63,2.63,0,1,0-2.62-2.63A2.64,2.64,0,0,0,806.89,565.42Z" transform="translate(-50 -46.63)" fill="#908820"/><rect x="795.17" y="345.66" width="34.42" height="11.9" rx="5" fill="#3f3d56" opacity="0.7"/><rect x="795.17" y="375.41" width="53.54" height="11.9" rx="5" fill="#908820" opacity="0.3"/><rect x="795.17" y="360.53" width="107.09" height="11.9" rx="5" fill="#908820" opacity="0.3"/><circle cx="762.56" cy="356.63" r="5.25" fill="#908820"/><path d="M808.53,409.45l-10.42,6.94-14.44-9.62v-3.51l14.44,9.63,8.3-5.53a7.39,7.39,0,0,1-.1-8H783.24a3.52,3.52,0,0,0-3.51,3.51v21.88a3.52,3.52,0,0,0,3.51,3.51H813a3.51,3.51,0,0,0,3.5-3.51v-15.2a7.37,7.37,0,0,1-8-.06Z" transform="translate(-50 -46.63)" fill="#908820"/><ellipse cx="509.64" cy="746.02" rx="26.08" ry="5.01" fill="#908820"/><ellipse cx="509.21" cy="743.89" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="739.03" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="734.17" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="729.31" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="724.46" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="719.6" rx="3.04" ry="3.97" fill="#3f3d56"/><ellipse cx="509.21" cy="714.74" rx="3.04" ry="3.97" fill="#3f3d56"/><path d="M570.57,728.13a13.59,13.59,0,0,0,1.13-1.67l-8-1.31,8.62.07a14.59,14.59,0,0,0,.28-11.52l-11.57,6,10.67-7.85a14.54,14.54,0,1,0-24,16.28,15.11,15.11,0,0,0-1.66,2.65l10.36,5.38-11-3.7a14.53,14.53,0,0,0,2.34,13.65,14.54,14.54,0,1,0,22.86,0,14.55,14.55,0,0,0,0-18Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M544.6,737.12a14.45,14.45,0,0,0,3.11,9,14.54,14.54,0,1,0,22.86,0C572.52,743.64,544.6,735.49,544.6,737.12Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M802,436.86a13.68,13.68,0,0,1-1-2.7c-.1-1.07.32-2.48-.63-3-.53-.29-1.27-.07-1.74-.45a2.15,2.15,0,0,1-.51-1.2,4.1,4.1,0,0,0-3.8-3c1-2.71,4.68-3.37,6.3-5.76.81-1.19.55-3.38-.88-3.39a2.49,2.49,0,0,0-1,.25l-5.33,2.2a27.35,27.35,0,0,0-3,1.39c-2.93,1.67-5.12,4.38-7.09,7.13a30.49,30.49,0,0,0-4.41,8,18,18,0,0,0-.94,5.49c0,.19,0,.38,0,.56-.87.15-1.58.53-2.49.71l-2.07,5.47a6.93,6.93,0,0,1-1.35,2.49,12.52,12.52,0,0,1-1.46,1.15,8.45,8.45,0,0,0-2.91,7.37,7.93,7.93,0,0,0-4.66,9.78,9.31,9.31,0,0,0-6.81,6,4.43,4.43,0,0,1-.89,1.88,3.55,3.55,0,0,1-2.37.73c-2.34.15-4.38,2-6.49,1a13.21,13.21,0,0,0-7.71-1.08,5.77,5.77,0,0,1-1.65.15,4.43,4.43,0,0,1-1.35-.47,10.46,10.46,0,0,0-3.06-1.21,2.84,2.84,0,0,0-2.9,1.12c-.74-.81-1.18-1.86-1.85-2.73a3,3,0,0,0-2.82-1.35c.7-.63,0-1.89-.93-2.11a11.56,11.56,0,0,0-2.81.1c-2.44,0-4.32-2.24-6.69-2.83-2.18-.53-5.09.11-6.21-1.85-.35-.63-.45-1.45-1-1.92a3.64,3.64,0,0,0-.71-1.45c-.9-1-2.41-1.16-3.76-1.12a15.68,15.68,0,0,0-4.37.75c0-.17-.09-.36-.15-.56a11.09,11.09,0,0,0-4.84-5.94A20,20,0,0,0,688,454.2c0-.18,0-.35,0-.53a22.55,22.55,0,0,1,1.16-7.43c.08-.26.18-.52.27-.78a26.6,26.6,0,0,0,12-44.63,6.83,6.83,0,0,0-.21-1.6,7.86,7.86,0,0,0-1.6-3.12c-1.22-2.72-3.12-3.92-5.55-5.06-.7-.33-1.44-.65-2.22-1-3.87-1.74-7.61-4.3-12-4.29-4,0-7.86,2.21-11.82,2.13l-.3,0h-.29l-.36,0c-1.11-.13-2.44-.39-3.17.46a4.5,4.5,0,0,0-.56,1.09,2.64,2.64,0,0,1-.2.37h0a4.4,4.4,0,0,1-.51.69h0c-1.28,1.4-3.43,2-5.2,2.94-4,2.05-6.62,6.23-7.66,10.61s-.68,9,.1,13.44c.4,2.22,1.26,4.74,2.84,6.14h0l0,0s-13.37,9.51-10.93,10.86c1.33.74,6.87-2.77,11.64-6.12.05.2.09.41.15.61a44,44,0,0,0,2.18,6.25c1,2.39,2.44,5,4.93,5.69a5.36,5.36,0,0,0,1.69.19l.44,0a39.39,39.39,0,0,1-1.15,10.87,6.61,6.61,0,0,0-4.09,1.13,3.29,3.29,0,0,1-1.66.88,2.89,2.89,0,0,1-1.71-.84,9.7,9.7,0,0,0-12.14.64c-4.84,4.57-4.29,13.49-10,16.95-1.56.95-3.56,1.46-4.48,3.05a11.41,11.41,0,0,1-.77,1.47,4.05,4.05,0,0,1-1.36,1,26.48,26.48,0,0,0-4.68,2.7,6.72,6.72,0,0,0-2.69,4.54,8.61,8.61,0,0,0,.82,3.81,36.44,36.44,0,0,0,3.12,6,6.38,6.38,0,0,0-1.26,2.95c-.4,2.81.94,5.58,1.09,8.42.13,2.48-.66,4.9-1.1,7.34-1.76,9.54,1.61,19.2,5,28.26a14.58,14.58,0,0,1,1.2,4.65c.09,3-1.6,5.67-2.07,8.59-1,6.14,3.53,11.92,3.63,18.13,0,2.87-.86,5.78-.14,8.55.47,1.85,1.62,3.44,2.44,5.17,1.44,3.06,1.83,6.53,3.21,9.62a23.07,23.07,0,0,0,7.12,8.48c1.73,5.49-.73,12.12,0,18q1.41-.56,2.85-1a41.17,41.17,0,0,0,6.3,24.84c1.55,2.42,3.37,4.68,4.54,7.31a34.26,34.26,0,0,1,1.77,5.79q2.51,10.47,5,20.92c1.36,5.65,2.73,11.4,2.32,17.2-.28,4-1.3,8.57,1.18,11.77a8.51,8.51,0,0,1,1.49,2.06,5.22,5.22,0,0,1,0,2.72,180.71,180.71,0,0,0-2.48,20.39c-.56.75-1.12,1.51-1.63,2.26-4.11,6-6.09,13.21-7.69,20.32a245.83,245.83,0,0,0-5.16,42.44,28.38,28.38,0,0,0-5.26,6.41c-2.15,4-2.43,8.7-2.66,13.24a4.73,4.73,0,0,0,.24,2.18A4.48,4.48,0,0,0,644.2,822l1.24.59c-.27,1-.51,2-.72,3.05-1.58-1.78-4.39-4.6-4.87-2.54a101.78,101.78,0,0,0-2.56,21,1.77,1.77,0,0,0,1.9,2.12A116.25,116.25,0,0,0,660,848c5.65,0,11.29-.46,16.92-.89l5.77-.44a127.7,127.7,0,0,0,17.67-2.19c2.55-.56,5.18-1.35,7.1-3.14s2.86-4.85,1.54-7.12v0c-1.61-2.74-5.37-2.94-8.51-3.38a27.85,27.85,0,0,1-8.92-2.84c-.63-.32-1.24-.68-1.85-1,.16,0,.33-.08.48-.14,2.7-.91,4.85-3,7.39-4.25,3-1.45,6.31-1.78,9.57-2.19s6.62-1,9.33-2.86c2.54-1.74,4.34-4.91,3.75-7.89a5.15,5.15,0,0,0-.16-.62c-1-3.23-4.5-5-7.76-5.8s-6.8-1.28-9.44-3.38l-.39-.31c-.22-.19-.45-.37-.69-.56a5.62,5.62,0,0,0,.77-3.26c-.14-1.79-1.18-3.37-1.89-5-1.39-3.25-1.5-6.88-1.88-10.39-.94-8.83-3.65-17.35-5.69-26-.23-1-.45-2-.67-3a62.86,62.86,0,0,1,7.27-11.64,38,38,0,0,0,7-13.77c.42-1.54.73-3.1,1.05-4.67Q713.18,694.18,718,667a45.19,45.19,0,0,0,.91-7.84,23.89,23.89,0,0,0,1.35-7.43c.94-3.67,1.89-7.52,1.12-9.92-.54-1.7-.81-7.51-1.11-13.08a50.24,50.24,0,0,0-.54-8.93c-.09-.51-.19-1-.3-1.51q-.09-.45-.18-.75a25.68,25.68,0,0,0-1.76-4.86,23.63,23.63,0,0,0-1.32-2.71,12.34,12.34,0,0,0-1-1.48,62,62,0,0,1,6.22.58c.5.07,1.12.11,1.43-.3A1.47,1.47,0,0,0,723,608a12.75,12.75,0,0,0-1.62-6.76c-.8-1.42-1.91-2.8-1.88-4.43a6.53,6.53,0,0,1,.55-2.2l2.77-7.44c2.35-6.29,1.23-13.58,2.45-20.18a118.46,118.46,0,0,0,1.82-15.61c.23-4.47.09-9.32-2.6-12.9a14.73,14.73,0,0,1-2-2.84,8.41,8.41,0,0,1-.48-3.39q-.07-10.51.42-21a206.38,206.38,0,0,0,35.05-4,22,22,0,0,0,4-1.08,20.19,20.19,0,0,0,4-2.53c3.93-2.84,7.92-5.73,10.91-9.55a32.36,32.36,0,0,0,6.46-15.94c2.84-2.63,4.35-6.37,5.77-10a15.42,15.42,0,0,0,1.32-4.85c.07-1.72-1.06-3.52-.74-5.21a13.78,13.78,0,0,1,4.19-7.58,2.74,2.74,0,0,0,1-1.33,2.12,2.12,0,0,0-.23-1.3l-.09-.15c.28-.17.54-.35.81-.53.93-.66,1.82-1.39,2.69-2.1a17,17,0,0,0,3.49-3.54A5.75,5.75,0,0,0,802,436.86ZM673.69,820.15c-.24,0-.48,0-.71.06a9.57,9.57,0,0,0-2.54.73l-.19.1c1.1-2.38,2.14-4.8,3.08-7.26.11-.29.21-.58.32-.88l-.12,1.64A20.22,20.22,0,0,0,673.69,820.15Z" transform="translate(-50 -46.63)" fill="url(#f0793c8d-2cc7-4dcf-96d3-b23677fc2573)"/><path d="M688,455.73c.1,3.88,1.42,7.64,1.62,11.52-11.43.57-22.72-5-34-3.28,3.17-2.05,4.86-5.74,5.86-9.37a38.9,38.9,0,0,0,1.11-14.41,6.61,6.61,0,0,0-.14-.79c-.42-1.9-1.51-4.59-1.22-6.08.39-2,2.25-1.59,3.93-1.48a150.51,150.51,0,0,1,15.26,2c1.32.23,14.13,3.43,14.33,3.1a59.69,59.69,0,0,0-5.55,11.43A22.32,22.32,0,0,0,688,455.73Z" transform="translate(-50 -46.63)" fill="#ee8e9e"/><path d="M801.05,443.73a16.64,16.64,0,0,1-3.49,3.52c-.87.71-1.76,1.44-2.69,2.09a11.5,11.5,0,0,1-5.72,2.33c-2.72.18-8.51-1.42-10.2-3.75a6.77,6.77,0,0,1-1-3.91,17.88,17.88,0,0,1,.94-5.46,30.42,30.42,0,0,1,4.41-8c2-2.74,4.16-5.43,7.09-7.09a27.35,27.35,0,0,1,3-1.39l5.33-2.18a2.5,2.5,0,0,1,1-.26c1.43,0,1.69,2.2.88,3.38-1.62,2.38-5.27,3-6.3,5.73a4.09,4.09,0,0,1,3.8,2.95,2.11,2.11,0,0,0,.51,1.19c.47.37,1.21.16,1.74.45.95.5.53,1.91.63,3a13.5,13.5,0,0,0,1,2.68A5.69,5.69,0,0,1,801.05,443.73Z" transform="translate(-50 -46.63)" fill="#ee8e9e"/><path d="M643.4,833.08c.88-.88,2.32-.73,3.55-.54a132,132,0,0,0,19.95,1.61c.84-4.08-1.59-8-2.37-12.14a10.76,10.76,0,0,0-.8-3.08c-.94-1.8-3.06-2.63-5.05-3-3.59-.76-9.21-2.09-11.22,1.54C645.05,821.78,644.28,828.22,643.4,833.08Z" transform="translate(-50 -46.63)" fill="#65617d"/><path d="M716.49,817.65c-2.71,1.85-6.07,2.42-9.33,2.84s-6.61.74-9.57,2.19c-2.54,1.26-4.69,3.32-7.39,4.22a15.31,15.31,0,0,1-7.67.12c-3-.57-6.26-1.67-7.89-4.29-1.46-2.32-1.31-5.27-1.11-8,.24-3.09.47-6.2.69-9.3a4.4,4.4,0,0,1,.58-2.24c2.19-3.2,7.52,1.33,10.37,1.33,4.52,0,9.37-2.46,12.62-5.45a3.89,3.89,0,0,1,1-.74c1.29-.58,2.58.49,3.72,1.42l.39.31c2.64,2.09,6.18,2.52,9.44,3.37s6.75,2.55,7.76,5.77a4.57,4.57,0,0,1,.16.61C720.83,812.76,719,815.91,716.49,817.65Z" transform="translate(-50 -46.63)" fill="#3f3d56"/><path d="M716.49,817.65c-2.71,1.85-6.07,2.42-9.33,2.84s-6.61.74-9.57,2.19c-2.54,1.26-4.69,3.32-7.39,4.22a15.31,15.31,0,0,1-7.67.12c-3-.57-6.26-1.67-7.89-4.29-1.46-2.32-1.31-5.27-1.11-8,.24-3.09.47-6.2.69-9.3a4.4,4.4,0,0,1,.58-2.24c2.19-3.2,7.52,1.33,10.37,1.33,4.52,0,9.37-2.46,12.62-5.45a3.89,3.89,0,0,1,1-.74c1.29-.58,2.58.49,3.72,1.42l.39.31c2.64,2.09,6.18,2.52,9.44,3.37s6.75,2.55,7.76,5.77a4.57,4.57,0,0,1,.16.61C720.83,812.76,719,815.91,716.49,817.65Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M716.49,818.69c-2.71,1.85-6.07,2.43-9.33,2.85s-6.61.73-9.57,2.18c-2.54,1.26-4.69,3.32-7.39,4.22a15.31,15.31,0,0,1-7.67.12,12.6,12.6,0,0,1-7.89-5.33c-1.46-2.32-1.31-5.27-1.11-8,.24-3.09.47-6.2.69-9.3a4.4,4.4,0,0,1,.58-2.24c2.19-3.2,7.52,1.33,10.37,1.33,4.52,0,9.37-2.46,12.62-5.45a3.89,3.89,0,0,1,1-.74c1.29-.58,2.58.49,3.72,1.42a26.4,26.4,0,0,1-3.17,6.56,13.63,13.63,0,0,1-10,6.22c-2,.15-4.09-.25-6,.27s-3.77,2.52-3.08,4.42,3.06,2.25,5,2.36a22.38,22.38,0,0,0,8.21-.58c1.6-.53,3.07-1.41,4.62-2.06,6.62-2.74,15-1.21,20.46-5.85a13,13,0,0,1,1.67-1.28C721.67,812.8,719,817,716.49,818.69Z" transform="translate(-50 -46.63)" fill="#e8eaf2"/><path d="M716.49,818.69c-2.71,1.85-6.07,2.43-9.33,2.85s-6.61.73-9.57,2.18c-2.54,1.26-4.69,3.32-7.39,4.22a15.31,15.31,0,0,1-7.67.12,12.6,12.6,0,0,1-7.89-5.33c-1.46-2.32-1.31-5.27-1.11-8,.24-3.09.47-6.2.69-9.3a4.4,4.4,0,0,1,.58-2.24c2.19-3.2,7.52,1.33,10.37,1.33,4.52,0,9.37-2.46,12.62-5.45a3.89,3.89,0,0,1,1-.74c1.29-.58,2.58.49,3.72,1.42a26.4,26.4,0,0,1-3.17,6.56,13.63,13.63,0,0,1-10,6.22c-2,.15-4.09-.25-6,.27s-3.77,2.52-3.08,4.42,3.06,2.25,5,2.36a22.38,22.38,0,0,0,8.21-.58c1.6-.53,3.07-1.41,4.62-2.06,6.62-2.74,15-1.21,20.46-5.85a13,13,0,0,1,1.67-1.28C721.67,812.8,719,817,716.49,818.69Z" transform="translate(-50 -46.63)" opacity="0.05"/><path d="M644.64,616.07a40.77,40.77,0,0,0,5.58,30c1.55,2.4,3.37,4.66,4.54,7.27a33.77,33.77,0,0,1,1.77,5.77l5,20.8c1.36,5.62,2.73,11.34,2.32,17.11-.28,4-1.3,8.53,1.18,11.71a8.46,8.46,0,0,1,1.49,2.05,5.15,5.15,0,0,1,0,2.7c-2.54,15.11-4.43,30.9.16,45.51,4.17,13.25,4.41,27.93,7.95,41.35a9.24,9.24,0,0,0,2.19,4.59,7.27,7.27,0,0,0,3.3,1.56c5.22,1.25,10.73-.21,15.59-2.46,3.42-1.59,7.1-4.37,6.81-8.12-.14-1.79-1.18-3.36-1.89-5-1.39-3.23-1.5-6.83-1.88-10.33-.94-8.78-3.65-17.26-5.69-25.84s-3.44-17.57-1.71-26.23a229.79,229.79,0,0,1,16.87-51.9,6.27,6.27,0,0,1,1.55-2.32,16.86,16.86,0,0,0,2.19-1.49,6.66,6.66,0,0,0,1-2.72c.76-2.74,3-4.77,4.53-7.17,3.83-6.08,2.62-13.87,2.48-21.05-.14-7,.86-14-.28-20.92S714.65,607,708,604.88c-3.29-1.07-6.85-.82-10.29-.48C679.48,606.2,661.76,609.51,644.64,616.07Z" transform="translate(-50 -46.63)" fill="#696791"/><path d="M644.64,617.07a40.77,40.77,0,0,0,5.58,30c1.55,2.4,3.37,4.66,4.54,7.27a33.77,33.77,0,0,1,1.77,5.77l5,20.8c1.36,5.62,2.73,11.34,2.32,17.11-.28,4-1.3,8.53,1.18,11.71a8.46,8.46,0,0,1,1.49,2.05,5.15,5.15,0,0,1,0,2.7c-2.54,15.11-4.43,30.9.16,45.51,4.17,13.25,4.41,27.93,7.95,41.35a9.24,9.24,0,0,0,2.19,4.59,7.27,7.27,0,0,0,3.3,1.56c5.22,1.25,10.73-.21,15.59-2.46,3.42-1.59,7.1-4.37,6.81-8.12-.14-1.79-1.18-3.36-1.89-5-1.39-3.23-1.5-6.83-1.88-10.33-.94-8.78-3.65-17.26-5.69-25.84s-3.44-17.57-1.71-26.23a229.79,229.79,0,0,1,16.87-51.9,6.27,6.27,0,0,1,1.55-2.32,16.86,16.86,0,0,0,2.19-1.49,6.66,6.66,0,0,0,1-2.72c.76-2.74,3-4.77,4.53-7.17,3.83-6.08,2.62-13.87,2.48-21.05-.14-7,.86-14-.28-20.92S714.65,608,708,605.88c-3.29-1.07-6.85-.82-10.29-.48C679.48,607.2,661.76,610.51,644.64,617.07Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M718.92,659.86a44,44,0,0,1-.91,8.07q-4.83,27-10.28,54c-.32,1.56-.63,3.12-1.05,4.65a37.78,37.78,0,0,1-7,13.7c-15.15,18.89-16.46,46.32-24.68,69.1-.56,1.54-1.11,3.09-1.7,4.61a115.75,115.75,0,0,1-5.07,11.31,3.22,3.22,0,0,1-1,1.35,3.08,3.08,0,0,1-1.65.33,48.31,48.31,0,0,1-21.36-4.86,4.48,4.48,0,0,1-2.27-1.89,4.66,4.66,0,0,1-.24-2.17c.23-4.51.51-9.19,2.66-13.16a28.22,28.22,0,0,1,5.26-6.38,242.36,242.36,0,0,1,5.16-42.22c1.6-7.08,3.58-14.23,7.69-20.21,3-4.42,7.38-8.78,6.84-14.11-.23-2.34-1.21-5.25.69-6.62.83-.61,2.06-.69,2.63-1.55a3,3,0,0,0,.37-1.32,32.66,32.66,0,0,0-.68-10.93,58.79,58.79,0,0,1-1.63-7.13,9,9,0,0,1,1.79-6.88c.1-4.87,0-9.92.07-14.78a27.16,27.16,0,0,0-.69-8.25c-.36-1.27-.93-2.49-1.21-3.79-1-4.67,1.52-10.27-1.5-14-1.26-1.54-3.2-2.31-4.85-3.42a17.71,17.71,0,0,1-6.66-9.57A52.59,52.59,0,0,1,655.75,622a11.28,11.28,0,0,1,0-2.53,4.35,4.35,0,0,1,.34-1.27c.94-2.11,3.46-2.93,5.71-3.47a185.15,185.15,0,0,1,43.61-5.1c5.44-3.7,8.49-2.37,10.8,1.54a41.09,41.09,0,0,1,3.07,7.51c1,3.18,1.12,21,2.14,24.17C722.72,647,718.87,655.5,718.92,659.86Z" transform="translate(-50 -46.63)" fill="#696791"/><path d="M620.68,497.92c-.4,2.8.94,5.55,1.09,8.38.13,2.47-.66,4.88-1.1,7.31-1.76,9.48,1.61,19.09,5,28.11a14.4,14.4,0,0,1,1.2,4.62c.09,2.94-1.6,5.64-2.07,8.55-1,6.11,3.53,11.85,3.63,18,0,2.85-.86,5.75-.14,8.5.47,1.84,1.62,3.43,2.43,5.14,1.45,3,1.84,6.5,3.22,9.57,2.49,5.55,7.84,9.18,12.94,12.51,2.89-1,6-2.11,7.62-4.72a11.63,11.63,0,0,0,1.33-4.14c1.1-6.33,1.06-12.79,1-19.21A118.59,118.59,0,0,0,656,564c-1-7-3.29-13.7-4.92-20.57-2-8.65-3.09-17.59-6.28-25.9-2.81-7.29-7.17-13.84-11.77-20.12-1.75-2.39-3.14-5.81-6.54-5.26A7.13,7.13,0,0,0,620.68,497.92Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M620.68,495.92c-.4,2.8.94,5.55,1.09,8.38.13,2.47-.66,4.88-1.1,7.31-1.76,9.48,1.61,19.09,5,28.11a14.4,14.4,0,0,1,1.2,4.62c.09,2.94-1.6,5.64-2.07,8.55-1,6.11,3.53,11.85,3.63,18,0,2.85-.86,5.75-.14,8.5.47,1.84,1.62,3.43,2.43,5.14,1.45,3,1.84,6.5,3.22,9.57,2.49,5.55,7.84,9.18,12.94,12.51,2.89-1,6-2.11,7.62-4.72a11.63,11.63,0,0,0,1.33-4.14c1.1-6.33,1.06-12.79,1-19.21A118.59,118.59,0,0,0,656,562c-1-7-3.29-13.7-4.92-20.57-2-8.65-3.09-17.59-6.28-25.9-2.81-7.29-7.17-13.84-11.77-20.12-1.75-2.39-3.14-5.81-6.54-5.26A7.13,7.13,0,0,0,620.68,495.92Z" transform="translate(-50 -46.63)" opacity="0.05"/><path d="M694.66,436.91a59.69,59.69,0,0,0-5.55,11.43,26.19,26.19,0,0,1-6.3.76c-8.25,0-14.08-4.69-18.93-10.62-.42-1.9-3-3.67-2.74-5.16.39-2,2.25-1.59,3.93-1.48a150.51,150.51,0,0,1,15.26,2C681.65,434,694.46,437.24,694.66,436.91Z" transform="translate(-50 -46.63)" opacity="0.1"/><circle cx="632.81" cy="375.34" r="26.41" fill="#ee8e9e"/><path d="M716.18,611.19c-4.57-.27-9.18-.08-13.76.1a47.15,47.15,0,0,0-8.55.88c-3.47.79-6.71,2.37-10.13,3.36-5.48,1.59-11.26,1.66-16.93,2.22a94.36,94.36,0,0,0-11.09,1.74,4.35,4.35,0,0,1,.34-1.27c.94-2.11,3.46-2.93,5.71-3.47a185.15,185.15,0,0,1,43.61-5.1C710.82,606,713.87,607.28,716.18,611.19Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M698.18,465.21c-1.15,1.33-2.7,2.8-4.4,2.37a6.08,6.08,0,0,1-1.72-.92c-3-2-6.78-2.42-10.36-3a152.4,152.4,0,0,1-21.34-4.8,21.93,21.93,0,0,0-6.32-1.28c-4.47,0-8.44,3-11.24,6.48s-4.8,7.57-7.55,11.1c-2,2.52-4.3,4.74-6.13,7.35-4.62,6.59-5.62,15.19-4.42,23.15s4.37,15.47,7.53,22.88A78.47,78.47,0,0,0,638.37,541c-1.06,6-.5,13,2.38,18.4.93,1.77,2.08,3.49,2.39,5.47a13.85,13.85,0,0,1-.16,4l-1.35,9.56c-.41,2.91-.82,5.82-1.35,8.72-1,5.29-2.28,11,.17,15.82,2.93,5.74-.15,13.09.62,19.49a97.8,97.8,0,0,1,25.74-6.11c5.67-.56,11.45-.63,16.93-2.22,3.42-1,6.66-2.57,10.13-3.37a47,47,0,0,1,8.55-.87c6.33-.25,12.7-.51,19,.42.5.08,1.12.12,1.43-.29a1.47,1.47,0,0,0,.19-.75,12.63,12.63,0,0,0-1.62-6.72c-.8-1.41-1.91-2.79-1.88-4.41a6.48,6.48,0,0,1,.55-2.19l2.77-7.4c2.35-6.25,1.23-13.51,2.45-20.07a117.46,117.46,0,0,0,1.82-15.53c.23-4.45.09-9.27-2.6-12.83a14.67,14.67,0,0,1-2-2.83,8.34,8.34,0,0,1-.48-3.37q-.09-13.36.73-26.72c-2.34-4.21-4.79-8.51-7.13-12.72-2.73-4.9-5.49-9.9-6.59-15.4-.73-3.63-.71-7.38-1.23-11.05a4.9,4.9,0,0,0-.93-2.59,4.69,4.69,0,0,0-3.76-1.11A16.44,16.44,0,0,0,698.18,465.21Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M794.87,449.34a11.5,11.5,0,0,1-5.72,2.33c-2.72.18-8.51-1.42-10.2-3.75a6.77,6.77,0,0,1-1-3.91,4.91,4.91,0,0,1,1.14-.22,29.88,29.88,0,0,1,8.86.36c2.88.7,5.66,2.45,6.87,5.16Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M707,506.46c1.61,2,3.28,4.06,5.56,5.25,2.63,1.36,5.72,1.39,8.67,1.33a208,208,0,0,0,36.23-4,22.81,22.81,0,0,0,4-1.07,20.6,20.6,0,0,0,4-2.52c3.93-2.82,7.92-5.7,10.91-9.51a32.05,32.05,0,0,0,6.46-15.85c2.84-2.61,4.35-6.34,5.77-9.93a15.24,15.24,0,0,0,1.32-4.82c.07-1.71-1.06-3.5-.74-5.18a13.64,13.64,0,0,1,4.19-7.54,2.74,2.74,0,0,0,1-1.33,2.1,2.1,0,0,0-.23-1.29c-1.22-2.71-4-4.45-6.88-5.16a29.61,29.61,0,0,0-8.87-.36,20.06,20.06,0,0,0-2.9.78l-2.07,5.43a6.88,6.88,0,0,1-1.35,2.48,12.52,12.52,0,0,1-1.46,1.15,8.38,8.38,0,0,0-2.91,7.33,7.88,7.88,0,0,0-4.66,9.72,9.3,9.3,0,0,0-6.81,6,4.45,4.45,0,0,1-.89,1.87A3.66,3.66,0,0,1,753,480c-2.34.14-4.38,2-6.49,1a13.29,13.29,0,0,0-7.71-1.08,5.77,5.77,0,0,1-1.65.15,4.43,4.43,0,0,1-1.35-.47,10.72,10.72,0,0,0-3.06-1.2,2.84,2.84,0,0,0-2.9,1.12c-.74-.81-1.18-1.85-1.85-2.72a3,3,0,0,0-2.82-1.35c.7-.62,0-1.87-.93-2.1a12.66,12.66,0,0,0-2.81.1c-2.44,0-4.32-2.23-6.69-2.81-2.18-.53-5.09.11-6.21-1.84-.39-.69-.47-1.63-1.14-2a2.2,2.2,0,0,0-1.48-.17,14.24,14.24,0,0,0-4.45,1.35c-2.29,9-1.09,18.24.65,27.3A24.07,24.07,0,0,0,707,506.46Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M706.93,538.51l-11.37,22.94c1.23-2.13,3.82-3,5.87-4.31A15.54,15.54,0,0,0,707,549.6a47.14,47.14,0,0,0,2.15-9.29c.23-1.45.71-3.49,0-4.81C707.89,535.77,707.47,537.44,706.93,538.51Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M674.21,558c-2.57,2.54-5.71,4.41-8.53,6.66-6.6,5.26-11.3,12.46-15.89,19.53a30.77,30.77,0,0,0-3.25,5.92c.54.46,1.34,0,1.88-.49l8.93-7.84a20.19,20.19,0,0,0,3-3c1.55-2.09,2.24-4.69,3.65-6.88a26.84,26.84,0,0,1,5.43-5.53c2.05-1.76,11.47-9.73,9.18-12.83C676.92,554.34,675.56,556.64,674.21,558Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M693.72,458.58a26.78,26.78,0,0,0-9.45-3.11,20,20,0,0,0-6-.43c-1.86.23-3.65.95-5.52,1-2.63.15-5.14-.92-7.68-1.61s-5.47-1-7.6.57a3.29,3.29,0,0,1-1.66.88,3,3,0,0,1-1.71-.84,9.74,9.74,0,0,0-12.14.64c-4.84,4.54-4.29,13.41-10,16.86-1.56,1-3.56,1.45-4.48,3a12,12,0,0,1-.77,1.46,4,4,0,0,1-1.36,1,26.43,26.43,0,0,0-4.68,2.69,6.66,6.66,0,0,0-2.69,4.51,8.53,8.53,0,0,0,.82,3.79,35.62,35.62,0,0,0,34.61,22.13,5.42,5.42,0,0,0,3.63-1.13,6.93,6.93,0,0,0,1.27-2.09c2.67-5.59,7.68-9.72,10.86-15,1.89-3.17,3.11-6.72,5.09-9.84,3-4.73,7.54-8.23,12-11.62,2.52-1.91,5.31-3.92,8.47-3.79,2.81.12,4.86.27,3.82-3.17A11,11,0,0,0,693.72,458.58Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M693.72,458.58a26.78,26.78,0,0,0-9.45-3.11,20,20,0,0,0-6-.43c-1.86.23-3.65.95-5.52,1-2.63.15-5.14-.92-7.68-1.61s-5.47-1-7.6.57a3.29,3.29,0,0,1-1.66.88,3,3,0,0,1-1.71-.84,9.74,9.74,0,0,0-12.14.64c-4.84,4.54-4.29,13.41-10,16.86-1.56,1-3.56,1.45-4.48,3a12,12,0,0,1-.77,1.46,4,4,0,0,1-1.36,1,26.43,26.43,0,0,0-4.68,2.69,6.66,6.66,0,0,0-2.69,4.51,8.53,8.53,0,0,0,.82,3.79,35.62,35.62,0,0,0,34.61,22.13,5.42,5.42,0,0,0,3.63-1.13,6.93,6.93,0,0,0,1.27-2.09c2.67-5.59,7.68-9.72,10.86-15,1.89-3.17,3.11-6.72,5.09-9.84,3-4.73,7.54-8.23,12-11.62,2.52-1.91,5.31-3.92,8.47-3.79,2.81.12,4.86.27,3.82-3.17A11,11,0,0,0,693.72,458.58Z" transform="translate(-50 -46.63)" opacity="0.05"/><path d="M707.47,841.36c-1.92,1.79-4.55,2.57-7.1,3.12a127.7,127.7,0,0,1-17.67,2.19l-5.77.44c-5.63.43-11.27.86-16.92.88a117.17,117.17,0,0,1-20.82-1.8,2.28,2.28,0,0,1-1.65-.8,2.24,2.24,0,0,1-.25-1.31,100.74,100.74,0,0,1,2.56-20.9c.65-2.81,5.7,3.5,5.93,3.77a5.76,5.76,0,0,0,4.59,2.43c5.81.32,12.32-3.21,16.93-6.35a22.18,22.18,0,0,1,3.14-1.95,9.19,9.19,0,0,1,2.54-.72,15.28,15.28,0,0,1,8.64,1.75c3.47,1.72,6.51,4.19,10,6a28.06,28.06,0,0,0,8.92,2.82c3.14.43,6.9.64,8.51,3.36v0C710.33,836.54,709.37,839.58,707.47,841.36Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M707.47,842.4c-1.92,1.79-4.55,2.57-7.1,3.12a127.7,127.7,0,0,1-17.67,2.19l-5.77.44c-5.63.43-11.27.86-16.92.88a116.24,116.24,0,0,1-20.82-1.8,2.27,2.27,0,0,1-1.65-.79,9.26,9.26,0,0,1-1.29-2.36c.12-7,2-14.05,3.6-20.9.65-2.81,5.7,3.5,5.93,3.77a5.76,5.76,0,0,0,4.59,2.43c5.81.32,12.32-3.21,16.93-6.35a22.18,22.18,0,0,1,3.14-1.95,9.19,9.19,0,0,1,2.54-.72c-.75,4.18-6.74,9.23-9.84,10.77-6.26,3.11-13.7,1.55-20.68,1.92a3,3,0,0,0-1.47.35c-.83.56-.88,1.75-.86,2.75.06,1.66.27,3.62,1.72,4.46a4.94,4.94,0,0,0,2.32.49,327.89,327.89,0,0,0,51.57-1.81,28.23,28.23,0,0,0,8.4-1.87,21.44,21.44,0,0,0,4.87-3.14C711.21,836,709.37,840.62,707.47,842.4Z" transform="translate(-50 -46.63)" fill="#e8eaf2"/><path d="M693.09,421.69c-.1,2.73,1.76,8-3.35,6.55-3-.88-3.9-4.49-7.76-3.69s-4.76,5.57-6.06,8.68a16.59,16.59,0,0,1-12,9.72,7.26,7.26,0,0,1-3.31,0c-2.49-.71-3.91-3.28-4.93-5.66a44.08,44.08,0,0,1-2.18-6.22,25.31,25.31,0,0,1-.79-4.56h0a19.48,19.48,0,0,1,0-3.15c.46-6.21,4-11.87,8.52-16.15s10-7.38,15.45-10.43a22.9,22.9,0,0,1,5.7-2.54,19.21,19.21,0,0,1,6-.24,20.85,20.85,0,0,1,7.66,1.8,10.71,10.71,0,0,1,2.69,1.89,8.11,8.11,0,0,1,2.34,3.89c1,4-1.58,7.63-3.66,10.8C695.51,415.19,693.22,418.17,693.09,421.69Z" transform="translate(-50 -46.63)" fill="#434175"/><path d="M779,480.3c-1.09-2.75-5.67-10.36-4.35-13a33.77,33.77,0,0,1,3.49,6.24C779,475.69,778.32,478.55,779,480.3Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M697.47,412.37c2.08-3.17,4.64-6.79,3.66-10.8a8.35,8.35,0,0,0-3-4.44,7.27,7.27,0,0,1,.76,1.87c1,4-1.58,7.62-3.66,10.8-1.85,2.82-4.14,5.81-4.28,9.32-.1,2.73,1.77,8-3.34,6.55-3-.88-3.9-4.49-7.77-3.69s-4.75,5.56-6.06,8.68a16.57,16.57,0,0,1-12,9.72,7.26,7.26,0,0,1-3.31,0,5.05,5.05,0,0,1-1.56-.77,6.76,6.76,0,0,0,3.77,3.34,7.44,7.44,0,0,0,3.32,0,16.63,16.63,0,0,0,12-9.73c1.31-3.12,2.16-7.86,6.06-8.67s4.72,2.8,7.77,3.68c5.11,1.48,3.24-3.82,3.34-6.55C693.33,418.18,695.63,415.19,697.47,412.37Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M698.6,406.94a21.84,21.84,0,0,1-1.56,2.24,26.58,26.58,0,0,1-3.61,3.74c-.79.68-1.6,1.31-2.44,1.91a38.47,38.47,0,0,1-4.93,2.95,91.47,91.47,0,0,1-15.78,5.65l-10.54,3.07c-1.3,1-3.67,2.74-6.28,4.57a25.31,25.31,0,0,1-.79-4.56h0a19.48,19.48,0,0,1,0-3.15c.46-6.21,4-11.87,8.52-16.15s10-7.38,15.45-10.43a22.9,22.9,0,0,1,5.7-2.54,19.21,19.21,0,0,1,6-.24,20.85,20.85,0,0,1,7.66,1.8,10.71,10.71,0,0,1,2.69,1.89,10.14,10.14,0,0,1,1.35,2.86C700.7,402.7,699.78,405,698.6,406.94Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M698.6,406.23a21.84,21.84,0,0,1-1.56,2.24,26.58,26.58,0,0,1-3.61,3.74c-.79.68-1.6,1.31-2.44,1.91a39.65,39.65,0,0,1-4.93,2.95,91.47,91.47,0,0,1-15.78,5.65l-10.54,3.06c-3.68,2.79-16,11.91-18.07,10.77-2.44-1.34,10.93-10.81,10.93-10.81l.61.47c-2-1.23-3-4.08-3.42-6.55-.78-4.42-1.15-9-.1-13.37s3.67-8.51,7.66-10.55c1.77-.92,3.92-1.53,5.2-2.93a4.4,4.4,0,0,0,.51-.69,2.08,2.08,0,0,0,.2-.36,4.32,4.32,0,0,1,.56-1.09c.73-.84,2.06-.59,3.17-.45l.36,0a5.68,5.68,0,0,0,.59,0c4,.09,7.81-2.12,11.82-2.12,4.39,0,8.13,2.54,12,4.28.78.35,1.52.67,2.22,1,2.8,1.3,4.9,2.69,6,6.38C700.7,402,699.78,404.31,698.6,406.23Z" transform="translate(-50 -46.63)" fill="#908820"/><path d="M667.94,390.28a1.8,1.8,0,0,1-.42,2.32,3.2,3.2,0,0,1-1.66.59,11.2,11.2,0,0,1-1.3.1,3.58,3.58,0,0,1-1.71-.29,2.2,2.2,0,0,1-.3-.19,4.4,4.4,0,0,0,.51-.69c.1.08.2.06.59,0a9.13,9.13,0,0,0,2.14-.36,2.38,2.38,0,0,0,.8-.39,2.48,2.48,0,0,0,.76-1.15Z" transform="translate(-50 -46.63)" opacity="0.1"/><path d="M652.86,425.64a4.87,4.87,0,0,0,1.83.68,8.29,8.29,0,0,0,1.41.23,7.84,7.84,0,0,0,3.45-1" transform="translate(-50 -46.63)" opacity="0.1"/></svg>
<svg id="a75d69e6-d07c-4db3-9c83-72b2470e5561" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1108" height="731.18" viewBox="0 0 1108 731.18"><title>react</title><path d="M813.68,415.2l6.89-2.7c.66-.26,1.31-.54,2-.81a59.85,59.85,0,0,1,15.28-115.77l-4.8,24.2,18.22-23.92.16,0a59.87,59.87,0,0,1,51.16,67.45c-.15,1.13-.39,2.23-.6,3.34,5.61-4.43,10.87-9.32,14.81-15,8-11.54,10.71-25.76,13.47-43.17,5.47-34.48,10.4-69.34,14.84-104.18a59.83,59.83,0,0,1-42-65.36c.25-1.82.6-3.59,1-5.34.1-.44.2-.88.31-1.31.43-1.69.91-3.35,1.47-5,.09-.27.21-.52.3-.78.5-1.37,1-2.72,1.65-4,.22-.5.44-1,.67-1.47.69-1.43,1.42-2.83,2.22-4.19.27-.45.56-.88.83-1.33.62-1,1.27-2,1.94-2.93.36-.5.71-1,1.08-1.49l.27-.39,0,0a59.81,59.81,0,0,1,74.64-17l-6.24,31.49,18-23.64a59.5,59.5,0,0,1,16.32,68.62h.07c-.17.4-.38.78-.55,1.18-.33.74-.67,1.47-1,2.2-.46.93-1,1.84-1.45,2.74-.38.68-.75,1.36-1.16,2-.59,1-1.23,1.9-1.87,2.83-.38.54-.73,1.1-1.12,1.63-1,1.39-2.12,2.72-3.26,4-.4.45-.83.86-1.25,1.3-.81.86-1.62,1.71-2.48,2.52-.53.5-1.09,1-1.64,1.46-.79.7-1.6,1.38-2.43,2-.59.47-1.19.92-1.79,1.36-.87.63-1.75,1.22-2.64,1.81-.6.39-1.2.78-1.81,1.15-1,.61-2.05,1.17-3.1,1.72-.53.27-1,.57-1.59.83-1.58.77-3.2,1.48-4.85,2.11-.31.12-.64.21-.95.32-1.38.5-2.77,1-4.18,1.35-.61.17-1.23.31-1.85.46-1.15.28-2.31.54-3.48.76q-1,.18-2.07.33c-1.15.17-2.31.3-3.48.4-.69.06-1.37.12-2.07.16-1.26.06-2.53.07-3.8.06a2.08,2.08,0,0,0-.25,0q-2.82,22-5.92,44A59.82,59.82,0,0,1,1042,240.88l-15.68,26.58,25.21-16.59a59.68,59.68,0,0,1,11.5,44.16,58.76,58.76,0,0,1-4.5,15.94,59.83,59.83,0,0,1-111.19-4c-.23,1.48-.45,3-.68,4.46-2.44,15.35-5.47,34.46-16.23,50-7.29,10.56-17.33,18.36-27.09,25.22q-9.52,6.7-19.48,12.77a59.85,59.85,0,0,1-24.53,13.74q-16,8.19-32.69,14.81l-6.95,2.72c-6.25,2.44-12.65,4.94-19,7.6,1,.09,2,.17,3,.3a59.9,59.9,0,0,1,50.86,49.05l-42.31,23.11,42.18-1.89A59.16,59.16,0,0,1,843,534.54a59.87,59.87,0,0,1-82.7,11.74l2.1,3.91c1.4,2.58,2.78,5.15,4.12,7.7,18.33,34.74,19.66,64.74,3.75,84.49l-13-10.45c15.49-19.22,3.2-49.77-5.5-66.27-1.32-2.5-2.68-5-4-7.56C736,536.27,722.65,511.53,728,485.2,736,445.5,779.05,428.7,813.68,415.2Z" transform="translate(-46 -84.41)" fill="#f2f2f2"/><path d="M315.6,603.42l-5.38-2.11c-.52-.2-1-.43-1.54-.63a46.75,46.75,0,0,0-11.94-90.42l3.75,18.9-14.22-18.68h-.13a46.76,46.76,0,0,0-40,52.68c.12.89.31,1.74.47,2.61a55.84,55.84,0,0,1-11.56-11.73c-6.23-9-8.37-20.12-10.53-33.71C220.29,493.4,216.45,466.18,213,439a46.72,46.72,0,0,0,32.77-51.05c-.2-1.42-.47-2.8-.79-4.17l-.24-1c-.34-1.32-.71-2.62-1.15-3.88-.07-.21-.16-.41-.24-.61-.39-1.07-.82-2.12-1.28-3.15-.18-.38-.35-.77-.53-1.15-.54-1.11-1.11-2.2-1.73-3.26-.21-.36-.44-.7-.65-1-.49-.78-1-1.55-1.52-2.29-.28-.39-.55-.79-.84-1.17l-.21-.3h0a46.76,46.76,0,0,0-58.29-13.32l4.88,24.6-14.07-18.46a46.48,46.48,0,0,0-12.75,53.59h-.05c.13.31.29.61.43.92.26.58.52,1.15.8,1.72q.54,1.08,1.14,2.13c.29.54.58,1.07.9,1.59.46.75,1,1.48,1.46,2.21.3.42.57.86.88,1.27.8,1.08,1.65,2.12,2.54,3.13.31.35.65.67,1,1,.63.67,1.27,1.33,1.94,2,.41.4.85.77,1.28,1.14q.93.83,1.89,1.59c.46.36.93.71,1.4,1.06.68.49,1.37,1,2.06,1.41.47.31.94.61,1.42.9.79.48,1.6.91,2.42,1.34.42.22.82.45,1.24.66,1.24.6,2.5,1.15,3.79,1.64.24.1.5.16.74.25,1.07.39,2.16.75,3.27,1.06.47.13,1,.24,1.44.35.9.22,1.8.43,2.72.6l1.61.26c.9.13,1.81.23,2.72.31.54.05,1.07.09,1.62.12,1,.05,2,.06,3,.05h.19q2.2,17.22,4.62,34.41a46.71,46.71,0,0,0-67.48-8.14L149.49,488l-19.68-12.95a46.57,46.57,0,0,0-9,34.49A45.8,45.8,0,0,0,124.34,522a46.73,46.73,0,0,0,86.84-3.12c.18,1.15.35,2.32.53,3.48,1.9,12,4.27,26.92,12.67,39.08,5.7,8.24,13.54,14.33,21.16,19.69q7.44,5.24,15.21,10a46.88,46.88,0,0,0,19.16,10.74c8.32,4.26,16.84,8.14,25.54,11.56l5.42,2.13c4.88,1.9,9.88,3.85,14.81,5.93-.78.07-1.57.13-2.36.24A46.77,46.77,0,0,0,283.6,660l33,18-32.94-1.47a46.78,46.78,0,0,0,73.57,29.2c-.55,1-1.09,2-1.64,3.05-1.09,2-2.17,4-3.22,6-14.31,27.13-15.35,50.57-2.92,66l10.13-8.16c-12.1-15-2.5-38.87,4.3-51.76,1-1.95,2.09-3.92,3.16-5.9,9.19-17,19.61-36.37,15.46-56.94C376.29,627.08,342.64,614,315.6,603.42Z" transform="translate(-46 -84.41)" fill="#f2f2f2"/><ellipse cx="554" cy="707.68" rx="554" ry="23.5" fill="#3f3d56"/><ellipse cx="554" cy="707.68" rx="554" ry="23.5" opacity="0.1"/><ellipse cx="554" cy="694.68" rx="554" ry="23.5" fill="#3f3d56"/><path d="M131,770.73c-3-5.51.4-12.26,4.28-17.18s8.61-10,8.51-16.29c-.14-9-9.7-14.31-17.33-19.08a84.78,84.78,0,0,1-15.56-12.51,23,23,0,0,1-4.78-6.4c-1.58-3.52-1.53-7.52-1.43-11.38q.5-19.26,1.9-38.48" transform="translate(-46 -84.41)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="4"/><path d="M92.11,648.7a14,14,0,0,1,7-11.49l3.14,6.21-.09-7.52a13.86,13.86,0,0,1,4.62-.56A14,14,0,1,1,92.11,648.7Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M117.14,743.7a14,14,0,0,0,26.5-5.76,13.82,13.82,0,0,0-.91-5.69,14,14,0,0,0-26.27.15l8.77,7.13-9.64-2.24A13.89,13.89,0,0,0,117.14,743.7Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M124.07,716.45a14,14,0,0,0,4.46-27.54l.07,5.79-3.18-6.29h0a14,14,0,0,0-1.31,28.05Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M90,693.78a14,14,0,0,0,1.32-28,13.88,13.88,0,0,0-7.52,1.78l2.48,6.8-5.11-4.9a14,14,0,0,0-4.53,9.69A13.33,13.33,0,0,0,77,683,14,14,0,0,0,90,693.78Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M94,652.44c3.24.36,6.39,1.37,9.64,1.57s6.82-.58,8.88-3.1c1.11-1.37,1.66-3.09,2.59-4.57a10.08,10.08,0,0,1,3.54-3.33,14,14,0,1,1-26.24,9.32C92.94,652.35,93.47,652.39,94,652.44Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M90,693.78a14,14,0,0,0,13.35-20,10.4,10.4,0,0,0-2.82,2.83c-1,1.51-1.61,3.25-2.78,4.64-2.19,2.57-5.92,3.41-9.31,3.25s-6.66-1.11-10-1.42c-.47,0-.94-.08-1.41-.09A14,14,0,0,0,90,693.78Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M124.07,716.45a14,14,0,0,0,13.47-19.76,11.36,11.36,0,0,0-3,2.85c-1.09,1.54-1.77,3.31-3,4.73-2.36,2.64-6.34,3.57-9.93,3.48s-6.82-.93-10.28-1.19A14,14,0,0,0,124.07,716.45Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M117.14,743.7a14,14,0,0,0,26.5-5.76,13.82,13.82,0,0,0-.91-5.69,14,14,0,0,0-3.08,2.74c-1.34,1.63-2.21,3.47-3.76,5-2.87,2.82-7.5,4-11.62,4.09A62.35,62.35,0,0,1,117.14,743.7Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M152.19,764.39s-11.08-.34-14.41-2.72-17-5.22-17.87-1.4-16.65,19-4.14,19.06,29.06-2,32.39-4S152.19,764.39,152.19,764.39Z" transform="translate(-46 -84.41)" fill="#656380"/><path d="M115.54,778c12.51.1,29.06-2,32.4-4,2.53-1.54,3.54-7.09,3.88-9.65l.37,0s-.7,8.93-4,11-19.89,4.08-32.39,4c-3.61,0-4.86-1.31-4.79-3.22C111.48,777.26,112.86,778,115.54,778Z" transform="translate(-46 -84.41)" opacity="0.2"/><path d="M1101,779.73c-3-5.51.4-12.26,4.28-17.18s8.61-10,8.51-16.29c-.14-9-9.7-14.31-17.33-19.08a84.78,84.78,0,0,1-15.56-12.51,23,23,0,0,1-4.78-6.4c-1.58-3.52-1.53-7.52-1.43-11.38q.49-19.26,1.9-38.48" transform="translate(-46 -84.41)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="4"/><path d="M1062.11,657.7a14,14,0,0,1,7-11.49l3.14,6.21-.09-7.52a13.86,13.86,0,0,1,4.62-.56,14,14,0,1,1-14.68,13.36Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M1087.14,752.7a14,14,0,0,0,26.5-5.76,13.82,13.82,0,0,0-.91-5.69,14,14,0,0,0-26.27.15l8.77,7.13-9.64-2.24A13.89,13.89,0,0,0,1087.14,752.7Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M1094.07,725.45a14,14,0,0,0,4.46-27.54l.07,5.79-3.18-6.29h0a14,14,0,0,0-1.31,28.05Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M1060,702.78a14,14,0,0,0,1.32-28,13.88,13.88,0,0,0-7.52,1.78l2.48,6.8-5.11-4.9a14,14,0,0,0-4.53,9.69A13.33,13.33,0,0,0,1047,692,14,14,0,0,0,1060,702.78Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M1064,661.44c3.24.36,6.39,1.37,9.64,1.57s6.82-.58,8.88-3.1c1.11-1.37,1.66-3.09,2.59-4.57a10.08,10.08,0,0,1,3.54-3.33,14,14,0,1,1-26.24,9.32C1062.94,661.35,1063.47,661.39,1064,661.44Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M1060,702.78a14,14,0,0,0,13.35-20,10.4,10.4,0,0,0-2.82,2.83c-1,1.51-1.61,3.25-2.78,4.64-2.19,2.57-5.92,3.41-9.31,3.25s-6.66-1.11-10-1.42c-.47,0-.94-.08-1.41-.09A14,14,0,0,0,1060,702.78Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M1094.07,725.45a14,14,0,0,0,13.47-19.76,11.36,11.36,0,0,0-3,2.85c-1.09,1.54-1.77,3.31-3,4.73-2.36,2.64-6.34,3.57-9.93,3.48s-6.82-.93-10.28-1.19A14,14,0,0,0,1094.07,725.45Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M1087.14,752.7a14,14,0,0,0,26.5-5.76,13.82,13.82,0,0,0-.91-5.69,14,14,0,0,0-3.08,2.74c-1.34,1.63-2.21,3.47-3.76,5-2.87,2.82-7.5,4-11.62,4.09A62.35,62.35,0,0,1,1087.14,752.7Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M1122.19,773.39s-11.08-.34-14.41-2.72-17-5.22-17.87-1.4-16.65,19-4.14,19.06,29.06-2,32.39-4S1122.19,773.39,1122.19,773.39Z" transform="translate(-46 -84.41)" fill="#656380"/><path d="M1085.54,787c12.51.1,29.06-2,32.4-4,2.53-1.54,3.54-7.09,3.88-9.65l.37,0s-.7,8.93-4,11-19.89,4.08-32.39,4c-3.61,0-4.86-1.31-4.79-3.22C1081.48,786.26,1082.86,787,1085.54,787Z" transform="translate(-46 -84.41)" opacity="0.2"/><path d="M791.57,415.56q-9.92-3.4-20-6.3,1.66-6.79,3.09-13.65c15.14-73.51,5.24-132.73-28.57-152.22-32.42-18.7-85.44.8-139,47.39q-7.92,6.92-15.47,14.2-5.07-4.85-10.3-9.51C525.21,245.64,469,224.65,435.18,244.2c-32.39,18.75-42,74.42-28.35,144.09q2,10.35,4.59,20.58c-7.95,2.26-15.64,4.67-23,7.24C322.63,439,280.61,475,280.61,512.29c0,38.51,45.11,77.14,113.64,100.56q8.34,2.83,16.8,5.26-2.74,11-4.88,22.18c-13,68.46-2.84,122.82,29.46,141.45C469,801,525,781.2,579.52,733.54c4.31-3.77,8.63-7.77,13-11.95q8.18,7.89,16.79,15.32c52.82,45.45,105,63.81,137.26,45.12,33.33-19.29,44.16-77.69,30.1-148.73q-1.6-8.14-3.73-16.61,5.91-1.74,11.56-3.61c71.24-23.6,117.6-61.76,117.6-100.79,0-37.43-43.38-73.62-110.5-96.73Z" transform="translate(-46 -84.41)" fill="#53c1de"/><path d="M776.12,587.88c-3.4,1.13-6.88,2.21-10.44,3.27a621.6,621.6,0,0,0-31.47-78.73,623.06,623.06,0,0,0,30.25-77.58c6.35,1.84,12.53,3.78,18.47,5.83,57.51,19.79,92.59,49.06,92.59,71.62,0,24-37.89,55.21-99.4,75.59ZM750.6,638.46c6.22,31.41,7.1,59.81,3,82-3.7,19.95-11.14,33.25-20.34,38.58-19.59,11.33-61.47-3.4-106.64-42.27q-7.77-6.69-15.63-14.25a624.23,624.23,0,0,0,52.09-66.14,620.2,620.2,0,0,0,84.17-13q1.9,7.68,3.37,15ZM492.48,757.1c-19.14,6.76-34.38,6.95-43.59,1.64-19.6-11.31-27.75-55-16.63-113.5q2-10.37,4.53-20.63a625.79,625.79,0,0,0,83.75,12.13,645.09,645.09,0,0,0,53.35,65.9q-5.79,5.61-11.84,10.91c-24.11,21.08-48.28,36-69.57,43.55ZM402.84,587.73c-30.31-10.36-55.33-23.82-72.49-38.5-15.41-13.2-23.19-26.31-23.19-36.94,0-22.63,33.74-51.5,90-71.11q10.6-3.69,21.39-6.73a637.08,637.08,0,0,0,30.29,78.48,646.29,646.29,0,0,0-30.66,79.6Q410.44,590.32,402.84,587.73Zm30.05-204.54c-11.68-59.69-3.93-104.72,15.59-116,20.79-12,66.76,5.12,115.22,48.15,3.1,2.75,6.21,5.63,9.32,8.6a635.89,635.89,0,0,0-52.93,65.52,648.42,648.42,0,0,0-82.93,12.89q-2.39-9.52-4.27-19.15ZM701,449.38q-9.11-15.74-18.89-31.08c19.83,2.51,38.82,5.83,56.66,9.91a565.54,565.54,0,0,1-19.89,53.51q-8.61-16.35-17.88-32.34ZM591.64,342.91a569.3,569.3,0,0,1,36.57,44.14q-36.71-1.73-73.45,0c12.07-15.92,24.44-30.72,36.88-44.13Zm-110,106.65q-9.15,15.87-17.55,32.14c-7.73-18.33-14.34-36.36-19.74-53.77,17.73-4,36.64-7.21,56.34-9.68Q490.77,433.69,481.62,449.56Zm19.61,158.65A564.64,564.64,0,0,1,444,599c5.49-17.71,12.25-36.13,20.15-54.86q8.43,16.29,17.61,32.16Q491.1,592.49,501.23,608.21Zm91.14,75.33C579.79,670,567.24,655,555,638.79q17.85.71,36.36.71,19,0,37.51-.84A563.94,563.94,0,0,1,592.37,683.54ZM719.08,543.19A562.82,562.82,0,0,1,740,597.85a561.35,561.35,0,0,1-58,9.89q9.85-15.63,19.08-31.63,9.38-16.26,18-32.92Zm-41,19.66q-14.12,24.52-29.81,48.1c-18.44,1.32-37.49,2-56.91,2s-38.15-.6-56.27-1.78Q519,587.67,504.7,563t-26.51-50.08q12.27-25.53,26.43-50.1h0q14.14-24.56,30.13-48c18.49-1.4,37.43-2.13,56.59-2.13s38.22.74,56.69,2.14q15.81,23.37,30,47.81t26.79,49.87q-12.45,25.62-26.71,50.3Zm54.77-296.46c20.81,12,28.9,60.4,15.83,123.87q-1.29,6.19-2.8,12.35a635.74,635.74,0,0,0-83.09-13.13,625.16,625.16,0,0,0-52.53-65.56q7-6.74,14.29-13.11c45.88-39.93,88.76-55.69,108.3-44.42Z" transform="translate(-46 -84.41)" fill="#fff"/><path d="M591.34,456.79a55.5,55.5,0,1,1-55.5,55.5,55.49,55.49,0,0,1,55.5-55.5" transform="translate(-46 -84.41)" fill="#53c1de"/><path d="M605.26,104.81a16.6,16.6,0,1,1-16.6-16.6A16.52,16.52,0,0,1,605.26,104.81Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><path d="M575.4,109a16.62,16.62,0,0,1,12.49-24.54,16.6,16.6,0,1,0,1.41,33.07A16.6,16.6,0,0,1,575.4,109Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><rect x="521.91" y="43.52" width="44.47" height="69.37" fill="#3f3d56"/><rect x="521.91" y="43.52" width="44.47" height="69.37" opacity="0.1"/><path d="M497.66,379.61s-4.75,13.05-16,14.83-17.79,4.74-17.79,4.74l26.68,17.19,14.23-1.78s1.78-18.38,7.71-22.53S497.66,379.61,497.66,379.61Z" transform="translate(-46 -84.41)" fill="#a0616a"/><path d="M690.34,379.61s4.75,13.05,16,14.83,17.79,4.74,17.79,4.74l-26.68,17.19-14.23-1.78s-1.78-18.38-7.71-22.53S690.34,379.61,690.34,379.61Z" transform="translate(-46 -84.41)" fill="#a0616a"/><path d="M560.5,264.59s-7.11,3.56-9.48,10.68c0,0-13,4.15-20.16,15.41,0,0-8.9,9.49-10.08,14.23S506,338.11,506,338.11s-13.64,44.47-20.75,49.21c0,0,22.53-4.74,26.68,17.79,0,0,5.33,0,11.26-20.75s20.16-46.25,20.16-46.25,4.74-17.78,8.89-20.75,10.67-16.6,16.6-13,45.06-.6,45.06-.6S637,314.4,637.58,321.51s4.74,7.71,5.33,13.64,22.53,52.76,22.53,52.76,4.15,13,5.34,16c0,0,13-31.42,30.24-13.63,0,0-.6-11.86-5.93-19s-28.46-62.84-28.46-62.84-2.37-8.3-4.74-10.67-20.76-21.35-27.28-21.35c0,0-3.55-8.89-7.7-10.08Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><path d="M507.14,410.44a28.89,28.89,0,0,1-28.22-3.79,56.37,56.37,0,0,0-12.59-7.3c-6.29-2.54-13.81-4.42-16.1-.17-4.15,7.71,50.39,26.68,50.39,26.68S507.74,421.71,507.14,410.44Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><path d="M680.86,410.44a28.89,28.89,0,0,0,28.22-3.79,56.37,56.37,0,0,1,12.59-7.3c6.29-2.54,13.81-4.42,16.1-.17,4.15,7.71-50.39,26.68-50.39,26.68S680.26,421.71,680.86,410.44Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><path d="M579.88,167.08c-.22,2.06-.9,4.29-2.66,5.38-1.44.9-3.26.83-4.92,1.17a11.46,11.46,0,0,0-7.82,6.77,17.51,17.51,0,0,0-.75,10.55,39.22,39.22,0,0,0,1.52,5.06c1.74,4.92,3.84,9.8,7.09,13.89a59,59,0,0,0,9.3,8.77c2.11,1.72,4.29,3.47,6.89,4.28a21.75,21.75,0,0,0,5.4.72c2.25.1,4.65.17,6.6-.95,3-1.73,3.89-5.55,5.52-8.62,1.38-2.6,3.44-4.81,4.73-7.46a29.49,29.49,0,0,0,2-6.52l2.77-12.3c1-4.47,1.92-9.51-.59-13.34a2.28,2.28,0,0,0-.74-.77,2.79,2.79,0,0,0-1.52-.15,16.44,16.44,0,0,1-9.15-2.09,3.93,3.93,0,0,1-1.15-.9,4.72,4.72,0,0,1-.7-2.12,22.2,22.2,0,0,0-1.28-4.45,3,3,0,0,0-.87-1.34,3.15,3.15,0,0,0-1.41-.47,114.47,114.47,0,0,0-12.93-1.35c-1.42-.07-3.56-.64-4.54.62S580,165.58,579.88,167.08Z" transform="translate(-46 -84.41)" fill="#a0616a"/><path d="M579.88,167.08c-.22,2.06-.9,4.29-2.66,5.38-1.44.9-3.26.83-4.92,1.17a11.46,11.46,0,0,0-7.82,6.77,17.51,17.51,0,0,0-.75,10.55,39.22,39.22,0,0,0,1.52,5.06c1.74,4.92,3.84,9.8,7.09,13.89a59,59,0,0,0,9.3,8.77c2.11,1.72,4.29,3.47,6.89,4.28a21.75,21.75,0,0,0,5.4.72c2.25.1,4.65.17,6.6-.95,3-1.73,3.89-5.55,5.52-8.62,1.38-2.6,3.44-4.81,4.73-7.46a29.49,29.49,0,0,0,2-6.52l2.77-12.3c1-4.47,1.92-9.51-.59-13.34a2.28,2.28,0,0,0-.74-.77,2.79,2.79,0,0,0-1.52-.15,16.44,16.44,0,0,1-9.15-2.09,3.93,3.93,0,0,1-1.15-.9,4.72,4.72,0,0,1-.7-2.12,22.2,22.2,0,0,0-1.28-4.45,3,3,0,0,0-.87-1.34,3.15,3.15,0,0,0-1.41-.47,114.47,114.47,0,0,0-12.93-1.35c-1.42-.07-3.56-.64-4.54.62S580,165.58,579.88,167.08Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M571.85,174.15a26.93,26.93,0,0,0-7.13-1.12c-5.72-.14-11.84,1.83-15.14,6.5A15.34,15.34,0,0,0,547.29,191a23.57,23.57,0,0,0,5.2,10.66,32,32,0,0,1,4,5.24c1.58,3,1.63,6.59,2.06,10,1,8.12,4.46,15.75,5.83,23.82s.19,17.42-6.18,22.55c-1.07.86-2.37,1.77-2.46,3.14-.14,2.19,2.71,3.12,4.9,3.33,6.55.62,13.27,1.23,19.63-.46,2.63-.7,5.17-1.79,7.85-2.29,6.8-1.27,13.64,1.34,20.45,2.59a90.61,90.61,0,0,0,14.91,1.21,7.94,7.94,0,0,0,3.43-.45,2.75,2.75,0,0,0,1.74-2.74,4.76,4.76,0,0,0-1.18-2c-3.44-4.25-4.49-9.9-5.43-15.29l-3.15-18.11c-1.69-9.68-3.26-20.2,1.32-28.89,3.29-6.25,9.36-10.65,12.83-16.8a9.84,9.84,0,0,0,1.55-6.28c-.52-3.09-3.31-5.3-6.17-6.59A23.21,23.21,0,0,0,611,173c.1,4.33-1,8.71-1.71,13l-1.21,7a88.83,88.83,0,0,1-2.71,12.2,12.85,12.85,0,0,1-2.17,4.42,11.43,11.43,0,0,1-3.83,2.79,22.83,22.83,0,0,1-10.14,2.32,13.54,13.54,0,0,1-4.48-.69,19,19,0,0,1-6.17-4.34,10.11,10.11,0,0,1-1.63-1.77,9.93,9.93,0,0,1-1.11-2.67,55.42,55.42,0,0,1-1.91-7.48c-.3-1.94-.4-3.91-.54-5.87C573,186.12,573,179.72,571.85,174.15Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M565.84,288.9s-2.37,31.43,12.45,36.76,1.78-26.09,1.78-26.09l-4.75-9.48Z" transform="translate(-46 -84.41)" fill="#a0616a"/><path d="M616.23,288.9s2.37,31.43-12.45,36.76S602,299.57,602,299.57l4.75-9.48Z" transform="translate(-46 -84.41)" fill="#a0616a"/><path d="M552.79,184l-4.74,7.71s4.15,26.68,4.74,31.42a62.2,62.2,0,0,0,2.38,10.08s.59,14.23.59,19.57,3,14.23,3,14.23a64.82,64.82,0,0,0,3,14.82c2.37,6.52,4.74,14.23,4.74,14.23s3-8.3,14.82,4.15a7.18,7.18,0,0,0,0-10.08s-3.55-16-3.55-24.31-6.53-48-6.53-48S567,172.7,552.79,184Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M551.61,183.37l-4.74,7.71s4.15,26.68,4.74,31.42A61.84,61.84,0,0,0,554,232.58s.59,14.23.59,19.56,3,14.23,3,14.23a64.53,64.53,0,0,0,3,14.82c2.37,6.53,4.75,14.23,4.75,14.23s3-8.3,14.82,4.15a7.18,7.18,0,0,0,0-10.07s-3.56-16-3.56-24.31-6.52-48-6.52-48S565.84,172.1,551.61,183.37Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M629.87,179.22l3,3.53s3.49,20.18-4.22,45.68l-2.37,23.12s-4.15,19.57-4.74,24.31-.59,5.33-1.78,7.11a2.29,2.29,0,0,0,0,3l-3.56,13s-5.33-6.52-16,1.78c0,0-3-4.74-.6-8.89s2.37-5.93,1.78-7.12,2.37-7.11,2.37-8.3S607.93,248,607.93,248s1.78-26.68,3.56-35.57c1.09-5.47,1.07-17.66.88-26.24-.19-8.94,10.63-13.78,17-7.5C629.52,178.85,629.7,179,629.87,179.22Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M631.65,178l3,3.53s3.49,20.19-4.22,45.68l-2.37,23.12s-4.15,19.57-4.74,24.31-.6,5.34-1.78,7.12a2.28,2.28,0,0,0,0,3L618,297.8s-5.33-6.53-16,1.77c0,0-3-4.74-.59-8.89s2.37-5.93,1.78-7.11,2.37-7.12,2.37-8.3,4.15-28.46,4.15-28.46,1.78-26.68,3.56-35.58.59-35.57.59-35.57S623.35,169.14,631.65,178Z" transform="translate(-46 -84.41)" fill="#908820"/><circle cx="545.92" cy="61.61" r="23.72" fill="#a0616a"/><path d="M604.29,110.41a16.6,16.6,0,0,1-31.09.44c9.26-3.73,18.72-.41,18.72-.41C596.65,108.78,600.75,108.93,604.29,110.41Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M591.92,111.63s-45.65-16-34.38,61.66c0,0,10.08,6.52,17.78,2.37,0,0-5.33-53.95,15.42-51.58s17.19,50.4,17.19,50.4,11.86,3.55,15.42-3S622.16,101,591.92,111.63Z" transform="translate(-46 -84.41)" fill="#3f3d56"/><path d="M590.74,120.52c-14.24-1.62-16.2,23.27-16,39.05.17-15.69,2.81-37,16-35.49,14.81,1.69,17.24,26.66,17.41,40.8C608.32,151.9,606.9,122.37,590.74,120.52Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M557.54,169.73a159.38,159.38,0,0,1-1.77-20.53,152.12,152.12,0,0,0,1.77,24.09s10.08,6.52,17.78,2.37c0,0-.12-1.28-.26-3.43C567.39,176.1,557.54,169.73,557.54,169.73Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M623.35,168c-3.32,6.08-13.83,3.41-15.25,3-.07,2.2-.17,3.52-.17,3.52s11.86,3.55,15.42-3c.73-1.34,1.11-5.13,1-10.2C624.25,164.57,623.9,167,623.35,168Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M872.73,757.37c4.74-8.78-.64-19.54-6.82-27.37s-13.73-16-13.57-26c.23-14.35,15.45-22.81,27.62-30.42a134.77,134.77,0,0,0,24.79-19.93c3-3.05,5.87-6.31,7.62-10.21,2.52-5.6,2.45-12,2.29-18.12q-.79-30.71-3-61.34" transform="translate(-46 -84.41)" fill="none" stroke="#3f3d56" stroke-miterlimit="10" stroke-width="4"/><path d="M934.74,562.9a22.31,22.31,0,0,0-11.16-18.32l-5,9.9.15-12a22.37,22.37,0,1,0,16,20.41Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M894.86,714.29a22.36,22.36,0,1,1,1.08-18l-14,11.37,15.37-3.57A22.25,22.25,0,0,1,894.86,714.29Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M883.81,670.86A22.37,22.37,0,0,1,876.72,627l-.12,9.22,5.06-10h.06a22.37,22.37,0,1,1,2.09,44.69Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M938.11,634.73A22.37,22.37,0,1,1,948,592.87l-4,10.83,8.13-7.8a22.37,22.37,0,0,1-14.07,38.83Z" transform="translate(-46 -84.41)" fill="#908820"/><path d="M931.74,568.86c-5.17.57-10.19,2.18-15.37,2.5s-10.87-.92-14.14-5c-1.77-2.17-2.65-4.91-4.14-7.28a16,16,0,0,0-5.63-5.3,22.37,22.37,0,1,0,41.81,14.84C933.42,568.71,932.58,568.77,931.74,568.86Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M938.11,634.73a22.39,22.39,0,0,1-21.28-31.91,16.37,16.37,0,0,1,4.5,4.51c1.59,2.4,2.56,5.18,4.43,7.39,3.48,4.09,9.44,5.43,14.84,5.19s10.6-1.78,16-2.28c.75-.06,1.5-.11,2.26-.13A22.37,22.37,0,0,1,938.11,634.73Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M883.81,670.86a22.37,22.37,0,0,1-21.46-31.49,17.83,17.83,0,0,1,4.73,4.55c1.74,2.45,2.82,5.27,4.86,7.54,3.77,4.2,10.11,5.68,15.83,5.54,5.54-.12,10.87-1.48,16.38-1.9A22.36,22.36,0,0,1,883.81,670.86Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M894.86,714.29A22.37,22.37,0,0,1,854.08,696a22.27,22.27,0,0,1,4.91,4.38c2.13,2.59,3.53,5.53,6,7.94,4.57,4.49,11.95,6.34,18.52,6.51A97.1,97.1,0,0,0,894.86,714.29Z" transform="translate(-46 -84.41)" opacity="0.1"/><path d="M839,747.26s17.65-.54,23-4.33,27.16-8.31,28.48-2.24,26.53,30.22,6.6,30.38-46.31-3.1-51.62-6.34S839,747.26,839,747.26Z" transform="translate(-46 -84.41)" fill="#656380"/><path d="M897.4,769c-19.93.16-46.31-3.11-51.62-6.34-4-2.47-5.65-11.3-6.19-15.38l-.59,0s1.12,14.24,6.43,17.47,31.69,6.5,51.62,6.34c5.75,0,7.74-2.09,7.63-5.12C903.88,767.78,901.69,768.92,897.4,769Z" transform="translate(-46 -84.41)" opacity="0.2"/><path d="M369,127.27l-18.25,11.57,11.08-20.15a18,18,0,0,0-11-3.94h-.3a21.58,21.58,0,0,1-3.78-.28l-6.19,3.92,2.65-4.82a22.06,22.06,0,0,1-10.82-8.21l-11.07,7,7-12.72c-6.48-7.77-15.2-12.54-24.81-12.54-11.51,0-21.76,6.85-28.38,17.52a21.24,21.24,0,0,1-18.76,10.12l-.62,0c-12.71,0-23,14.41-23,32.19s10.3,32.18,23,32.18a17.2,17.2,0,0,0,8-2c8.29-4.31,19.17-4.39,27.85-.54a29,29,0,0,0,23.68.05c8.62-3.77,19.37-3.69,27.61.54a17.23,17.23,0,0,0,7.93,2c12.71,0,23-14.41,23-32.18A40.86,40.86,0,0,0,369,127.27Z" transform="translate(-46 -84.41)" fill="#f2f2f2"/><path d="M334.59,174.36a34,34,0,0,0-19.72,2.14,29,29,0,0,1-23.67-.05c-8.69-3.85-19.57-3.77-27.86.54a17.2,17.2,0,0,1-8,2c-11.29,0-20.68-11.38-22.64-26.38a21.58,21.58,0,0,0,5.64-6.08C245,135.87,255.21,129,266.72,129s21.64,6.77,28.26,17.32a21.79,21.79,0,0,0,18.76,10.31H314C323,156.64,330.8,163.85,334.59,174.36Z" transform="translate(-46 -84.41)" opacity="0.03"/><path d="M846.07,150.83,837,156.6l5.53-10a9,9,0,0,0-5.51-2h-.14a10.79,10.79,0,0,1-1.89-.14l-3.09,2L833.2,144a11,11,0,0,1-5.4-4.09l-5.52,3.5,3.49-6.34a16.17,16.17,0,0,0-12.37-6.26c-5.75,0-10.86,3.42-14.16,8.74a10.57,10.57,0,0,1-9.36,5h-.31c-6.34,0-11.48,7.19-11.48,16s5.14,16.05,11.48,16.05a8.62,8.62,0,0,0,4-1,16.56,16.56,0,0,1,13.89-.27,14.52,14.52,0,0,0,11.81,0,16.58,16.58,0,0,1,13.77.27,8.61,8.61,0,0,0,4,1c6.34,0,11.48-7.18,11.48-16.05A20.28,20.28,0,0,0,846.07,150.83Z" transform="translate(-46 -84.41)" fill="#f2f2f2"/><path d="M828.92,174.32a17,17,0,0,0-9.83,1.07,14.52,14.52,0,0,1-11.81,0,16.58,16.58,0,0,0-13.9.27,8.55,8.55,0,0,1-4,1c-5.64,0-10.32-5.67-11.3-13.15a10.93,10.93,0,0,0,2.82-3c3.3-5.32,8.42-8.73,14.16-8.73s10.79,3.37,14.09,8.63a10.85,10.85,0,0,0,9.36,5.14h.15C823.15,165.48,827,169.08,828.92,174.32Z" transform="translate(-46 -84.41)" opacity="0.03"/><path d="M250.34,300.27,265,309.55l-8.88-16.15a14.48,14.48,0,0,1,8.85-3.15h.23a17.08,17.08,0,0,0,3-.22l4.95,3.14L271,289.31a17.78,17.78,0,0,0,8.67-6.58l8.87,5.62L283,278.16c5.19-6.22,12.18-10,19.87-10,9.23,0,17.44,5.48,22.75,14a17,17,0,0,0,15,8.11h.5c10.18,0,18.43,11.54,18.43,25.78s-8.25,25.79-18.43,25.79a13.86,13.86,0,0,1-6.43-1.61,26.59,26.59,0,0,0-22.31-.43,23.28,23.28,0,0,1-19,0,26.64,26.64,0,0,0-22.12.43,13.9,13.9,0,0,1-6.35,1.57c-10.19,0-18.44-11.54-18.44-25.79A32.78,32.78,0,0,1,250.34,300.27Z" transform="translate(-46 -84.41)" fill="#f2f2f2"/><path d="M277.89,338a27.36,27.36,0,0,1,15.8,1.72,23.28,23.28,0,0,0,19,0,26.61,26.61,0,0,1,22.32.43,13.82,13.82,0,0,0,6.42,1.61c9.05,0,16.57-9.11,18.14-21.13a17.44,17.44,0,0,1-4.52-4.88c-5.31-8.54-13.52-14-22.75-14s-17.33,5.42-22.64,13.88a17.44,17.44,0,0,1-15,8.25h-.23C287.17,323.81,280.93,329.59,277.89,338Z" transform="translate(-46 -84.41)" opacity="0.03"/></svg>
<svg id="5315dcf4-9ab7-45f5-a3d8-2e76525705db" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="813.15" height="752" viewBox="0 0 813.15 752"><title>tweetstorm</title><polygon points="559.73 403.7 347.86 352.67 347.86 752 559.73 696.54 559.73 403.7" fill="#e0e0e0"/><polygon points="559.73 403.7 347.86 352.67 347.86 752 559.73 696.54 559.73 403.7" opacity="0.1"/><polygon points="135.99 403.7 347.86 352.67 347.86 752 135.99 697.65 135.99 403.7" fill="#e0e0e0"/><polygon points="135.99 401.93 347.86 350.91 347.86 752 135.99 697.65 135.99 401.93" opacity="0.17"/><polygon points="346.09 354.44 457.68 261.71 669.54 316.07 557.96 405.46 346.09 354.44" fill="#e0e0e0"/><polygon points="29.05 318.94 137.76 405.46 349.62 354.44 242.03 263.48 29.05 318.94" fill="#e0e0e0"/><polygon points="27.29 317.18 138.64 405.46 350.51 354.44 240.26 261.71 27.29 317.18" opacity="0.05"/><polygon points="135.99 403.7 135.99 697.65 347.86 752 350.72 460.27 135.99 403.7" fill="#e0e0e0"/><polygon points="559.73 696.54 347.86 752 347.86 460.27 559.73 403.7 559.73 696.54" fill="#e0e0e0"/><polygon points="559.73 696.54 347.86 752 347.86 460.27 559.73 403.7 559.73 696.54" opacity="0.07"/><polygon points="475.42 598.92 475.42 695.43 559.73 672.13 559.73 575.63 475.42 598.92" opacity="0.07"/><polygon points="133.22 556.22 251.91 586.17 347.31 508.52 347.31 460.82 135.44 407.58 133.22 556.22" opacity="0.1"/><polygon points="561.39 556.22 442.7 586.17 347.31 508.52 347.31 460.82 559.17 407.58 561.39 556.22" opacity="0.1"/><path d="M452.66,473.1l8.21-1.64s-6.9,7.72-8.21,8.21h0a33.05,33.05,0,0,0,0,4.93c-15.77,67.69-73.93,37.79-73.93,37.79,21.19.49,23-8.21,23-8.21-12.32-1.81-14.79-9.86-14.79-9.86a5.08,5.08,0,0,0,6.57-1.64c-13.31-3.45-11.5-16.43-11.5-16.43,2.3,2,5.91,2.79,8.05,3.12-1.48-1.81-14-16.76-6.41-24.48,0,0,9.2,16.43,33.35,17.91l1.64-.33a12.89,12.89,0,0,1-.33-3.61,17.25,17.25,0,0,1,17.25-17.25c11.5,0,14,6.57,14,6.57l8.21-4.93C457.59,463.24,456.77,471.46,452.66,473.1Z" transform="translate(-193.43 -74)" fill="#00aced"/><polygon points="135.99 403.7 27.29 493.55 239.15 546.79 347.86 460.27 135.99 403.7" fill="#e0e0e0"/><path d="M618.25,470.91l4.06-5.65s.18,8.6-.34,9.65h0a27.47,27.47,0,0,0,2.79,3c28.77,50.08-23.53,64.86-23.53,64.86,13.16-11.71,9.33-18,9.33-18C602,530.62,596,527.12,596,527.12a4.22,4.22,0,0,0,3.06-4.72c-10,5.44-16.3-3.47-16.3-3.47,2.52-.1,5.18-1.65,6.66-2.66-1.92-.26-18-2.27-17.77-11.25,0,0,14.9,4.77,30.42-8l.81-1.13a10.71,10.71,0,0,1-2.25-2,14.33,14.33,0,0,1,.71-20.26c7-6.52,12.21-3.92,12.21-3.92l2.2-7.65C615.66,462.13,619.81,467.58,618.25,470.91Z" transform="translate(-193.43 -74)" fill="#908820"/><polygon points="559.73 403.7 669.54 493.55 458.78 546.79 347.86 460.27 559.73 403.7" fill="#e0e0e0"/><polygon points="559.73 403.7 669.54 493.55 458.78 546.79 347.86 460.27 559.73 403.7" opacity="0.07"/><path d="M610.52,196.35l7.51-3.61s-4.74,9.15-5.89,9.95h0a32.88,32.88,0,0,0,1.22,4.75c1.53,69.12-61.9,54.68-61.9,54.68,20.54-4.76,20.13-13.6,20.13-13.6-12.32,1.3-16.68-5.84-16.68-5.84a5,5,0,0,0,5.93-3.21c-13.68,0-15.14-13-15.14-13,2.7,1.33,6.39,1.23,8.53,1-1.87-1.38-17.6-12.7-12.22-22,0,0,12.93,13.56,36.56,9l1.5-.72a12.82,12.82,0,0,1-1.21-3.4,17.16,17.16,0,0,1,12.36-20.89c11.08-2.84,15.08,2.88,15.08,2.88l6.7-6.78C612.83,185.63,614.07,193.75,610.52,196.35Z" transform="translate(-193.43 -74)" fill="#00aced"/><path d="M267,249l8.17-1.63s-6.86,7.68-8.17,8.17h0a32.88,32.88,0,0,0,0,4.9c-15.69,67.33-73.54,37.59-73.54,37.59,21.08.49,22.88-8.17,22.88-8.17-12.26-1.8-14.71-9.81-14.71-9.81a5,5,0,0,0,6.54-1.63C194.9,275,196.7,262.12,196.7,262.12c2.29,2,5.88,2.78,8,3.11-1.47-1.8-13.89-16.67-6.37-24.35,0,0,9.15,16.34,33.18,17.81l1.63-.33a12.82,12.82,0,0,1-.33-3.6A17.16,17.16,0,0,1,250,237.61c11.44,0,13.89,6.54,13.89,6.54l8.17-4.9C271.87,239.24,271.06,247.41,267,249Z" transform="translate(-193.43 -74)" fill="#00aced"/><path d="M993.24,103.71l13.33-2.67s-11.2,12.53-13.33,13.33h0a53.64,53.64,0,0,0,0,8c-25.6,109.85-120,61.32-120,61.32,34.39.8,37.33-13.33,37.33-13.33-20-2.93-24-16-24-16a8.24,8.24,0,0,0,10.66-2.67c-21.6-5.6-18.66-26.66-18.66-26.66,3.73,3.2,9.6,4.53,13.06,5.07-2.4-2.93-22.66-27.2-10.4-39.73,0,0,14.93,26.66,54.12,29.06l2.67-.53a20.92,20.92,0,0,1-.53-5.87,28,28,0,0,1,28-28c18.66,0,22.66,10.66,22.66,10.66l13.33-8C1001.24,87.71,999.91,101,993.24,103.71Z" transform="translate(-193.43 -74)" fill="#00aced"/><path d="M775.38,81.73l5.52-1.1s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43A11.59,11.59,0,0,1,763.9,74c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C778.69,75.1,778.14,80.63,775.38,81.73Z" transform="translate(-193.43 -74)" fill="#00aced"/><path d="M524.71,302.58l5.52-1.1s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43,11.59,11.59,0,0,1,11.59-11.59c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C528,296,527.47,301.48,524.71,302.58Z" transform="translate(-193.43 -74)" fill="#535461" opacity="0.3"/><path d="M740,199.89l5.52-1.1S740.93,204,740,204.3h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43,11.59,11.59,0,0,1,11.59-11.59c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C743.36,193.26,742.8,198.78,740,199.89Z" transform="translate(-193.43 -74)" fill="#00aced" opacity="0.3"/><path d="M635.8,384.76l11.48-2.3s-9.65,10.8-11.48,11.48h0a46.21,46.21,0,0,0,0,6.89c-22.05,94.63-103.36,52.83-103.36,52.83,29.63.69,32.16-11.48,32.16-11.48-17.23-2.53-20.67-13.78-20.67-13.78a7.1,7.1,0,0,0,9.19-2.3c-18.6-4.82-16.08-23-16.08-23,3.22,2.76,8.27,3.9,11.25,4.36-2.07-2.53-19.52-23.43-9-34.22,0,0,12.86,23,46.63,25l2.3-.46a18,18,0,0,1-.46-5.05,24.11,24.11,0,0,1,24.12-24.12c16.08,0,19.52,9.19,19.52,9.19L642.92,371C642.69,371,641.54,382.47,635.8,384.76Z" transform="translate(-193.43 -74)" fill="#908820"/><path d="M554.52,100.5,560,99.4s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43A11.59,11.59,0,0,1,543,92.77c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C557.84,93.88,557.29,99.4,554.52,100.5Z" transform="translate(-193.43 -74)" fill="#908820"/><path d="M385.57,223.08l5.52-1.1s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43,11.59,11.59,0,0,1,11.59-11.59c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C388.88,216.45,388.33,222,385.57,223.08Z" transform="translate(-193.43 -74)" fill="#908820"/><path d="M339.19,143.57l5.52-1.1s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43,11.59,11.59,0,0,1,11.59-11.59c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C342.5,136.94,342,142.47,339.19,143.57Z" transform="translate(-193.43 -74)" fill="#908820" opacity="0.3"/><path d="M533.54,480.37l5.52-1.1s-4.64,5.19-5.52,5.52h0a22.21,22.21,0,0,0,0,3.31c-10.6,45.5-49.69,25.4-49.69,25.4,14.25.33,15.46-5.52,15.46-5.52-8.28-1.21-9.94-6.63-9.94-6.63a3.41,3.41,0,0,0,4.42-1.1c-8.94-2.32-7.73-11-7.73-11a11.48,11.48,0,0,0,5.41,2.1c-1-1.21-9.39-11.26-4.31-16.45,0,0,6.18,11,22.42,12l1.1-.22a8.66,8.66,0,0,1-.22-2.43,11.59,11.59,0,0,1,11.59-11.59c7.73,0,9.39,4.42,9.39,4.42l5.52-3.31C536.86,473.75,536.3,479.27,533.54,480.37Z" transform="translate(-193.43 -74)" fill="#535461"/><path d="M855.77,240.7,867,238.45s-9.46,10.59-11.26,11.26h0a45.32,45.32,0,0,0,0,6.76c-21.63,92.81-101.37,51.81-101.37,51.81C783.46,309,785.94,297,785.94,297c-16.9-2.48-20.27-13.52-20.27-13.52a7,7,0,0,0,9-2.25c-18.25-4.73-15.77-22.53-15.77-22.53,3.15,2.7,8.11,3.83,11,4.28-2-2.48-19.15-23-8.79-33.57,0,0,12.62,22.53,45.73,24.55l2.25-.45a17.67,17.67,0,0,1-.45-5,23.65,23.65,0,0,1,23.65-23.65c15.77,0,19.15,9,19.15,9l11.26-6.76C862.53,227.18,861.4,238.45,855.77,240.7Z" transform="translate(-193.43 -74)" fill="#535461"/><path d="M408.54,331.25,419.8,329s-9.46,10.59-11.26,11.26h0a45.32,45.32,0,0,0,0,6.76c-21.63,92.81-101.37,51.81-101.37,51.81,29.06.68,31.54-11.26,31.54-11.26-16.9-2.48-20.27-13.52-20.27-13.52a7,7,0,0,0,9-2.25c-18.25-4.73-15.77-22.53-15.77-22.53,3.15,2.7,8.11,3.83,11,4.28-2-2.48-19.15-23-8.79-33.57,0,0,12.62,22.53,45.73,24.55l2.25-.45a17.67,17.67,0,0,1-.45-5,23.65,23.65,0,0,1,23.65-23.65c15.77,0,19.15,9,19.15,9l11.26-6.76C415.3,317.73,414.17,329,408.54,331.25Z" transform="translate(-193.43 -74)" fill="#535461"/></svg>
<svg id="b847c0d8-de26-4ae3-b269-ef35a8ef771f" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1130" height="831.56" viewBox="0 0 1130 831.56"><defs><linearGradient id="af868219-7e5b-405b-a411-f7ffae4abd96" x1="321.68" y1="857.11" x2="321.68" y2="447.21" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient></defs><title>youtube tutorial</title><path d="M691.54,158.91C619.1,156.4,550.1,133,484.22,107.42S353.07,53.49,282.37,39.93C236.89,31.21,184.88,30,148.24,54.36c-35.27,23.46-46.66,64-52.78,101.55-4.61,28.28-7.32,58,5.3,84.51,8.77,18.37,24.34,33.82,35.11,51.42,37.45,61.26,11,136.79-29.62,196.59-19,28.06-41.13,54.82-55.82,84.69s-21.49,64.08-8.64,94.54c12.75,30.2,43.11,52.88,76,68.83,66.81,32.4,145.54,41.68,222.31,46.93C510.05,795,680.9,790,851.29,785c63.06-1.87,126.39-3.76,188.43-13.51,34.44-5.41,70-14,95-34.75,31.75-26.34,39.62-70.94,18.34-104-35.68-55.39-134.35-69.15-159.29-128.6-13.73-32.72.37-69.17,20.32-99.52,42.8-65.11,114.54-122.22,118.32-196.64,2.6-51.11-31.89-102.3-85.21-126.48-55.9-25.35-133.4-22.16-174.64,19.8C830.12,144.51,755.5,161.13,691.54,158.91Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><polygon points="664.18 755.89 469.18 753.39 469.75 748.39 479.18 665.89 650.43 665.89 663.03 748.39 663.99 754.64 664.18 755.89" fill="#d0d2d5"/><polygon points="663.99 754.64 566.68 754.64 469.18 753.39 469.75 748.39 663.03 748.39 663.99 754.64" opacity="0.1"/><rect x="436.68" y="749.64" width="258.75" height="6.25" fill="#d0d2d5"/><path d="M977.3,204.59a16.27,16.27,0,0,0-16.19-16.35H238.5a16.27,16.27,0,0,0-16.2,16.35v438h755Z" transform="translate(-35 -34.22)" fill="#444053"/><path d="M222.3,638.24v51.3a16.2,16.2,0,0,0,16.2,16.2H961.11a16.2,16.2,0,0,0,16.19-16.2v-51.3Z" transform="translate(-35 -34.22)" fill="#d0d2d5"/><rect x="218.55" y="181.52" width="696.25" height="393.75" fill="#908820" opacity="0.1"/><path d="M603.55,686.27a16.89,16.89,0,0,0,13.28-6.44v0a17.21,17.21,0,0,0,1.31-1.93l-9.26-1.52,10,.08a16.85,16.85,0,0,0,.32-13.38l-13.43,7,12.39-9.1a16.87,16.87,0,1,0-27.87,18.88v0A16.87,16.87,0,0,0,603.55,686.27Z" transform="translate(-35 -34.22)" fill="#908820"/><polygon points="585.43 671.52 663.22 749.64 651.29 671.52 585.43 671.52" opacity="0.1"/><rect x="239" y="252.56" width="457" height="274" rx="2.6" fill="#fff"/><rect x="219" y="181.56" width="696" height="23" fill="#fff"/><rect x="222" y="185.56" width="20" height="14" rx="0.78" fill="#908820"/><rect x="460" y="185.56" width="210" height="14" rx="0.58" fill="#908820" opacity="0.3"/><path d="M698.77,228.22h-.51l-.19-.16a4.25,4.25,0,0,0,1-2.72,4.14,4.14,0,1,0-4.13,4.17,4.27,4.27,0,0,0,2.72-1l.19.16v.52l3.2,3.2,1-1Zm-3.84,0a2.89,2.89,0,1,1,2.88-2.88A2.86,2.86,0,0,1,694.93,228.22Z" transform="translate(-35 -34.22)" fill="#444053"/><circle cx="905" cy="192.56" r="7" fill="#908820"/><rect x="729" y="229.56" width="65" height="44" rx="2.43" fill="#fff"/><rect x="811" y="229.56" width="84" height="7" rx="0.61" fill="#fff"/><rect x="811" y="244.56" width="84" height="7" rx="0.61" fill="#fff"/><rect x="811" y="259.56" width="84" height="7" rx="0.61" fill="#fff"/><rect x="729" y="298.56" width="65" height="44" rx="2.43" fill="#fff"/><rect x="811" y="298.56" width="84" height="7" rx="0.61" fill="#fff"/><rect x="811" y="313.56" width="84" height="7" rx="0.61" fill="#fff"/><rect x="811" y="328.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="729" y="367.56" width="65" height="44" rx="2.43" fill="#fff"/><rect x="811" y="367.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="382.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="397.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="729" y="436.56" width="65" height="44" rx="2.43" fill="#fff"/><rect x="811" y="436.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="451.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="466.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="729" y="505.56" width="65" height="44" rx="2.43" fill="#fff"/><rect x="811" y="505.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="520.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="811" y="535.56" width="84" height="7" rx="0.66" fill="#fff"/><rect x="304.92" y="325.17" width="102.83" height="5.33" fill="#908820" opacity="0.1"/><rect x="314.06" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="328.53" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="343" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="357.47" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="371.94" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="386.42" y="296.99" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="314.06" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="328.53" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="343" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="357.47" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="371.94" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="386.42" y="302.32" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="314.06" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="328.53" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="343" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="357.47" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="371.94" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="386.42" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="530.37" y="338.88" width="102.83" height="5.33" fill="#908820" opacity="0.1"/><rect x="539.51" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="553.98" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="568.45" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="582.93" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="597.4" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="611.87" y="310.7" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="539.51" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="553.98" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="568.45" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="582.93" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="597.4" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="611.87" y="316.03" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="539.51" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="553.98" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="568.45" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="582.93" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="597.4" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="611.87" y="329.74" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="530.37" y="377.72" width="102.83" height="5.33" fill="#908820" opacity="0.1"/><rect x="539.51" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="553.98" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="568.45" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="582.93" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="597.4" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="611.87" y="349.54" width="9.9" height="28.18" fill="#908820" opacity="0.1"/><rect x="539.51" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="553.98" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="568.45" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="582.93" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="597.4" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="611.87" y="354.87" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="539.51" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="553.98" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="568.45" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="582.93" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="597.4" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><rect x="611.87" y="368.58" width="9.9" height="3.05" fill="#908820" opacity="0.1"/><path d="M403,419.66a28.45,28.45,0,0,1-2.1-3.09l14.81-2.43-16,.12a27.06,27.06,0,0,1-.52-21.38L420.64,404l-19.81-14.56a27,27,0,1,1,44.57,30.2,26.24,26.24,0,0,1,3.08,4.92l-19.22,10,20.49-6.87A27,27,0,0,1,445.4,453,27,27,0,1,1,403,453a27,27,0,0,1,0-33.37Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M451.18,436.34A26.89,26.89,0,0,1,445.4,453,27,27,0,1,1,403,453C399.37,448.43,451.18,433.31,451.18,436.34Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M428.52,503.88a2.53,2.53,0,0,1,.13.22h-9.16c.05-.07.08-.15.13-.22a8.87,8.87,0,0,1-1.18-4.51,9.26,9.26,0,0,1,.55-3.18v0c.51.08,1.14.17,1.87.25a32.1,32.1,0,0,0,6.39,0q.92-.09,1.89-.24a.09.09,0,0,0,0,.05,9,9,0,0,1,.54,3.13A8.87,8.87,0,0,1,428.52,503.88Z" transform="translate(-35 -34.22)" fill="#3f3d56" opacity="0.2"/><path d="M395.6,504.1h-51c-.11-.29-.21-.6-.3-.9a26.85,26.85,0,0,1,.59-17.64,27,27,0,0,1-9.85-31.89,25.83,25.83,0,0,1-2.91-2.33l13.42-6.69-15.26,4.84a27,27,0,0,1-6.8-20.27l23.81,4.3-23.22-8.07a27,27,0,1,1,51.49,15.7A27.35,27.35,0,0,1,380,445l-15.42,15.21,17.55-12.62a27,27,0,0,1,3.32,25.5A27,27,0,0,1,395.6,504.1Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M395.6,504.1h-51c-.11-.29-.21-.6-.3-.9a26.85,26.85,0,0,1,.59-17.64c-4.8-3.32,40.23-33.07,41.12-30.17a26.88,26.88,0,0,1-.59,17.65A27,27,0,0,1,395.6,504.1Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M626.35,467.56a26.9,26.9,0,0,1-13.51,11.36,27.4,27.4,0,0,1,.11,5.8l-21.61-1.3L612.45,488a27,27,0,0,1-3.22,8.16,26.57,26.57,0,0,1-7.2,7.9H572.61v.77h-30c.06-.26.12-.51.19-.77a27,27,0,0,1,16.45-18.31,27,27,0,0,1,17.13-28.64,27.36,27.36,0,0,1-.22-3.73l13.95,5.51-13.8-8.11A27,27,0,0,1,586.9,432.2l12.71,20.58-9.53-22.66a27,27,0,0,1,36.27,37.44Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M609.23,496.2a26.57,26.57,0,0,1-7.2,7.9H572.61v.77h-30c.06-.26.12-.51.19-.77a27,27,0,0,1,16.45-18.31c-.25-2,5.56-1.7,13.31-.4C587.75,487.94,610.26,494.48,609.23,496.2Z" transform="translate(-35 -34.22)" fill="#908820" opacity="0.1"/><path d="M481.71,424.19s3.7,20-4.56,26.53,7,45.67,32.18,41.1,35.44-37.4,35.44-37.4-25.22-5.22-15.87-31.75Z" transform="translate(-35 -34.22)" fill="#ee8e9e"/><path d="M481.71,424.19s3.7,20-4.56,26.53,7,45.67,32.18,41.1,35.44-37.4,35.44-37.4-25.22-5.22-15.87-31.75Z" transform="translate(-35 -34.22)" opacity="0.1"/><circle cx="469" cy="362.9" r="41.75" fill="#ee8e9e"/><path d="M572.61,476.49v28.38H434.75V477.78a17.48,17.48,0,0,1,10.67-16.1l3.95-1.68c10-4.26,22.15-9.55,26.12-11.75a6.53,6.53,0,0,0,1.22-.79c2.17-2.17,1.74.65,1.74.65s-.06.54-.1,1.47c-.24,6.39.68,31.5,30.11,29.41,28.71-2,30-21.42,29.71-27.13a15.45,15.45,0,0,0-.13-1.58,37.09,37.09,0,0,1,5.81,1.9c6,2.22,14.86,5.78,17.23,7.46.74.52,1.64,1.06,2.62,1.61a17.23,17.23,0,0,1,3.1,2.21A17.51,17.51,0,0,1,572.61,476.49Z" transform="translate(-35 -34.22)" fill="#908820"/><path d="M543.85,452.18c-2.43,11.26-10.06,32.35-37.56,32.9-29.3.58-31-28.56-30.81-36.83a5.91,5.91,0,0,0,1.23-.79c2.18-2.17,1.74.65,1.74.65s-3.69,33.27,30,30.88S538,450.29,538,450.29C538,450.13,540.51,451,543.85,452.18Z" transform="translate(-35 -34.22)" opacity="0.1"/><path d="M454.58,399.21a7.15,7.15,0,0,1,7.53,2.75c1.52,2.13,1.75,4.7,1.94,7.16,1.81.38,2.83-1.67,3-3.22.67-6.31.72-12.74,2.84-18.84s6.75-12,13.86-14.33a16.08,16.08,0,0,1,7.57-.72c1.51.25,2.93.78,4.43,1,5.2.86,10.85-1.6,15.72.11,3.06,1.08,5,3.57,6.43,6.06a13.08,13.08,0,0,0,2.5,3.63c2.07,1.86,5.31,2.34,8.28,2.92s6.21,1.59,7.48,3.88c1.14,2.07.3,4.46-.08,6.7a16.42,16.42,0,0,0,1.69,10.37c.55,1,1.67,2.21,3,1.88a2.63,2.63,0,0,0,1.42-1.15c3.41-4.78,3.32-11,7.6-15.31,1.43-1.44,3.28-2.56,4.67-4a9.08,9.08,0,0,0,2.42-8.24c-.77-3.3-3.41-6-5.54-8.9a35,35,0,0,1-5.93-12.31,2.23,2.23,0,0,1,0-1.61,4.29,4.29,0,0,1,1.52-1.24,5.74,5.74,0,0,0,1.94-7.33C547.66,346,545,344.12,542,343a44.8,44.8,0,0,0-9.37-2c-2.1.3-4.56-.89-5.21-2.57-.29-.75-.29-1.56-.58-2.31a5.3,5.3,0,0,0-4-2.8,11.7,11.7,0,0,0-5.35.38c-2,.56-4.27,1.4-6,.42-.91-.51-1.41-1.44-2.39-1.86-2-.84-4.06.86-5.49,2.26a13.36,13.36,0,0,1-4.95,3.33,7.37,7.37,0,0,1-6.14-.41c-1.37-.87-2.06-2.27-3.15-3.38a11.86,11.86,0,0,0-7.45-3,26.23,26.23,0,0,0-8.41.92,50.78,50.78,0,0,0-14.77,6.2,15.87,15.87,0,0,0-5.67,5.27,12,12,0,0,0-1.27,5.43c-.22,5.91,1.74,12.13,6.85,16.27-3.12,1.3-4.17,4.49-4.9,7.32-1.17,4.52-2.34,9.11-1.9,13.71.22,2.38.87,4.76.52,7.13a15.36,15.36,0,0,0-.46,3.41A3.27,3.27,0,0,0,454.58,399.21Z" transform="translate(-35 -34.22)" fill="#454b69"/><g opacity="0.1"><path d="M556.23,378a9.76,9.76,0,0,1-1.8,2.66c-1.39,1.46-3.24,2.59-4.67,4-4.28,4.28-4.19,10.54-7.6,15.32a2.57,2.57,0,0,1-1.42,1.14c-1.3.34-2.42-.84-3-1.88a17.2,17.2,0,0,1-1.49-4c-.07.35-.14.7-.2,1a16.42,16.42,0,0,0,1.69,10.37c.55,1,1.67,2.21,3,1.88a2.63,2.63,0,0,0,1.42-1.15c3.41-4.78,3.32-11,7.6-15.31,1.43-1.44,3.28-2.56,4.67-4a9.08,9.08,0,0,0,2.42-8.24A10.74,10.74,0,0,0,556.23,378Z" transform="translate(-35 -34.22)"/><path d="M546.91,348.4a4.19,4.19,0,0,0-1.52,1.24,2.21,2.21,0,0,0,0,1.6,33.82,33.82,0,0,0,1.51,4.56h0a5.74,5.74,0,0,0,1.94-7.33,7,7,0,0,0-.7-1.17A5.76,5.76,0,0,1,546.91,348.4Z" transform="translate(-35 -34.22)"/><path d="M458.71,357.67a18.54,18.54,0,0,1-6.55-11.59,15.62,15.62,0,0,0-.3,2.72,20.82,20.82,0,0,0,3.29,12.37A7,7,0,0,1,458.71,357.67Z" transform="translate(-35 -34.22)"/><path d="M536.08,388.94c.38-2.25,1.22-4.64.08-6.7-1.27-2.3-4.51-3.3-7.48-3.88s-6.21-1.07-8.28-2.92a13.08,13.08,0,0,1-2.5-3.63c-1.41-2.49-3.37-5-6.43-6.06-4.87-1.71-10.52.74-15.72-.11-1.5-.25-2.92-.77-4.43-1a16,16,0,0,0-7.57.72c-7.11,2.34-11.74,8.24-13.86,14.33s-2.17,12.52-2.84,18.84c-.17,1.54-1.19,3.6-3,3.22-.19-2.47-.42-5-1.94-7.17a7.15,7.15,0,0,0-7.53-2.74,3.66,3.66,0,0,1-2.11-1.35,11.59,11.59,0,0,1,0,2.76,15.36,15.36,0,0,0-.46,3.41,3.27,3.27,0,0,0,2.61,2.57,7.15,7.15,0,0,1,7.53,2.75c1.52,2.13,1.75,4.7,1.94,7.16,1.81.38,2.83-1.67,3-3.22.67-6.31.72-12.74,2.84-18.84s6.75-12,13.86-14.33a16.08,16.08,0,0,1,7.57-.72c1.51.25,2.93.78,4.43,1,5.2.86,10.85-1.6,15.72.11,3.06,1.08,5,3.57,6.43,6.06a13.08,13.08,0,0,0,2.5,3.63c2.07,1.86,5.31,2.34,8.28,2.92s6,1.52,7.33,3.64C536,389.24,536.05,389.09,536.08,388.94Z" transform="translate(-35 -34.22)"/><path d="M452.43,385.83a19,19,0,0,0-.33-5.57,24.82,24.82,0,0,0-.19,5.84c0,.5.11,1,.18,1.49C452.2,387,452.35,386.42,452.43,385.83Z" transform="translate(-35 -34.22)"/></g><path d="M403.49,545.22V527.17a.51.51,0,0,1,.51-.51h2.05a.51.51,0,0,1,.51.51v7.51l8.32-7.71a1.36,1.36,0,0,1,2.23,1v16.35a1.36,1.36,0,0,1-2.23,1l-8.32-7.66v7.47a.51.51,0,0,1-.51.51H404A.51.51,0,0,1,403.49,545.22Z" transform="translate(-35 -34.22)" fill="#444053"/><path d="M604.52,527.17v18.05a.51.51,0,0,1-.52.51h-2a.51.51,0,0,1-.51-.51v-7.51l-8.32,7.7a1.37,1.37,0,0,1-2.24-1V528a1.37,1.37,0,0,1,2.24-1l8.32,7.66v-7.46a.51.51,0,0,1,.51-.51h2A.51.51,0,0,1,604.52,527.17Z" transform="translate(-35 -34.22)" fill="#444053"/><path d="M500.61,546h-4.07a2,2,0,0,1-2-2V529.06a2,2,0,0,1,2-2h4.07a2,2,0,0,1,2,2V544A2,2,0,0,1,500.61,546Zm12.9-2V529.06a2,2,0,0,0-2-2H507.4a2,2,0,0,0-2,2V544a2,2,0,0,0,2,2h4.07A2,2,0,0,0,513.51,544Z" transform="translate(-35 -34.22)" fill="#444053"/><rect x="349" y="468.62" width="240" height="2.67" fill="#444053" opacity="0.6"/><circle cx="423.11" cy="469.95" r="6.33" fill="#444053"/><rect x="349" y="468.62" width="74.11" height="2.67" fill="#444053"/><ellipse cx="957.08" cy="758.54" rx="59.47" ry="11.44" fill="#908820"/><ellipse cx="956.09" cy="753.68" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="742.6" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="731.53" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="720.45" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="709.37" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="698.29" rx="6.92" ry="9.06" fill="#3f3d56"/><ellipse cx="956.09" cy="687.21" rx="6.92" ry="9.06" fill="#3f3d56"/><path d="M1017,645.63a34.28,34.28,0,0,0,2.58-3.8l-18.2-3,19.68.15a33.27,33.27,0,0,0,.63-26.28L995.31,626.4l24.34-17.89a33.17,33.17,0,1,0-54.78,37.12,33.85,33.85,0,0,0-3.78,6l23.62,12.27-25.18-8.45a33.19,33.19,0,0,0,5.34,31.14,33.16,33.16,0,1,0,52.14,0,33.18,33.18,0,0,0,0-41Z" transform="translate(-35 -34.22)" fill="#908820"/><path d="M957.78,666.13a33,33,0,0,0,7.09,20.5,33.16,33.16,0,1,0,52.14,0C1021.45,681,957.78,662.41,957.78,666.13Z" transform="translate(-35 -34.22)" opacity="0.1"/><ellipse cx="290" cy="810.56" rx="99" ry="21" fill="#908820" opacity="0.1"/><path d="M358.15,842.34c-2-.73-4.14-.61-6.23-.84a19.28,19.28,0,0,1-11.46-5.56,21,21,0,0,1-4.58-7c.66.24,1.32.49,2,.76-2-4.67.1-9.91,1.94-14.63q4.68-12,8.85-24.13c7-20.49,13-41.8,12.87-63.6,0-7.88-1.45-15.33-1.73-23.2-.21-5.84.69-11.67.65-17.52,0-.82,0-1.63-.06-2.45a15.53,15.53,0,0,0,5.27-12.66,30.48,30.48,0,0,0-.53-3.68,59.68,59.68,0,0,0-1.4-6.26c-.66-2.17-1.56-4.25-2.33-6.37-1.49-4.11-2.5-8.39-3.77-12.57A4.33,4.33,0,0,1,359,643a2.93,2.93,0,0,1-.64-1.31,32.41,32.41,0,0,1-1.2-8.17l.85-.38a21.27,21.27,0,0,0-.41-8.09c.48-4.29,1.23-8.61,1.69-12.88.17-1.6.1-3.61-1.37-4-.3-2.29-.42-4.61-.54-6.92q-.63-12.09-1.25-24.17c-.42-8.09-.84-16.17-1-24.27-.11-5.14-.17-10.46-1.39-15.38a18.6,18.6,0,0,0-.71-7c-1.86-5.89-7.64-10.56-13.5-10.23a26.86,26.86,0,0,0-6.39-6.46c1.17-2.62,2.57-5.32,3.72-7.44C346.24,503.25,353.1,494,353.1,483a25.43,25.43,0,0,0-2.75-11.59,12.52,12.52,0,0,0,.44-1.35,11.09,11.09,0,0,0-.45-6.49,19,19,0,0,0-1-2.32c-.26-.5-.54-1-.83-1.48L348,459c-.31-.47-.63-.92-1-1.36a33.1,33.1,0,0,0-2.28-2.74,22.56,22.56,0,0,0-3.28-2.92,14.46,14.46,0,0,0-5.4-2.49,16.46,16.46,0,0,0-3.43-.31c-.69,0-1.39,0-2.08.06-1.4.08-2.79.18-4.18.17-.46,0-.93,0-1.39-.07a50.63,50.63,0,0,1-7.24-1.51l-1.2-.25c-.41-.08-.81-.15-1.22-.2a22.89,22.89,0,0,0-3.31-.2l-1.11,0a30.68,30.68,0,0,0-3.31.34l-1.1.17a31.6,31.6,0,0,0-8.05,2.39c-.33.15-.67.32-1,.5l-.2.11c-.27.14-.54.29-.8.45l-.15.09-.85.54-.07.05c-7.94,5.34-12.63,15.46-13.82,25.5-1.33,11.27,1,22.63,3.33,33.72l2.1,10c.09.45.19.89.28,1.34a22,22,0,0,0-6.59,9.14c-2,5-2.05,10.41-1.89,15.83,0,1.1.06,2.2.17,3.3-.25,2.08-.47,4.17-.61,6.27-.63,9.7.55,19.45.24,29.17-.24,7.49-1.37,15-.82,22.44.48,6.6,2.25,13,4,19.37l2.52,9.08a2.41,2.41,0,0,0,.63,1.25,1.9,1.9,0,0,0,1.41.29A85.12,85.12,0,0,0,282.2,650a35.5,35.5,0,0,0-1.48,7.38c-.33,4.58.73,9.13,1.79,13.58q2.4,10,4.78,20.08,1.68,7.05,3.35,14.09c2.2,9.26,4.41,18.58,4.82,28.11a61.05,61.05,0,0,1-1,14.29,124.52,124.52,0,0,0-1.24,42.83,210,210,0,0,0,8.09,35.41c.81-.43,1.63-.83,2.47-1.2l-.35.65q-1.73,3-3.2,6.15a35,35,0,0,0-1.71,4.21c-1.28,4-1.48,8.26-1.66,12.47a6.1,6.1,0,0,0,.21,2.41c.55,1.51,2.13,2.28,3.6,2.7,4.32,1.23,8.81.79,13.3.21a1.61,1.61,0,0,0,.09.29c.55,1.51,2.12,2.28,3.6,2.7,6.18,1.76,12.7.1,19.07-.5s12.63-.16,18.76-1.63a8.93,8.93,0,0,0,2.6-1,8.51,8.51,0,0,0,3.28-4.55,5.83,5.83,0,0,0,.38-1.49C362,845,360.13,843.07,358.15,842.34ZM337,749.73a25.2,25.2,0,0,1-2,6.78,129.4,129.4,0,0,0-10.26,32.17,149.15,149.15,0,0,0-2.27,22.25c-.1,5.5.11,11.26-2.18,16.2l.83-.13c-.26.48-.51,1-.75,1.38l-.12.22a21.68,21.68,0,0,1-2.73-7.66c0-.09-.45-.16-1.14-.2a206.93,206.93,0,0,1-2.92-47.89,22.17,22.17,0,0,1,.8-5.75c.51-1.61,1.37-3.08,2-4.64a34.09,34.09,0,0,0,2-8.14,115.64,115.64,0,0,0,1.26-21.52c-.24-6.79,1.33-13.54,2.67-20.3.64-3.21,1.23-6.43,1.55-9.65l.06-.65a96.87,96.87,0,0,1,12.28,31.88A45.6,45.6,0,0,1,337,749.73Zm-42.8-144.52.32.28a22,22,0,0,1,2.66,2.79,3.69,3.69,0,0,1,.84,1.54c.3,1.46-.91,2.7-1.9,3.77a17,17,0,0,0-1.21,1.46Q294.59,610.13,294.23,605.21Z" transform="translate(-35 -34.22)" fill="url(#af868219-7e5b-405b-a411-f7ffae4abd96)"/><path d="M280.36,542.53l-1.1,8.49c-.37,2.88-.74,5.75-.94,8.64-.67,9.62.57,19.28.25,28.92-.26,7.42-1.44,14.83-.87,22.24.51,6.54,2.37,12.9,4.23,19.2l2.64,9a2.38,2.38,0,0,0,.66,1.24,2.33,2.33,0,0,0,1.69.27l11.72-1.06c-.75-3.44-1.58-7-2-10.53-.32-2.76-.51-5.54-.71-8.32-.62-8.66-1.33-17.32-2-26l-1.25-15.2c-.53-6.6-1.08-13.23-2.74-19.64A39.09,39.09,0,0,0,280.36,542.53Z" transform="translate(-35 -34.22)" fill="#908820"/><path d="M280.36,542.53l-1.1,8.49c-.37,2.88-.74,5.75-.94,8.64-.67,9.62.57,19.28.25,28.92-.26,7.42-1.44,14.83-.87,22.24.51,6.54,2.37,12.9,4.23,19.2l2.64,9a2.38,2.38,0,0,0,.66,1.24,2.33,2.33,0,0,0,1.69.27l11.72-1.06c-.75-3.44-1.58-7-2-10.53-.32-2.76-.51-5.54-.71-8.32-.62-8.66-1.33-17.32-2-26l-1.25-15.2c-.53-6.6-1.08-13.23-2.74-19.64A39.09,39.09,0,0,0,280.36,542.53Z" transform="translate(-35 -34.22)" opacity="0.1"/><path d="M301.24,831.55a35.81,35.81,0,0,0-1.8,4.18c-1.34,3.95-1.54,8.19-1.74,12.36a6,6,0,0,0,.22,2.39c.58,1.5,2.24,2.26,3.79,2.67,6.49,1.75,13.33.11,20-.49s13.26-.16,19.7-1.61a10.06,10.06,0,0,0,2.73-.95,8.51,8.51,0,0,0,3.45-4.5,5.88,5.88,0,0,0,.39-1.48c.21-2.2-1.7-4.1-3.78-4.82s-4.35-.61-6.54-.83a20.6,20.6,0,0,1-18.24-17.21c-.08-.48-10.84-.19-12,.43-1.32.73-2.12,2.54-2.87,3.77Q302.78,828.43,301.24,831.55Z" transform="translate(-35 -34.22)" fill="#a26565"/><path d="M301.24,831.55a35.81,35.81,0,0,0-1.8,4.18c-1.34,3.95-1.54,8.19-1.74,12.36a6,6,0,0,0,.22,2.39c.58,1.5,2.24,2.26,3.79,2.67,6.49,1.75,13.33.11,20-.49s13.26-.16,19.7-1.61a10.06,10.06,0,0,0,2.73-.95,8.51,8.51,0,0,0,3.45-4.5,5.88,5.88,0,0,0,.39-1.48c.21-2.2-1.7-4.1-3.78-4.82s-4.35-.61-6.54-.83a20.6,20.6,0,0,1-18.24-17.21c-.08-.48-10.84-.19-12,.43-1.32.73-2.12,2.54-2.87,3.77Q302.78,828.43,301.24,831.55Z" transform="translate(-35 -34.22)" opacity="0.1"/><path d="M319.09,834.73a34,34,0,0,0-1.8,4.17c-1.35,4-1.55,8.19-1.74,12.36a5.83,5.83,0,0,0,.22,2.39c.57,1.5,2.23,2.26,3.78,2.68,6.49,1.74,13.34.1,20-.5s13.27-.16,19.7-1.61a9.62,9.62,0,0,0,2.73-1,8.47,8.47,0,0,0,3.45-4.5,5.88,5.88,0,0,0,.39-1.48c.21-2.19-1.7-4.1-3.78-4.82s-4.35-.6-6.54-.83a20.61,20.61,0,0,1-18.24-17.2c-.08-.48-10.84-.2-12,.42-1.33.73-2.13,2.54-2.88,3.77C321.24,830.61,320.12,832.65,319.09,834.73Z" transform="translate(-35 -34.22)" fill="#a26565"/><path d="M365.62,728.5c.14,21.61-6.14,42.73-13.51,63q-4.38,12.06-9.29,23.91c-1.94,4.68-4.13,9.88-2.05,14.5-6-2.28-12.1-3.62-18.41-2.57,2.4-4.9,2.17-10.6,2.28-16A140.3,140.3,0,0,1,327,789.29a123,123,0,0,1,10.79-31.89,24.3,24.3,0,0,0,2.09-6.72c.82-5.14.18-10.42-.89-15.51a92.47,92.47,0,0,0-12.9-31.6l-.06.64a93.27,93.27,0,0,1-1.62,9.57c-1.41,6.69-3.06,13.39-2.81,20.12a108.83,108.83,0,0,1-1.32,21.33,32.43,32.43,0,0,1-2.15,8.06c-.69,1.55-1.59,3-2.13,4.61a21,21,0,0,0-.84,5.7c-.8,16.27,0,32.79,3.32,48.74-5.57-.44-11.14,1.17-16.12,3.67a198.54,198.54,0,0,1-8.5-35.1,116.62,116.62,0,0,1,1.3-42.46,56.42,56.42,0,0,0,1.05-14.15c-.43-9.45-2.75-18.69-5.06-27.87l-3.52-14q-2.5-10-5-19.91c-1.11-4.41-2.23-8.92-1.88-13.46a33.4,33.4,0,0,1,1.56-7.32,81.69,81.69,0,0,1,11.07-22.64c19.27,1.08,38.77-.72,58-2.2a3.89,3.89,0,0,1,2.72.46,3.85,3.85,0,0,1,1.1,2.48l7.23,41a103,103,0,0,1,2.05,17.3c0,5.8-.9,11.58-.68,17.37C364.1,713.31,365.58,720.69,365.62,728.5Z" transform="translate(-35 -34.22)" fill="#454b69"/><path d="M326.05,704.21a93.27,93.27,0,0,1-1.62,9.57c-4.19-7.25-8.71-14.32-12.57-21.75-1-1.91-1.94-3.85-2.89-5.79l-2.85-5.8c-.53-1.09-1.06-2.18-1.54-3.29a23.77,23.77,0,0,1-1.79-5c1.4-.26,4.86,3.52,5.9,4.51a80.64,80.64,0,0,1,6,6.64,72.81,72.81,0,0,1,6.26,8.2A39.88,39.88,0,0,1,326.05,704.21Z" transform="translate(-35 -34.22)" opacity="0.1"/><path d="M342.1,505.3s-10.7,17.05-8.72,20.62-33.7-3.17-33.7-3.17,19.43-17.05,17.44-24.19S342.1,505.3,342.1,505.3Z" transform="translate(-35 -34.22)" fill="#efb7b9"/><circle cx="297.59" cy="452.05" r="24.19" fill="#efb7b9"/><path d="M342.46,523.18A24.56,24.56,0,0,0,332.29,515a36.38,36.38,0,0,0-7.23-1.78l-9.65-1.7c-2.36-.42-4.89-.81-7.09.16a11.62,11.62,0,0,0-3,2.21c-3.35,3.11-6.6,6.52-10.81,8.32-1.15.49-2.35.85-3.5,1.34-4.71,2-8.23,6.26-10.22,11-2.1,4.94-2.16,10.31-2,15.68a27.56,27.56,0,0,0,.55,5.52,35.57,35.57,0,0,0,1.83,5.06,92.57,92.57,0,0,1,3.34,10.73q3.75,14.35,6.24,29a10.87,10.87,0,0,0,1.21,4,14.36,14.36,0,0,0,3.34,3.21,20.86,20.86,0,0,1,2.79,2.77A3.62,3.62,0,0,1,299,612c.31,1.44-1,2.68-2,3.73a18.06,18.06,0,0,0-4.52,7.87,3.17,3.17,0,0,0-.72,2.17,3.8,3.8,0,0,1,.23,1.72,3.88,3.88,0,0,1-.91,1.41,5.33,5.33,0,0,0-1,4.28,67.89,67.89,0,0,0,23.68,5.54c2,.1,4,.1,6,.34,1.42.18,2.82.47,4.24.64a45.78,45.78,0,0,0,6.2.16c10.77-.22,21.85-.49,31.74-4.76.93-5.07-1.08-10.24-2.34-15.24-2-8-2.5-16.19-4-24.26-1-5.51-2.5-10.94-3.35-16.49s-1-11.3.48-16.7c1.35-4.78,1.76-9.66,3.16-14.43s2.38-9.94.8-14.66C354.69,527.48,348.62,522.85,342.46,523.18Z" transform="translate(-35 -34.22)" fill="#908820"/><path d="M354.34,473.44c1.18-4.29-1.14-8.76-3.9-12.25-3-3.75-6.82-7.13-11.51-8.07-3.81-.77-7.76.16-11.64-.15-3.43-.27-6.73-1.51-10.14-2a32.12,32.12,0,0,0-9.28.35,35.25,35.25,0,0,0-8.45,2.36c-10.3,4.57-16.33,15.83-17.73,27s1.05,22.43,3.49,33.42l2.21,9.94c2.3,10.35,4.6,20.74,5.21,31.32s-.53,21.44-4.88,31.1a66.89,66.89,0,0,0,30.73-34.79c1.56-4.09,2.72-8.34,4.46-12.36,1.49-3.46,3.4-6.74,4.72-10.27a33.36,33.36,0,0,0,2.11-12.47c-.08-3.2-.61-6.38-.52-9.58s.91-6.53,3.12-8.85c2-2.05,4.75-3,7.28-4.33a30.31,30.31,0,0,0,10.19-8.5c1.59-2.08,1.68-3,2.08-5.36S353.72,475.71,354.34,473.44Z" transform="translate(-35 -34.22)" fill="#a26565"/><path d="M354.37,655.15c.25,2.49-.2,5,.34,7.47.41,1.84,1.36,3.5,1.92,5.3a25.2,25.2,0,0,1,.85,6.37c.08,1.8-.06,4-1.66,4.84-.7.37-1.54.37-2.27.69s-1.35,1.19-.93,1.87,1.14.57,1.8.7,1.37.81,1,1.36a1.14,1.14,0,0,1-.58.37,7.08,7.08,0,0,0-1.88.9,1.92,1.92,0,0,0-.77,1.83,1.13,1.13,0,0,0,1.61.74,5,5,0,0,0-.47,2c2.56,1.1,5.43-.34,7.83-1.73a29.49,29.49,0,0,0,3-1.92A15,15,0,0,0,370,673.17a29.79,29.79,0,0,0-.57-3.65,54.92,54.92,0,0,0-1.47-6.21c-.69-2.15-1.63-4.2-2.44-6.31-1.72-4.49-2.83-9.19-4.39-13.74a2.08,2.08,0,0,0-.75-1.19,2.11,2.11,0,0,0-1.11-.21,18.19,18.19,0,0,0-5.21.48c-1,.3-2.71,1-3.18,2s.28,1.84.73,2.67A22.38,22.38,0,0,1,354.37,655.15Z" transform="translate(-35 -34.22)" fill="#efb7b9"/><path d="M352.74,533.64a7.33,7.33,0,0,1,1.28,1.78c3.44,6.25,3.48,13.72,3.64,20.85.18,8,.62,16,1.06,24.05l1.31,24c.13,2.29.25,4.58.58,6.85,1.54.39,1.6,2.39,1.43,4-1.11,9.78-3.86,19.85-1,29.26a2.94,2.94,0,0,0,.67,1.3,7.81,7.81,0,0,0-4.47-.06c-1.43.3-2.81.83-4.25,1.16-1.82.41-3.88.59-5.12,2a99.24,99.24,0,0,1-3.35-12.92c-.68-4-.87-8.12-2.49-11.82-.65-1.49-1.53-2.87-2.33-4.3-4.19-7.48-6.25-15.93-8.26-24.27a35.43,35.43,0,0,1-.64-12.06,41.47,41.47,0,0,0,.5-5.55,42.64,42.64,0,0,0-.77-5.84,39.15,39.15,0,0,1,1.17-16.91c1.5-5.09,2.82-10.45,6-14.73S347.43,533.71,352.74,533.64Z" transform="translate(-35 -34.22)" opacity="0.1"/><path d="M353.93,532.84a7.39,7.39,0,0,1,1.28,1.79c3.44,6.24,3.48,13.72,3.64,20.85.18,8,.62,16,1.06,24l1.31,24c.13,2.29.25,4.58.58,6.85,1.54.39,1.6,2.39,1.42,4-1.1,9.78-3.85,19.84-1,29.26a2.94,2.94,0,0,0,.67,1.3,7.81,7.81,0,0,0-4.47-.06c-1.43.3-2.81.83-4.25,1.15-1.82.42-3.88.59-5.12,2A98.21,98.21,0,0,1,345.73,635c-.68-4-.87-8.11-2.49-11.81a48.28,48.28,0,0,0-2.33-4.3c-4.2-7.48-6.25-15.94-8.26-24.28a35.42,35.42,0,0,1-.64-12.06,26,26,0,0,0-.27-11.39,39.1,39.1,0,0,1,1.17-16.9c1.5-5.09,2.82-10.45,6-14.73S348.62,532.92,353.93,532.84Z" transform="translate(-35 -34.22)" fill="#908820"/><g opacity="0.1"><path d="M350.44,461.19c-3-3.75-6.82-7.13-11.51-8.07-3.81-.77-7.76.16-11.64-.15-3.43-.27-6.73-1.51-10.14-2a32.12,32.12,0,0,0-9.28.35,35.25,35.25,0,0,0-8.45,2.36,25.09,25.09,0,0,0-4.17,2.37,35.56,35.56,0,0,1,7.47-2,32,32,0,0,1,9.27-.35c3.42.44,6.71,1.67,10.15,1.94,3.87.31,7.82-.61,11.64.15,4.69.94,8.53,4.33,11.5,8.08,2.77,3.48,5.08,7.95,3.91,12.25-.62,2.26-2.06,4.24-2.45,6.57s-.49,3.28-2.09,5.36a30.38,30.38,0,0,1-7.34,6.83c.77-.36,1.56-.72,2.31-1.11a30.31,30.31,0,0,0,10.19-8.5c1.59-2.08,1.68-3,2.08-5.36s1.83-4.3,2.45-6.57C355.52,469.15,353.2,464.68,350.44,461.19Z" transform="translate(-35 -34.22)"/><path d="M332.34,498.2a10,10,0,0,1,1.35-1.17c-2.31,1.12-4.74,2.1-6.51,4-2.21,2.31-3,5.65-3.12,8.85s.45,6.38.52,9.57a33.41,33.41,0,0,1-2.11,12.48c-1.32,3.53-3.23,6.8-4.72,10.27-1.73,4-2.89,8.26-4.45,12.35a67,67,0,0,1-25.41,31.62c-.06.13-.11.27-.17.4a66.89,66.89,0,0,0,30.73-34.79c1.56-4.09,2.72-8.34,4.46-12.36,1.49-3.46,3.4-6.74,4.72-10.27a33.36,33.36,0,0,0,2.11-12.47c-.08-3.2-.61-6.38-.52-9.58S330.13,500.52,332.34,498.2Z" transform="translate(-35 -34.22)"/></g></svg>
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0; url=docs/installation-ios">
<script type="text/javascript">
window.location.href = 'docs/installation-ios';
</script>
<title>Your Site Title Here</title>
</head>
<body>
If you are not redirected automatically, follow this <a href="docs/installation-ios">link</a>.
</body>
</html>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment