diff --git a/.circleci/config.yml b/.circleci/config.yml
index 73b81fb16..7667897cc 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -119,7 +119,7 @@ jobs:
- save_cache: *save-npm-cache-linux
# Android builds
- android-build:
+ android-play-build:
<<: *defaults
docker:
- image: circleci/android:api-28-node
@@ -150,7 +150,7 @@ jobs:
# echo -e "android.enableAapt2=false" >> ./gradle.properties
echo -e "android.useAndroidX=true" >> ./gradle.properties
echo -e "android.enableJetifier=true" >> ./gradle.properties
- echo -e "FLIPPER_VERSION=0.33.1" >> ./gradle.properties
+ echo -e "FLIPPER_VERSION=0.51.0" >> ./gradle.properties
if [[ $KEYSTORE ]]; then
echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE
@@ -167,8 +167,10 @@ jobs:
- run:
name: Set Google Services
command: |
- cp google-services.prod.json google-services.json
- working_directory: android/app
+ if [[ $KEYSTORE ]]; then
+ echo $GOOGLE_SERVICES_ANDROID | base64 --decode > google-services.json
+ fi
+ working_directory: android/app/src/play
- run:
name: Config variables
@@ -176,12 +178,12 @@ jobs:
echo -e "export default { BUGSNAG_API_KEY: '$BUGSNAG_KEY' };" > ./config.js
- run:
- name: Build Android App
+ name: Build Android Play App
command: |
if [[ $KEYSTORE ]]; then
- bundle exec fastlane android release
+ bundle exec fastlane android playRelease
else
- bundle exec fastlane android build
+ bundle exec fastlane android playBuild
fi
working_directory: android
@@ -192,8 +194,8 @@ jobs:
yarn generate-source-maps-android upload \
--api-key=$BUGSNAG_KEY \
--app-version=$CIRCLE_BUILD_NUM \
- --minifiedFile=android/app/build/generated/assets/react/release/app.bundle \
- --source-map=android/app/build/generated/sourcemaps/react/release/app.bundle.map \
+ --minifiedFile=android/app/build/generated/assets/react/play/release/app.bundle \
+ --source-map=android/app/build/generated/sourcemaps/react/play/release/app.bundle.map \
--minified-url=app.bundle \
--upload-sources
fi
@@ -211,7 +213,68 @@ jobs:
- android/fastlane/report.xml
- android/app/build/outputs
- android-google-play-alpha:
+ android-foss-build:
+ <<: *defaults
+ docker:
+ - image: circleci/android:api-28-node
+
+ environment:
+ JAVA_OPTS: '-Xms512m -Xmx2g'
+ GRADLE_OPTS: '-Xmx3g -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"'
+ TERM: dumb
+ <<: *bash-env
+
+ steps:
+ - checkout
+
+ - run: *install-node
+
+ - restore_cache: *restore-npm-cache-linux
+
+ - run: *install-npm-modules
+
+ - run: *update-fastlane-android
+
+ - restore_cache: *restore-gradle-cache
+
+ - run:
+ name: Configure Gradle
+ command: |
+ echo -e "" > ./gradle.properties
+ # echo -e "android.enableAapt2=false" >> ./gradle.properties
+ echo -e "android.useAndroidX=true" >> ./gradle.properties
+ echo -e "android.enableJetifier=true" >> ./gradle.properties
+ echo -e "FLIPPER_VERSION=0.51.0" >> ./gradle.properties
+ echo -e "VERSIONCODE=$CIRCLE_BUILD_NUM" >> ./gradle.properties
+
+ if [[ $KEYSTORE ]]; then
+ echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE
+ echo -e "KEYSTORE=$KEYSTORE" >> ./gradle.properties
+ echo -e "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
+ echo -e "KEY_ALIAS=$KEY_ALIAS" >> ./gradle.properties
+ echo -e "KEY_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
+ fi
+ working_directory: android
+
+ - run:
+ name: Build Android Foss App
+ command: bundle exec fastlane android fossRelease
+ working_directory: android
+
+ - store_artifacts:
+ path: android/app/build/outputs
+
+ - save_cache: *save-npm-cache-linux
+
+ - save_cache: *save-gradle-cache
+
+ - persist_to_workspace:
+ root: .
+ paths:
+ - android/fastlane/report.xml
+ - android/app/build/outputs
+
+ android-google-play-beta:
<<: *defaults
docker:
- image: circleci/android:api-28-node
@@ -231,7 +294,7 @@ jobs:
- run:
name: Fastlane Play Store Upload
- command: bundle exec fastlane android alpha
+ command: bundle exec fastlane android beta
working_directory: android
# iOS builds
@@ -254,7 +317,9 @@ jobs:
- run:
name: Set Google Services
command: |
- cp GoogleService-Info.prod.plist GoogleService-Info.plist
+ if [[ $KEYSTORE ]]; then
+ echo $GOOGLE_SERVICES_REACTNATIVE | base64 --decode > GoogleService-Info.plist
+ fi
working_directory: ios
- run:
@@ -337,13 +402,20 @@ workflows:
requires:
- ios-hold-testflight
- - android-build:
+ - android-play-build:
requires:
- lint-testunit
- - android-hold-google-play-alpha:
+ - android-hold-google-play-beta:
type: approval
requires:
- - android-build
- - android-google-play-alpha:
+ - android-play-build
+ - android-google-play-beta:
requires:
- - android-hold-google-play-alpha
+ - android-hold-google-play-beta
+ - android-hold-foss-build:
+ type: approval
+ requires:
+ - lint-testunit
+ - android-foss-build:
+ requires:
+ - android-hold-foss-build
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 8436523f2..cb2d252ef 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,6 +1,38 @@
-- Your Rocket.Chat Experimental app version: ####
-- Your Rocket.Chat server version: ####
-- Device (or Simulator) you're running with: ####
+
+
+### Description:
+
+
+
+### Environment Information:
+
+- Rocket.Chat Server Version:
+- Rocket.Chat App Version:
+- Device Name:
+- OS Version:
+
+### Steps to reproduce:
+
+1.
+2.
+3.
+
+### Expected behavior:
+
+
+
+### Actual behavior:
+
+
+
+### Additional context:
+
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1990eb949..dee6f6dd4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,19 +20,23 @@ yarn
Run the app:
```sh
-npx react-native run-ios
+yarn ios
```
or
```sh
-npx react-native run-android
+yarn android
```
At this point, the app should be running on the simulator or on your device!
*Note: npm won't work on this project*
+### How to inspect the app
+
+We use [Reactotron](https://github.com/infinitered/reactotron) to inspect logs, redux state, redux-sagas, HTTP requests, etc.
+
## Issues needing help
Didn't find a bug or want a new feature not already reported? Check out the [help wanted](https://github.com/RocketChat/Rocket.Chat.ReactNative/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%91%8B+help+wanted%22) or the [good first issue](https://github.com/RocketChat/Rocket.Chat.ReactNative/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%8D%AD+good+first+issue%22) labels.
diff --git a/__mocks__/react-native-config-reader.js b/__mocks__/react-native-config-reader.js
new file mode 100644
index 000000000..2286d3cb6
--- /dev/null
+++ b/__mocks__/react-native-config-reader.js
@@ -0,0 +1,3 @@
+export default {
+ BuildConfigs: null
+};
diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap
index b9d4dc1ee..20dab567c 100644
--- a/__tests__/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/__snapshots__/Storyshots.test.js.snap
@@ -3333,6 +3333,165 @@ exports[`Storyshots Message list message 1`] = `
>
Edited
+
+ Encrypted
+
+
+
+
+
+
+
+
+ 10:00 AM
+
+
+
+
+
+
+
+ This message has error and is encrypted
+
+
+
+
+
+
+
+
+
= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-appcenter (1.8.0)
gh_inspector (1.1.3)
google-api-client (0.36.4)
addressable (~> 2.5, >= 2.5.1)
@@ -156,6 +155,7 @@ GEM
uber (0.1.0)
unf (0.1.4)
unf_ext
+ unf_ext (0.0.7.7)
unf_ext (0.0.7.7-x64-mingw32)
unicode-display_width (1.7.0)
word_wrap (1.0.0)
@@ -171,11 +171,11 @@ GEM
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
+ ruby
x64-mingw32
DEPENDENCIES
fastlane
- fastlane-plugin-appcenter
BUNDLED WITH
2.0.2
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3a6635823..566f17dd4 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,8 +1,13 @@
+def taskRequests = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase()
+def isPlay = !taskRequests.contains("foss")
+
apply plugin: "com.android.application"
-apply plugin: 'com.google.gms.google-services'
-apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android'
-apply plugin: 'com.bugsnag.android.gradle'
+
+if (isPlay) {
+ apply plugin: 'com.google.firebase.crashlytics'
+ apply plugin: 'com.bugsnag.android.gradle'
+}
import com.android.build.OutputFile
@@ -139,9 +144,11 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
- versionName "4.10.0"
+ versionName "4.11.0"
vectorDrawables.useSupportLibrary = true
- manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
+ if (isPlay) {
+ manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
+ }
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
}
@@ -168,8 +175,10 @@ android {
minifyEnabled enableProguardInReleaseBuilds
setProguardFiles([getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'])
signingConfig signingConfigs.release
- firebaseCrashlytics {
- nativeSymbolUploadEnabled true
+ if (isPlay) {
+ firebaseCrashlytics {
+ nativeSymbolUploadEnabled true
+ }
}
}
}
@@ -182,6 +191,19 @@ android {
// }
// applicationVariants are e.g. debug, release
+
+ flavorDimensions "type"
+ productFlavors {
+ foss {
+ dimension = "type"
+ buildConfigField "boolean", "FDROID_BUILD", "true"
+ }
+ play {
+ dimension = "type"
+ buildConfigField "boolean", "FDROID_BUILD", "false"
+ }
+ }
+
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
@@ -217,7 +239,7 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
- implementation "com.google.firebase:firebase-messaging:18.0.0"
+ playImplementation "com.google.firebase:firebase-messaging:18.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
@@ -240,6 +262,7 @@ dependencies {
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.github.bumptech.glide:glide:4.9.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
+ implementation "com.tencent:mmkv-static:1.2.1"
}
// Run this once to be able to run the application with BUCK
@@ -249,4 +272,7 @@ task copyDownloadableDepsToLibs(type: Copy) {
into 'libs'
}
-apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
\ No newline at end of file
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
+if (isPlay) {
+ apply plugin: 'com.google.gms.google-services'
+}
diff --git a/android/app/google-services.prod.json b/android/app/google-services.prod.json
deleted file mode 100644
index f6e7e6b9a..000000000
--- a/android/app/google-services.prod.json
+++ /dev/null
@@ -1,231 +0,0 @@
-{
- "project_info": {
- "project_number": "673693445664",
- "firebase_url": "https://rocketchat-9e9be.firebaseio.com",
- "project_id": "rocketchat-9e9be",
- "storage_bucket": "rocketchat-9e9be.appspot.com"
- },
- "client": [
- {
- "client_info": {
- "mobilesdk_app_id": "1:673693445664:android:6ef4638e500ec958",
- "android_client_info": {
- "package_name": "RocketChat"
- }
- },
- "oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "chat.rocket.reactnative"
- }
- }
- ]
- }
- }
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:673693445664:android:16da2e50aff9f0c9",
- "android_client_info": {
- "package_name": "chat.rocket.android"
- }
- },
- "oauth_client": [
- {
- "client_id": "673693445664-k0mvosdjoe5dbvqce3b377ckabb5dgu8.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "chat.rocket.android",
- "certificate_hash": "33fa8582794176014a59054192e261bfad0e5273"
- }
- },
- {
- "client_id": "673693445664-hrjftksij02vqtd467ln2cubvu48ft5j.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "chat.rocket.android",
- "certificate_hash": "41cf750df786a6d9da712a98a629d0c8391876d6"
- }
- },
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "chat.rocket.reactnative"
- }
- }
- ]
- }
- }
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:673693445664:android:1551054db195f705",
- "android_client_info": {
- "package_name": "chat.rocket.android.dev"
- }
- },
- "oauth_client": [
- {
- "client_id": "673693445664-t5aeku0oie010npd40a0tgn27c418vk7.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "chat.rocket.android.dev",
- "certificate_hash": "41cf750df786a6d9da712a98a629d0c8391876d6"
- }
- },
- {
- "client_id": "673693445664-iml14ln4vccuu7liclrpt2k671fkjs38.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "chat.rocket.android.dev",
- "certificate_hash": "33fa8582794176014a59054192e261bfad0e5273"
- }
- },
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "chat.rocket.reactnative"
- }
- }
- ]
- }
- }
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:673693445664:android:8be27b1f7c42a2ed",
- "android_client_info": {
- "package_name": "chat.rocket.reactnative"
- }
- },
- "oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "chat.rocket.reactnative"
- }
- }
- ]
- }
- }
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:673693445664:android:64932c99863e2838",
- "android_client_info": {
- "package_name": "com.konecty.rocket.chat"
- }
- },
- "oauth_client": [
- {
- "client_id": "673693445664-3ajben08beuco6eout3kpod2gbbm8fij.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "com.konecty.rocket.chat",
- "certificate_hash": "cd5806ba3f0141d0f2e47acfe64a485f575108ab"
- }
- },
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com",
- "client_type": 3
- },
- {
- "client_id": "673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com",
- "client_type": 2,
- "ios_info": {
- "bundle_id": "chat.rocket.reactnative"
- }
- }
- ]
- }
- }
- }
- ],
- "configuration_version": "1"
-}
\ No newline at end of file
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index ed773abe1..bc6c63eab 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -7,7 +7,8 @@
android:name=".MainDebugApplication"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:name"
- tools:targetApi="28" />
+ tools:targetApi="28"
+ android:networkSecurityConfig="@xml/network_security_config" />
\ No newline at end of file
diff --git a/android/app/src/debug/res/xml/network_security_config.xml b/android/app/src/debug/res/xml/network_security_config.xml
new file mode 100644
index 000000000..45f56937c
--- /dev/null
+++ b/android/app/src/debug/res/xml/network_security_config.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java
new file mode 100644
index 000000000..d8c0502eb
--- /dev/null
+++ b/android/app/src/foss/java/chat/rocket/reactnative/CustomPushNotification.java
@@ -0,0 +1,20 @@
+package chat.rocket.reactnative;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.wix.reactnativenotifications.core.AppLaunchHelper;
+import com.wix.reactnativenotifications.core.AppLifecycleFacade;
+import com.wix.reactnativenotifications.core.JsIOHelper;
+import com.wix.reactnativenotifications.core.notification.PushNotification;
+
+public class CustomPushNotification extends PushNotification {
+ public static ReactApplicationContext reactApplicationContext;
+
+ public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
+ super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
+ reactApplicationContext = new ReactApplicationContext(context);
+ }
+
+}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java b/android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java
similarity index 68%
rename from android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java
rename to android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java
index 32524a35f..48350be51 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java
+++ b/android/app/src/foss/java/chat/rocket/reactnative/DismissNotification.java
@@ -7,7 +7,6 @@ import android.content.Intent;
public class DismissNotification extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- int notId = intent.getExtras().getInt(CustomPushNotification.NOTIFICATION_ID);
- CustomPushNotification.clearMessages(notId);
+
}
}
diff --git a/android/app/src/foss/java/chat/rocket/reactnative/Ejson.java b/android/app/src/foss/java/chat/rocket/reactnative/Ejson.java
new file mode 100644
index 000000000..baf263260
--- /dev/null
+++ b/android/app/src/foss/java/chat/rocket/reactnative/Ejson.java
@@ -0,0 +1,97 @@
+package chat.rocket.reactnative;
+
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.Callback;
+
+import com.ammarahmed.mmkv.SecureKeystore;
+import com.tencent.mmkv.MMKV;
+
+import java.math.BigInteger;
+
+class RNCallback implements Callback {
+ public void invoke(Object... args) {
+
+ }
+}
+
+class Utils {
+ static public String toHex(String arg) {
+ try {
+ return String.format("%x", new BigInteger(1, arg.getBytes("UTF-8")));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+}
+
+public class Ejson {
+ String host;
+ String rid;
+ String type;
+ Sender sender;
+ String messageId;
+ String notificationType;
+
+ private MMKV mmkv;
+
+ private String TOKEN_KEY = "reactnativemeteor_usertoken-";
+
+ public Ejson() {
+ ReactApplicationContext reactApplicationContext = CustomPushNotification.reactApplicationContext;
+
+ // Start MMKV container
+ MMKV.initialize(reactApplicationContext);
+ SecureKeystore secureKeystore = new SecureKeystore(reactApplicationContext);
+
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ String alias = Utils.toHex("com.MMKV.default");
+
+ // Retrieve container password
+ secureKeystore.getSecureKey(alias, new RNCallback() {
+ @Override
+ public void invoke(Object... args) {
+ String error = (String) args[0];
+ if (error == null) {
+ String password = (String) args[1];
+ mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
+ }
+ }
+ });
+ }
+
+ public String getAvatarUri() {
+ if (type == null) {
+ return null;
+ }
+ return serverURL() + "/avatar/" + this.sender.username + "?rc_token=" + token() + "&rc_uid=" + userId();
+ }
+
+ public String token() {
+ String userId = userId();
+ if (mmkv != null && userId != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(userId));
+ }
+ return "";
+ }
+
+ public String userId() {
+ String serverURL = serverURL();
+ if (mmkv != null && serverURL != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(serverURL));
+ }
+ return "";
+ }
+
+ public String serverURL() {
+ String url = this.host;
+ if (url != null && url.endsWith("/")) {
+ url = url.substring(0, url.length() - 1);
+ }
+ return url;
+ }
+
+ public class Sender {
+ String username;
+ String _id;
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java
new file mode 100644
index 000000000..5203495b8
--- /dev/null
+++ b/android/app/src/foss/java/chat/rocket/reactnative/ReplyBroadcast.java
@@ -0,0 +1,13 @@
+package chat.rocket.reactnative;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class ReplyBroadcast extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ }
+}
diff --git a/android/app/src/foss/res/drawable/ic_launcher_background.xml b/android/app/src/foss/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..1369c4c71
--- /dev/null
+++ b/android/app/src/foss/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/foss/res/drawable/ic_launcher_foreground.xml b/android/app/src/foss/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..4fb25367c
--- /dev/null
+++ b/android/app/src/foss/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/foss/res/drawable/launch_screen.xml b/android/app/src/foss/res/drawable/launch_screen.xml
new file mode 100644
index 000000000..d4015d97e
--- /dev/null
+++ b/android/app/src/foss/res/drawable/launch_screen.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ -
+
+
+
+
diff --git a/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..b3ee0dad1
--- /dev/null
+++ b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..b3ee0dad1
--- /dev/null
+++ b/android/app/src/foss/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..f1878e47b
Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..8538bed39
Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..bd72cd260
Binary files /dev/null and b/android/app/src/foss/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..1e3da8a39
Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..a92ccfafb
Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..dfb390737
Binary files /dev/null and b/android/app/src/foss/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..ce6abf0d6
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..3ba37c9e9
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..710fac56a
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..1ba89143e
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..07f6de5a9
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..86822fdd2
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..2830a1249
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..5ae3008fe
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..a17913dc1
Binary files /dev/null and b/android/app/src/foss/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/foss/res/values-night/colors.xml b/android/app/src/foss/res/values-night/colors.xml
new file mode 100644
index 000000000..3c1a14f89
--- /dev/null
+++ b/android/app/src/foss/res/values-night/colors.xml
@@ -0,0 +1,4 @@
+
+
+ - #000000
+
diff --git a/android/app/src/foss/res/values/colors.xml b/android/app/src/foss/res/values/colors.xml
new file mode 100644
index 000000000..35e0dfb6d
--- /dev/null
+++ b/android/app/src/foss/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #660B0B0B
+ - #eeeff1
+ - #CC3333
+
diff --git a/android/app/src/foss/res/values/strings.xml b/android/app/src/foss/res/values/strings.xml
new file mode 100644
index 000000000..22a0b201d
--- /dev/null
+++ b/android/app/src/foss/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Rocket.Chat
+ Rocket.Chat
+
diff --git a/android/app/src/foss/res/values/styles.xml b/android/app/src/foss/res/values/styles.xml
new file mode 100644
index 000000000..50861d8e0
--- /dev/null
+++ b/android/app/src/foss/res/values/styles.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index ebfb8c74d..f95275dfe 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -67,9 +67,6 @@
-
diff --git a/android/app/src/main/assets/fonts/custom.ttf b/android/app/src/main/assets/fonts/custom.ttf
index a96927b90..9077f7ef4 100644
Binary files a/android/app/src/main/assets/fonts/custom.ttf and b/android/app/src/main/assets/fonts/custom.ttf differ
diff --git a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
deleted file mode 100644
index 0c68e4755..000000000
--- a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package chat.rocket.reactnative;
-
-import android.content.SharedPreferences;
-
-import chat.rocket.userdefaults.RNUserDefaultsModule;
-
-public class Ejson {
- String host;
- String rid;
- String type;
- Sender sender;
- String messageId;
- String notificationType;
-
- private String TOKEN_KEY = "reactnativemeteor_usertoken-";
- private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);
-
- public String getAvatarUri() {
- if (type == null) {
- return null;
- }
- return serverURL() + "/avatar/" + this.sender.username + "?rc_token=" + token() + "&rc_uid=" + userId();
- }
-
- public String token() {
- return sharedPreferences.getString(TOKEN_KEY.concat(userId()), "");
- }
-
- public String userId() {
- return sharedPreferences.getString(TOKEN_KEY.concat(serverURL()), "");
- }
-
- public String serverURL() {
- String url = this.host;
- if (url != null && url.endsWith("/")) {
- url = url.substring(0, url.length() - 1);
- }
- return url;
- }
-
- public class Sender {
- String username;
- String _id;
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java b/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java
deleted file mode 100644
index 9c4c03744..000000000
--- a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package chat.rocket.reactnative;
-
-import android.os.Bundle;
-import android.content.Context;
-
-import okhttp3.Call;
-import okhttp3.OkHttpClient;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.Interceptor;
-
-import com.google.gson.Gson;
-import java.io.IOException;
-
-import com.facebook.react.bridge.ReactApplicationContext;
-
-import chat.rocket.userdefaults.RNUserDefaultsModule;
-
-class JsonResponse {
- Data data;
-
- class Data {
- Notification notification;
-
- class Notification {
- String notId;
- String title;
- String text;
- Payload payload;
-
- class Payload {
- String host;
- String rid;
- String type;
- Sender sender;
- String messageId;
- String notificationType;
- String name;
- String messageType;
-
- class Sender {
- String _id;
- String username;
- String name;
- }
- }
- }
- }
-}
-
-public class LoadNotification {
- private static int RETRY_COUNT = 0;
- private static int[] TIMEOUT = new int[]{ 0, 1, 3, 5, 10 };
- private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
-
- public static void load(ReactApplicationContext reactApplicationContext, final Ejson ejson, Callback callback) {
- final OkHttpClient client = new OkHttpClient();
- HttpUrl.Builder url = HttpUrl.parse(ejson.serverURL().concat("/api/v1/push.get")).newBuilder();
-
- Request request = new Request.Builder()
- .header("x-user-id", ejson.userId())
- .header("x-auth-token", ejson.token())
- .url(url.addQueryParameter("id", ejson.messageId).build())
- .build();
-
- runRequest(client, request, callback);
- }
-
- private static void runRequest(OkHttpClient client, Request request, Callback callback) {
- try {
- Thread.sleep(TIMEOUT[RETRY_COUNT] * 1000);
-
- Response response = client.newCall(request).execute();
- String body = response.body().string();
- if (!response.isSuccessful()) {
- throw new Exception("Error");
- }
-
- Gson gson = new Gson();
- JsonResponse json = gson.fromJson(body, JsonResponse.class);
-
- Bundle bundle = new Bundle();
- bundle.putString("notId", json.data.notification.notId);
- bundle.putString("title", json.data.notification.title);
- bundle.putString("message", json.data.notification.text);
- bundle.putString("ejson", gson.toJson(json.data.notification.payload));
- bundle.putBoolean("notificationLoaded", true);
-
- callback.call(bundle);
-
- } catch (Exception e) {
- if (RETRY_COUNT <= TIMEOUT.length) {
- RETRY_COUNT++;
- runRequest(client, request, callback);
- } else {
- callback.call(null);
- }
- }
- }
-}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
index 759229137..5eefab57a 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.java
@@ -1,13 +1,33 @@
package chat.rocket.reactnative;
-import com.facebook.react.ReactActivityDelegate;
-import com.facebook.react.ReactRootView;
-import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import android.os.Bundle;
-import com.facebook.react.ReactFragmentActivity;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.SharedPreferences;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.ReactRootView;
+import com.facebook.react.ReactActivityDelegate;
+import com.facebook.react.ReactFragmentActivity;
+
+import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import com.zoontek.rnbootsplash.RNBootSplash;
+import com.tencent.mmkv.MMKV;
+import com.google.gson.Gson;
+
+class ThemePreferences {
+ String currentTheme;
+ String darkLevel;
+}
+
+class SortPreferences {
+ String sortBy;
+ Boolean groupByType;
+ Boolean showFavorites;
+ Boolean showUnread;
+}
public class MainActivity extends ReactFragmentActivity {
@@ -16,6 +36,61 @@ public class MainActivity extends ReactFragmentActivity {
// https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067
super.onCreate(null);
RNBootSplash.init(R.drawable.launch_screen, MainActivity.this);
+
+ MMKV.initialize(MainActivity.this);
+
+ // Start the MMKV container
+ MMKV defaultMMKV = MMKV.defaultMMKV();
+ boolean alreadyMigrated = defaultMMKV.decodeBool("alreadyMigrated");
+
+ if (!alreadyMigrated) {
+ // MMKV Instance that will be used by JS
+ MMKV mmkv = MMKV.mmkvWithID("default");
+
+ // SharedPreferences -> MMKV (Migration)
+ SharedPreferences sharedPreferences = getSharedPreferences("react-native", Context.MODE_PRIVATE);
+ mmkv.importFromSharedPreferences(sharedPreferences);
+
+ // SharedPreferences only save strings, so we saved this value as a String and now we'll need to cast into a MMKV object
+
+ // Theme preferences object
+ String THEME_PREFERENCES_KEY = "RC_THEME_PREFERENCES_KEY";
+ String themeJson = sharedPreferences.getString(THEME_PREFERENCES_KEY, "");
+ if (!themeJson.isEmpty()) {
+ ThemePreferences themePreferences = new Gson().fromJson(themeJson, ThemePreferences.class);
+ WritableMap themeMap = new Arguments().createMap();
+ themeMap.putString("currentTheme", themePreferences.currentTheme);
+ themeMap.putString("darkLevel", themePreferences.darkLevel);
+ Bundle bundle = Arguments.toBundle(themeMap);
+ mmkv.encode(THEME_PREFERENCES_KEY, bundle);
+ }
+
+ // Sort preferences object
+ String SORT_PREFS_KEY = "RC_SORT_PREFS_KEY";
+ String sortJson = sharedPreferences.getString(SORT_PREFS_KEY, "");
+ if (!sortJson.isEmpty()) {
+ SortPreferences sortPreferences = new Gson().fromJson(sortJson, SortPreferences.class);
+ WritableMap sortMap = new Arguments().createMap();
+ sortMap.putString("sortBy", sortPreferences.sortBy);
+ if (sortPreferences.groupByType != null) {
+ sortMap.putBoolean("groupByType", sortPreferences.groupByType);
+ }
+ if (sortPreferences.showFavorites != null) {
+ sortMap.putBoolean("showFavorites", sortPreferences.showFavorites);
+ }
+ if (sortPreferences.showUnread != null) {
+ sortMap.putBoolean("showUnread", sortPreferences.showUnread);
+ }
+ Bundle bundle = Arguments.toBundle(sortMap);
+ mmkv.encode(SORT_PREFS_KEY, bundle);
+ }
+
+ // Remove all our keys of SharedPreferences
+ sharedPreferences.edit().clear().commit();
+
+ // Mark migration complete
+ defaultMMKV.encode("alreadyMigrated", true);
+ }
}
/**
diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
index 6bcc62be8..12d7c0184 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
+++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java
@@ -49,8 +49,10 @@ public class MainApplication extends Application implements ReactApplication, IN
protected List getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List packages = new PackageList(this).getPackages();
+ if (!BuildConfig.FDROID_BUILD) {
+ packages.add(new RNNotificationsPackage(MainApplication.this));
+ }
packages.add(new KeyboardInputPackage(MainApplication.this));
- packages.add(new RNNotificationsPackage(MainApplication.this));
packages.add(new WatermelonDBPackage());
packages.add(new RNCViewPagerPackage());
// packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
diff --git a/android/app/src/main/res/drawable-hdpi/icon_facebook.png b/android/app/src/main/res/drawable-hdpi/icon_facebook.png
deleted file mode 100644
index 5c637c075..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_facebook.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_github.png b/android/app/src/main/res/drawable-hdpi/icon_github.png
deleted file mode 100644
index 32dbe3316..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_github.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_gitlab.png b/android/app/src/main/res/drawable-hdpi/icon_gitlab.png
deleted file mode 100644
index ce6c68bff..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_gitlab.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_google.png b/android/app/src/main/res/drawable-hdpi/icon_google.png
deleted file mode 100644
index c84a47c76..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_google.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_linkedin.png b/android/app/src/main/res/drawable-hdpi/icon_linkedin.png
deleted file mode 100644
index 93708032c..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_linkedin.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_meteor.png b/android/app/src/main/res/drawable-hdpi/icon_meteor.png
deleted file mode 100644
index 1d793b136..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_meteor.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_twitter.png b/android/app/src/main/res/drawable-hdpi/icon_twitter.png
deleted file mode 100644
index 4b057eddf..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_twitter.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-hdpi/icon_wordpress.png b/android/app/src/main/res/drawable-hdpi/icon_wordpress.png
deleted file mode 100644
index 4dbc11e67..000000000
Binary files a/android/app/src/main/res/drawable-hdpi/icon_wordpress.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_facebook.png b/android/app/src/main/res/drawable-mdpi/icon_facebook.png
deleted file mode 100644
index b362e2ffd..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_facebook.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_github.png b/android/app/src/main/res/drawable-mdpi/icon_github.png
deleted file mode 100644
index af76b3fdd..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_github.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_gitlab.png b/android/app/src/main/res/drawable-mdpi/icon_gitlab.png
deleted file mode 100644
index 908f0f31a..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_gitlab.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_google.png b/android/app/src/main/res/drawable-mdpi/icon_google.png
deleted file mode 100644
index 776775448..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_google.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_linkedin.png b/android/app/src/main/res/drawable-mdpi/icon_linkedin.png
deleted file mode 100644
index 29cbe97f9..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_linkedin.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_meteor.png b/android/app/src/main/res/drawable-mdpi/icon_meteor.png
deleted file mode 100644
index 5489e031f..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_meteor.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_twitter.png b/android/app/src/main/res/drawable-mdpi/icon_twitter.png
deleted file mode 100644
index 4570f1a51..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_twitter.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/icon_wordpress.png b/android/app/src/main/res/drawable-mdpi/icon_wordpress.png
deleted file mode 100644
index cc24baf40..000000000
Binary files a/android/app/src/main/res/drawable-mdpi/icon_wordpress.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xhdpi/icon_facebook.png
deleted file mode 100644
index e9543fd0f..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_facebook.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_github.png b/android/app/src/main/res/drawable-xhdpi/icon_github.png
deleted file mode 100644
index f92f531e6..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_github.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png
deleted file mode 100644
index 92a633c84..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_gitlab.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_google.png b/android/app/src/main/res/drawable-xhdpi/icon_google.png
deleted file mode 100644
index 4fb517cff..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_google.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png
deleted file mode 100644
index e96d63def..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_linkedin.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xhdpi/icon_meteor.png
deleted file mode 100644
index 25b8bc182..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_meteor.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xhdpi/icon_twitter.png
deleted file mode 100644
index 7f8cf4796..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_twitter.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xhdpi/icon_wordpress.png b/android/app/src/main/res/drawable-xhdpi/icon_wordpress.png
deleted file mode 100644
index e3457517b..000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/icon_wordpress.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png
deleted file mode 100644
index d6d01570f..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_facebook.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_github.png b/android/app/src/main/res/drawable-xxhdpi/icon_github.png
deleted file mode 100644
index de7533292..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_github.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png
deleted file mode 100644
index fb8211a7d..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_gitlab.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_google.png b/android/app/src/main/res/drawable-xxhdpi/icon_google.png
deleted file mode 100644
index 02342b216..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_google.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png
deleted file mode 100644
index 674c9ef3e..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_linkedin.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png
deleted file mode 100644
index b7a0a8457..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_meteor.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png
deleted file mode 100644
index 3faaeac18..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_twitter.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/icon_wordpress.png b/android/app/src/main/res/drawable-xxhdpi/icon_wordpress.png
deleted file mode 100644
index 82b896631..000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/icon_wordpress.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png b/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png
deleted file mode 100644
index d19bdb238..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_facebook.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_github.png b/android/app/src/main/res/drawable-xxxhdpi/icon_github.png
deleted file mode 100644
index 619329670..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_github.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png b/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png
deleted file mode 100644
index 01100b0fa..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_gitlab.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_google.png b/android/app/src/main/res/drawable-xxxhdpi/icon_google.png
deleted file mode 100644
index 7693fdfc3..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_google.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png b/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png
deleted file mode 100644
index 677a22b77..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_linkedin.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png b/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png
deleted file mode 100644
index eeb52443f..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_meteor.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png b/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png
deleted file mode 100644
index fda06f996..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_twitter.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/icon_wordpress.png b/android/app/src/main/res/drawable-xxxhdpi/icon_wordpress.png
deleted file mode 100644
index 6d7f42995..000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/icon_wordpress.png and /dev/null differ
diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml
index bb6ab93df..ef899d459 100644
--- a/android/app/src/main/res/xml/network_security_config.xml
+++ b/android/app/src/main/res/xml/network_security_config.xml
@@ -1,9 +1,10 @@
-
-
+
+
-
+
\ No newline at end of file
diff --git a/android/app/src/play/AndroidManifest.xml b/android/app/src/play/AndroidManifest.xml
new file mode 100644
index 000000000..ebfb8c74d
--- /dev/null
+++ b/android/app/src/play/AndroidManifest.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/google-services.json b/android/app/src/play/google-services.json
similarity index 100%
rename from android/app/google-services.json
rename to android/app/src/play/google-services.json
diff --git a/android/app/src/main/java/chat/rocket/reactnative/Callback.java b/android/app/src/play/java/chat/rocket/reactnative/Callback.java
similarity index 99%
rename from android/app/src/main/java/chat/rocket/reactnative/Callback.java
rename to android/app/src/play/java/chat/rocket/reactnative/Callback.java
index 2c2b2833a..b94484eb8 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/Callback.java
+++ b/android/app/src/play/java/chat/rocket/reactnative/Callback.java
@@ -1,6 +1,7 @@
package chat.rocket.reactnative;
import android.os.Bundle;
+
import androidx.annotation.Nullable;
public class Callback {
diff --git a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
similarity index 88%
rename from android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java
rename to android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
index 46c086eae..d9b8ebf5a 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java
+++ b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java
@@ -14,12 +14,14 @@ import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.app.Person;
+
import androidx.annotation.Nullable;
import com.google.gson.Gson;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
+
import java.util.concurrent.ExecutionException;
import java.lang.InterruptedException;
@@ -84,6 +86,15 @@ public class CustomPushNotification extends PushNotification {
boolean hasSender = loadedEjson.sender != null;
String title = bundle.getString("title");
+ // If it has a encrypted message
+ if (loadedEjson.msg != null) {
+ // Override message with the decrypted content
+ String decrypted = Encryption.shared.decryptMessage(loadedEjson, reactApplicationContext);
+ if (decrypted != null) {
+ bundle.putString("message", decrypted);
+ }
+ }
+
bundle.putLong("time", new Date().getTime());
bundle.putString("username", hasSender ? loadedEjson.sender.username : title);
bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1");
@@ -114,12 +125,12 @@ public class CustomPushNotification extends PushNotification {
Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
notification
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(intent)
- .setPriority(Notification.PRIORITY_HIGH)
- .setDefaults(Notification.DEFAULT_ALL)
- .setAutoCancel(true);
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(intent)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setDefaults(Notification.DEFAULT_ALL)
+ .setAutoCancel(true);
Integer notificationId = Integer.parseInt(notId);
notificationColor(notification);
@@ -132,7 +143,7 @@ public class CustomPushNotification extends PushNotification {
notificationStyle(notification, notificationId, bundle);
notificationReply(notification, notificationId, bundle);
- // message couldn't be loaded from server (Fallback notification)
+ // message couldn't be loaded from server (Fallback notification)
} else {
Gson gson = new Gson();
// iterate over the current notification ids to dismiss fallback notifications from same server
@@ -163,11 +174,11 @@ public class CustomPushNotification extends PushNotification {
private Bitmap getAvatar(String uri) {
try {
return Glide.with(mContext)
- .asBitmap()
- .apply(RequestOptions.bitmapTransform(new RoundedCorners(10)))
- .load(uri)
- .submit(100, 100)
- .get();
+ .asBitmap()
+ .apply(RequestOptions.bitmapTransform(new RoundedCorners(10)))
+ .load(uri)
+ .submit(100, 100)
+ .get();
} catch (final ExecutionException | InterruptedException e) {
return largeIcon();
}
@@ -203,8 +214,8 @@ public class CustomPushNotification extends PushNotification {
String CHANNEL_NAME = "All";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
- CHANNEL_NAME,
- NotificationManager.IMPORTANCE_DEFAULT);
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT);
final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
@@ -253,9 +264,9 @@ public class CustomPushNotification extends PushNotification {
messageStyle = new Notification.MessagingStyle("");
} else {
Person sender = new Person.Builder()
- .setKey("")
- .setName("")
- .build();
+ .setKey("")
+ .setName("")
+ .build();
messageStyle = new Notification.MessagingStyle(sender);
}
@@ -279,9 +290,14 @@ public class CustomPushNotification extends PushNotification {
} else {
Bitmap avatar = getAvatar(avatarUri);
+ String name = username;
+ if (ejson.senderName != null) {
+ name = ejson.senderName;
+ }
+
Person.Builder sender = new Person.Builder()
- .setKey(senderId)
- .setName(username);
+ .setKey(senderId)
+ .setName(name);
if (avatar != null) {
sender.setIcon(Icon.createWithBitmap(avatar));
@@ -317,18 +333,18 @@ public class CustomPushNotification extends PushNotification {
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
- .setLabel(label)
- .build();
+ .setLabel(label)
+ .build();
CharSequence title = label;
Notification.Action replyAction = new Notification.Action.Builder(smallIconResId, title, replyPendingIntent)
- .addRemoteInput(remoteInput)
- .setAllowGeneratedReplies(true)
- .build();
+ .addRemoteInput(remoteInput)
+ .setAllowGeneratedReplies(true)
+ .build();
notification
- .setShowWhen(true)
- .addAction(replyAction);
+ .setShowWhen(true)
+ .addAction(replyAction);
}
private void notificationDismiss(Notification.Builder notification, int notificationId) {
@@ -336,7 +352,7 @@ public class CustomPushNotification extends PushNotification {
intent.putExtra(NOTIFICATION_ID, notificationId);
PendingIntent dismissPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, intent, 0);
-
+
notification.setDeleteIntent(dismissPendingIntent);
}
diff --git a/android/app/src/play/java/chat/rocket/reactnative/DismissNotification.java b/android/app/src/play/java/chat/rocket/reactnative/DismissNotification.java
new file mode 100644
index 000000000..b43cf295d
--- /dev/null
+++ b/android/app/src/play/java/chat/rocket/reactnative/DismissNotification.java
@@ -0,0 +1,13 @@
+package chat.rocket.reactnative;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class DismissNotification extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int notId = intent.getExtras().getInt(CustomPushNotification.NOTIFICATION_ID);
+ CustomPushNotification.clearMessages(notId);
+ }
+}
diff --git a/android/app/src/play/java/chat/rocket/reactnative/Ejson.java b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java
new file mode 100644
index 000000000..0ce466349
--- /dev/null
+++ b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java
@@ -0,0 +1,107 @@
+package chat.rocket.reactnative;
+
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.Callback;
+
+import com.ammarahmed.mmkv.SecureKeystore;
+import com.tencent.mmkv.MMKV;
+
+import java.math.BigInteger;
+
+class RNCallback implements Callback {
+ public void invoke(Object... args) {
+
+ }
+}
+
+class Utils {
+ static public String toHex(String arg) {
+ try {
+ return String.format("%x", new BigInteger(1, arg.getBytes("UTF-8")));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+}
+
+public class Ejson {
+ String host;
+ String rid;
+ String type;
+ Sender sender;
+ String messageId;
+ String notificationType;
+ String senderName;
+ String msg;
+
+ private MMKV mmkv;
+
+ private String TOKEN_KEY = "reactnativemeteor_usertoken-";
+
+ public Ejson() {
+ ReactApplicationContext reactApplicationContext = CustomPushNotification.reactApplicationContext;
+
+ // Start MMKV container
+ MMKV.initialize(reactApplicationContext);
+ SecureKeystore secureKeystore = new SecureKeystore(reactApplicationContext);
+
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ String alias = Utils.toHex("com.MMKV.default");
+
+ // Retrieve container password
+ secureKeystore.getSecureKey(alias, new RNCallback() {
+ @Override
+ public void invoke(Object... args) {
+ String error = (String) args[0];
+ if (error == null) {
+ String password = (String) args[1];
+ mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
+ }
+ }
+ });
+ }
+
+ public String getAvatarUri() {
+ if (type == null) {
+ return null;
+ }
+ return serverURL() + "/avatar/" + this.sender.username + "?rc_token=" + token() + "&rc_uid=" + userId();
+ }
+
+ public String token() {
+ String userId = userId();
+ if (mmkv != null && userId != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(userId));
+ }
+ return "";
+ }
+
+ public String userId() {
+ String serverURL = serverURL();
+ if (mmkv != null && serverURL != null) {
+ return mmkv.decodeString(TOKEN_KEY.concat(serverURL));
+ }
+ return "";
+ }
+
+ public String privateKey() {
+ String serverURL = serverURL();
+ if (mmkv != null && serverURL != null) {
+ return mmkv.decodeString(serverURL.concat("-RC_E2E_PRIVATE_KEY"));
+ }
+ return null;
+ }
+
+ public String serverURL() {
+ String url = this.host;
+ if (url != null && url.endsWith("/")) {
+ url = url.substring(0, url.length() - 1);
+ }
+ return url;
+ }
+
+ public class Sender {
+ String username;
+ String _id;
+ }
+}
diff --git a/android/app/src/play/java/chat/rocket/reactnative/Encryption.java b/android/app/src/play/java/chat/rocket/reactnative/Encryption.java
new file mode 100644
index 000000000..83c2559cd
--- /dev/null
+++ b/android/app/src/play/java/chat/rocket/reactnative/Encryption.java
@@ -0,0 +1,210 @@
+package chat.rocket.reactnative;
+
+import android.util.Log;
+import android.util.Base64;
+import android.database.Cursor;
+
+import com.pedrouid.crypto.RSA;
+import com.pedrouid.crypto.RCTAes;
+import com.pedrouid.crypto.RCTRsaUtils;
+import com.pedrouid.crypto.Util;
+
+import com.google.gson.Gson;
+
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+
+import com.nozbe.watermelondb.Database;
+
+import java.util.Arrays;
+import java.security.SecureRandom;
+
+class Message {
+ String _id;
+ String userId;
+ String text;
+
+ Message(String id, String userId, String text) {
+ this._id = id;
+ this.userId = userId;
+ this.text = text;
+ }
+}
+
+class PrivateKey {
+ String d;
+ String dp;
+ String dq;
+ String e;
+ String n;
+ String p;
+ String q;
+ String qi;
+}
+
+class RoomKey {
+ String k;
+}
+
+class Room {
+ String e2eKey;
+ Boolean encrypted;
+
+ Room(String e2eKey, Boolean encrypted) {
+ this.e2eKey = e2eKey;
+ this.encrypted = encrypted;
+ }
+}
+
+class Encryption {
+ private Gson gson = new Gson();
+ private String E2ERoomKey;
+ private String keyId;
+
+ public static Encryption shared = new Encryption();
+ private ReactApplicationContext reactContext;
+
+ public Room readRoom(final Ejson ejson) {
+ Database database = new Database(ejson.serverURL().replace("https://", "") + "-experimental.db", reactContext);
+ String[] query = {ejson.rid};
+ Cursor cursor = database.rawQuery("select * from subscriptions where id == ? limit 1", query);
+
+ // Room not found
+ if (cursor.getCount() == 0) {
+ return null;
+ }
+
+ cursor.moveToFirst();
+ String e2eKey = cursor.getString(cursor.getColumnIndex("e2e_key"));
+ Boolean encrypted = cursor.getInt(cursor.getColumnIndex("encrypted")) > 0;
+ cursor.close();
+
+ return new Room(e2eKey, encrypted);
+ }
+
+ public String readUserKey(final Ejson ejson) throws Exception {
+ String privateKey = ejson.privateKey();
+ if (privateKey == null) {
+ return null;
+ }
+
+ PrivateKey privKey = gson.fromJson(privateKey, PrivateKey.class);
+
+ WritableMap jwk = Arguments.createMap();
+ jwk.putString("n", privKey.n);
+ jwk.putString("e", privKey.e);
+ jwk.putString("d", privKey.d);
+ jwk.putString("p", privKey.p);
+ jwk.putString("q", privKey.q);
+ jwk.putString("dp", privKey.dp);
+ jwk.putString("dq", privKey.dq);
+ jwk.putString("qi", privKey.qi);
+
+ return new RCTRsaUtils().jwkToPrivatePkcs1(jwk);
+ }
+
+ public String decryptRoomKey(final String e2eKey, final Ejson ejson) throws Exception {
+ String key = e2eKey.substring(12, e2eKey.length());
+ keyId = e2eKey.substring(0, 12);
+
+ String userKey = readUserKey(ejson);
+ if (userKey == null) {
+ return null;
+ }
+
+ RSA rsa = new RSA();
+ rsa.setPrivateKey(userKey);
+ String decrypted = rsa.decrypt(key);
+
+ RoomKey roomKey = gson.fromJson(decrypted, RoomKey.class);
+ byte[] decoded = Base64.decode(roomKey.k, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE);
+
+ return Util.bytesToHex(decoded);
+ }
+
+ public String decryptMessage(final Ejson ejson, final ReactApplicationContext reactContext) {
+ try {
+ this.reactContext = reactContext;
+
+ Room room = readRoom(ejson);
+ if (room == null || room.e2eKey == null) {
+ return null;
+ }
+ String e2eKey = decryptRoomKey(room.e2eKey, ejson);
+ if (e2eKey == null) {
+ return null;
+ }
+
+ String message = ejson.msg;
+ String msg = message.substring(12, message.length());
+ byte[] msgData = Base64.decode(msg, Base64.NO_WRAP);
+
+ String b64 = Base64.encodeToString(Arrays.copyOfRange(msgData, 16, msgData.length), Base64.DEFAULT);
+
+ String decrypted = RCTAes.decrypt(b64, e2eKey, Util.bytesToHex(Arrays.copyOfRange(msgData, 0, 16)));
+ byte[] data = Base64.decode(decrypted, Base64.NO_WRAP);
+ Message m = gson.fromJson(new String(data, "UTF-8"), Message.class);
+
+ return m.text;
+ } catch (Exception e) {
+ Log.d("[ROCKETCHAT][ENCRYPTION]", Log.getStackTraceString(e));
+ }
+
+ return null;
+ }
+
+ public String encryptMessage(final String message, final String id, final Ejson ejson) {
+ try {
+ Room room = readRoom(ejson);
+ if (room == null || !room.encrypted || room.e2eKey == null) {
+ return message;
+ }
+
+ String e2eKey = decryptRoomKey(room.e2eKey, ejson);
+ if (e2eKey == null) {
+ return message;
+ }
+
+ Message m = new Message(id, ejson.userId(), message);
+ String cypher = gson.toJson(m);
+
+ SecureRandom random = new SecureRandom();
+ byte[] bytes = new byte[16];
+ random.nextBytes(bytes);
+
+ String encrypted = RCTAes.encrypt(Base64.encodeToString(cypher.getBytes("UTF-8"), Base64.NO_WRAP), e2eKey, Util.bytesToHex(bytes));
+ byte[] data = Base64.decode(encrypted, Base64.NO_WRAP);
+
+ return keyId + Base64.encodeToString(concat(bytes, data), Base64.NO_WRAP);
+ } catch (Exception e) {
+ Log.d("[ROCKETCHAT][ENCRYPTION]", Log.getStackTraceString(e));
+ }
+
+ return message;
+ }
+
+ static byte[] concat(byte[]... arrays) {
+ // Determine the length of the result array
+ int totalLength = 0;
+ for (int i = 0; i < arrays.length; i++) {
+ totalLength += arrays[i].length;
+ }
+
+ // create the result array
+ byte[] result = new byte[totalLength];
+
+ // copy the source arrays into the result array
+ int currentIndex = 0;
+ for (int i = 0; i < arrays.length; i++) {
+ System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
+ currentIndex += arrays[i].length;
+ }
+
+ return result;
+ }
+}
diff --git a/android/app/src/play/java/chat/rocket/reactnative/LoadNotification.java b/android/app/src/play/java/chat/rocket/reactnative/LoadNotification.java
new file mode 100644
index 000000000..20d1313dc
--- /dev/null
+++ b/android/app/src/play/java/chat/rocket/reactnative/LoadNotification.java
@@ -0,0 +1,100 @@
+package chat.rocket.reactnative;
+
+import android.os.Bundle;
+import android.content.Context;
+
+import okhttp3.Call;
+import okhttp3.OkHttpClient;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.Interceptor;
+
+import com.google.gson.Gson;
+
+import java.io.IOException;
+
+import com.facebook.react.bridge.ReactApplicationContext;
+
+class JsonResponse {
+ Data data;
+
+ class Data {
+ Notification notification;
+
+ class Notification {
+ String notId;
+ String title;
+ String text;
+ Payload payload;
+
+ class Payload {
+ String host;
+ String rid;
+ String type;
+ Sender sender;
+ String messageId;
+ String notificationType;
+ String name;
+ String messageType;
+
+ class Sender {
+ String _id;
+ String username;
+ String name;
+ }
+ }
+ }
+ }
+}
+
+public class LoadNotification {
+ private static int RETRY_COUNT = 0;
+ private static int[] TIMEOUT = new int[]{0, 1, 3, 5, 10};
+ private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
+
+ public static void load(ReactApplicationContext reactApplicationContext, final Ejson ejson, Callback callback) {
+ final OkHttpClient client = new OkHttpClient();
+ HttpUrl.Builder url = HttpUrl.parse(ejson.serverURL().concat("/api/v1/push.get")).newBuilder();
+
+ Request request = new Request.Builder()
+ .header("x-user-id", ejson.userId())
+ .header("x-auth-token", ejson.token())
+ .url(url.addQueryParameter("id", ejson.messageId).build())
+ .build();
+
+ runRequest(client, request, callback);
+ }
+
+ private static void runRequest(OkHttpClient client, Request request, Callback callback) {
+ try {
+ Thread.sleep(TIMEOUT[RETRY_COUNT] * 1000);
+
+ Response response = client.newCall(request).execute();
+ String body = response.body().string();
+ if (!response.isSuccessful()) {
+ throw new Exception("Error");
+ }
+
+ Gson gson = new Gson();
+ JsonResponse json = gson.fromJson(body, JsonResponse.class);
+
+ Bundle bundle = new Bundle();
+ bundle.putString("notId", json.data.notification.notId);
+ bundle.putString("title", json.data.notification.title);
+ bundle.putString("message", json.data.notification.text);
+ bundle.putString("ejson", gson.toJson(json.data.notification.payload));
+ bundle.putBoolean("notificationLoaded", true);
+
+ callback.call(bundle);
+
+ } catch (Exception e) {
+ if (RETRY_COUNT <= TIMEOUT.length) {
+ RETRY_COUNT++;
+ runRequest(client, request, callback);
+ } else {
+ callback.call(null);
+ }
+ }
+ }
+}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/play/java/chat/rocket/reactnative/ReplyBroadcast.java
similarity index 89%
rename from android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
rename to android/app/src/play/java/chat/rocket/reactnative/ReplyBroadcast.java
index 81c62aeb0..b3e5c43e8 100644
--- a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
+++ b/android/app/src/play/java/chat/rocket/reactnative/ReplyBroadcast.java
@@ -11,10 +11,12 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
+
import java.io.IOException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+
import java.util.HashMap;
import java.util.Map;
@@ -25,7 +27,6 @@ import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
-import chat.rocket.userdefaults.RNUserDefaultsModule;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
public class ReplyBroadcast extends BroadcastReceiver {
@@ -65,7 +66,7 @@ public class ReplyBroadcast extends BroadcastReceiver {
final OkHttpClient client = new OkHttpClient();
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
- String json = buildMessage(rid, message.toString());
+ String json = buildMessage(rid, message.toString(), ejson);
CustomPushNotification.clearMessages(notId);
@@ -102,25 +103,32 @@ public class ReplyBroadcast extends BroadcastReceiver {
int count = 17;
StringBuilder builder = new StringBuilder();
while (count-- != 0) {
- int character = (int)(Math.random()*ALPHA_NUMERIC_STRING.length());
+ int character = (int) (Math.random() * ALPHA_NUMERIC_STRING.length());
builder.append(ALPHA_NUMERIC_STRING.charAt(character));
}
return builder.toString();
}
- protected String buildMessage(String rid, String message) {
+ protected String buildMessage(String rid, String message, Ejson ejson) {
Gson gsonBuilder = new GsonBuilder().create();
+ String id = getMessageId();
+
+ String msg = Encryption.shared.encryptMessage(message, id, ejson);
+
Map msgMap = new HashMap();
- msgMap.put("_id", getMessageId());
+ msgMap.put("_id", id);
msgMap.put("rid", rid);
- msgMap.put("msg", message);
+ msgMap.put("msg", msg);
+ if (msg != message) {
+ msgMap.put("t", "e2e");
+ }
msgMap.put("tmid", null);
- Map msg = new HashMap();
- msg.put("message", msgMap);
+ Map m = new HashMap();
+ m.put("message", msgMap);
- String json = gsonBuilder.toJson(msg);
+ String json = gsonBuilder.toJson(m);
return json;
}
diff --git a/android/build.gradle b/android/build.gradle
index 52f6ff0f0..13ea8c1ee 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -17,15 +17,18 @@ buildscript {
url 'https://maven.fabric.io/public'
}
}
- dependencies {
- classpath 'com.android.tools.build:gradle:3.5.3'
- classpath 'com.google.gms:google-services:4.2.0'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ def taskRequests = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase()
+ def isPlay = !taskRequests.contains("foss")
+
+ dependencies {
+ if (isPlay) {
+ classpath 'com.google.gms:google-services:4.2.0'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
+ classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
+ }
+ classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile
index e74a5b43f..d73d3381f 100644
--- a/android/fastlane/Fastfile
+++ b/android/fastlane/Fastfile
@@ -16,21 +16,26 @@
default_platform(:android)
platform :android do
- desc "Build App for development"
- lane :build do
- gradle(task: "assembleDebug")
+ desc "Play build for development"
+ lane :playBuild do
+ gradle(task: "assemblePlayDebug")
end
- desc "Build App for release"
- lane :release do
- gradle(task: "bundleRelease")
+ desc "Foss build for release"
+ lane :fossRelease do
+ gradle(task: "assembleFossRelease")
end
- desc "Upload App to Play store"
- lane :alpha do
+ desc "Play build for release"
+ lane :playRelease do
+ gradle(task: "bundlePlayRelease")
+ end
+
+ desc "Upload App to Play Store Internal"
+ lane :beta do
upload_to_play_store(
- track: 'alpha',
- aab: 'android/app/build/outputs/bundle/release/app-release.aab'
+ track: 'internal',
+ aab: 'android/app/build/outputs/bundle/playRelease/app-play-release.aab'
)
end
end
diff --git a/android/fastlane/README.md b/android/fastlane/README.md
index 3031222f9..32a0f12c2 100644
--- a/android/fastlane/README.md
+++ b/android/fastlane/README.md
@@ -16,19 +16,24 @@ or alternatively using `brew cask install fastlane`
# Available Actions
## Android
-### android build
+### android playBuild
```
-fastlane android build
+fastlane android playBuild
```
-Build App for development
-### android release
+Play build for development
+### android fossRelease
```
-fastlane android release
+fastlane android fossRelease
```
-Build App for release
-### android alpha
+Foss build for release
+### android playRelease
```
-fastlane android alpha
+fastlane android playRelease
+```
+Play build for release
+### android playAlpha
+```
+fastlane android playAlpha
```
Upload App to Play store
diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 3fec00201..4e5f9443e 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -53,6 +53,7 @@ export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPE
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
export const TOGGLE_CRASH_REPORT = 'TOGGLE_CRASH_REPORT';
+export const TOGGLE_ANALYTICS_EVENTS = 'TOGGLE_ANALYTICS_EVENTS';
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
export const SET_ACTIVE_USERS = 'SET_ACTIVE_USERS';
export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']);
@@ -66,3 +67,5 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
]);
export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']);
export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']);
+export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']);
+export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET_BANNER']);
diff --git a/app/actions/crashReport.js b/app/actions/crashReport.js
index 964f57cd2..8577b33e7 100644
--- a/app/actions/crashReport.js
+++ b/app/actions/crashReport.js
@@ -6,3 +6,10 @@ export function toggleCrashReport(value) {
payload: value
};
}
+
+export function toggleAnalyticsEvents(value) {
+ return {
+ type: types.TOGGLE_ANALYTICS_EVENTS,
+ payload: value
+ };
+}
diff --git a/app/actions/encryption.js b/app/actions/encryption.js
new file mode 100644
index 000000000..15eb7df20
--- /dev/null
+++ b/app/actions/encryption.js
@@ -0,0 +1,27 @@
+import * as types from './actionsTypes';
+
+export function encryptionInit() {
+ return {
+ type: types.ENCRYPTION.INIT
+ };
+}
+
+export function encryptionStop() {
+ return {
+ type: types.ENCRYPTION.STOP
+ };
+}
+
+export function encryptionSetBanner(banner) {
+ return {
+ type: types.ENCRYPTION.SET_BANNER,
+ banner
+ };
+}
+
+export function encryptionDecodeKey(password) {
+ return {
+ type: types.ENCRYPTION.DECODE_KEY,
+ password
+ };
+}
diff --git a/app/actions/enterpriseModules.js b/app/actions/enterpriseModules.js
new file mode 100644
index 000000000..74b097873
--- /dev/null
+++ b/app/actions/enterpriseModules.js
@@ -0,0 +1,14 @@
+import { ENTERPRISE_MODULES } from './actionsTypes';
+
+export function setEnterpriseModules(modules) {
+ return {
+ type: ENTERPRISE_MODULES.SET,
+ payload: modules
+ };
+}
+
+export function clearEnterpriseModules() {
+ return {
+ type: ENTERPRISE_MODULES.CLEAR
+ };
+}
diff --git a/app/actions/server.js b/app/actions/server.js
index 8a50fc4dd..01fb8c3e4 100644
--- a/app/actions/server.js
+++ b/app/actions/server.js
@@ -23,11 +23,13 @@ export function selectServerFailure() {
};
}
-export function serverRequest(server, certificate = null) {
+export function serverRequest(server, certificate = null, username = null, fromServerHistory = false) {
return {
type: SERVER.REQUEST,
server,
- certificate
+ certificate,
+ username,
+ fromServerHistory
};
}
diff --git a/app/constants/environment.js b/app/constants/environment.js
new file mode 100644
index 000000000..099d27d1c
--- /dev/null
+++ b/app/constants/environment.js
@@ -0,0 +1,3 @@
+import RNConfigReader from 'react-native-config-reader';
+
+export const isFDroidBuild = RNConfigReader.FDROID_BUILD;
diff --git a/app/constants/links.js b/app/constants/links.js
index f676ded9a..71b6cc07b 100644
--- a/app/constants/links.js
+++ b/app/constants/links.js
@@ -3,6 +3,7 @@ import { getBundleId, isIOS } from '../utils/deviceInfo';
const APP_STORE_ID = '1272915472';
export const PLAY_MARKET_LINK = `https://play.google.com/store/apps/details?id=${ getBundleId }`;
+export const FDROID_MARKET_LINK = 'https://f-droid.org/en/packages/chat.rocket.android';
export const APP_STORE_LINK = `https://itunes.apple.com/app/id${ APP_STORE_ID }`;
export const LICENSE_LINK = 'https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/LICENSE';
export const STORE_REVIEW_LINK = isIOS ? `itms-apps://itunes.apple.com/app/id${ APP_STORE_ID }?action=write-review` : `market://details?id=${ getBundleId }`;
diff --git a/app/constants/settings.js b/app/constants/settings.js
index 4c2030c0c..ccb1803e2 100644
--- a/app/constants/settings.js
+++ b/app/constants/settings.js
@@ -68,6 +68,9 @@ export default {
DirectMesssage_maxUsers: {
type: 'valueAsNumber'
},
+ E2E_Enable: {
+ type: 'valueAsBoolean'
+ },
Accounts_Directory_DefaultView: {
type: 'valueAsString'
},
@@ -181,5 +184,8 @@ export default {
},
Force_Screen_Lock_After: {
type: 'valueAsNumber'
+ },
+ Allow_Save_Media_to_Gallery: {
+ type: 'valueAsBoolean'
}
};
diff --git a/app/constants/userDefaults.js b/app/constants/userDefaults.js
deleted file mode 100644
index e21b7b852..000000000
--- a/app/constants/userDefaults.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export const SERVERS = 'kServers';
-export const TOKEN = 'kAuthToken';
-export const USER_ID = 'kUserId';
-export const SERVER_URL = 'kAuthServerURL';
-export const SERVER_NAME = 'kServerName';
-export const SERVER_ICON = 'kServerIconURL';
diff --git a/app/containers/FormContainer.js b/app/containers/FormContainer.js
index 19b5c189f..46a550546 100644
--- a/app/containers/FormContainer.js
+++ b/app/containers/FormContainer.js
@@ -23,14 +23,21 @@ export const FormContainerInner = ({ children }) => (
);
-const FormContainer = ({ children, theme, testID }) => (
+const FormContainer = ({
+ children, theme, testID, ...props
+}) => (
-
+
{children}
diff --git a/app/containers/HeaderButton.js b/app/containers/HeaderButton.js
index cd1808d2f..4dc72f8cc 100644
--- a/app/containers/HeaderButton.js
+++ b/app/containers/HeaderButton.js
@@ -61,6 +61,12 @@ export const SaveButton = React.memo(({ onPress, testID, ...props }) => (
));
+export const PreferencesButton = React.memo(({ onPress, testID, ...props }) => (
+
+
+
+));
+
export const LegalButton = React.memo(({ navigation, testID }) => (
navigation.navigate('LegalView')} testID={testID} />
));
@@ -89,6 +95,10 @@ SaveButton.propTypes = {
onPress: PropTypes.func.isRequired,
testID: PropTypes.string.isRequired
};
+PreferencesButton.propTypes = {
+ onPress: PropTypes.func.isRequired,
+ testID: PropTypes.string.isRequired
+};
LegalButton.propTypes = {
navigation: PropTypes.object.isRequired,
testID: PropTypes.string.isRequired
diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js
index 417e6523a..4ae647b5c 100644
--- a/app/containers/LoginServices.js
+++ b/app/containers/LoginServices.js
@@ -1,6 +1,6 @@
import React from 'react';
import {
- View, StyleSheet, Text, Animated, Easing, Image
+ View, StyleSheet, Text, Animated, Easing
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
@@ -17,6 +17,7 @@ import I18n from '../i18n';
import random from '../utils/random';
import { logEvent, events } from '../utils/log';
import RocketChat from '../lib/rocketchat';
+import { CustomIcon } from '../lib/Icons';
const BUTTON_HEIGHT = 48;
const SERVICE_HEIGHT = 58;
@@ -296,7 +297,7 @@ class LoginServices extends React.PureComponent {
const { CAS_enabled, theme } = this.props;
let { name } = service;
name = name === 'meteor-developer' ? 'meteor' : name;
- const icon = `icon_${ name }`;
+ const icon = `${ name }-monochromatic`;
const isSaml = service.service === 'saml';
let onPress = () => {};
@@ -325,18 +326,6 @@ class LoginServices extends React.PureComponent {
break;
}
- if (name === 'apple') {
- return (
-
- );
- }
-
name = name.charAt(0).toUpperCase() + name.slice(1);
let buttonText;
if (isSaml || (service.service === 'cas' && CAS_enabled)) {
@@ -361,7 +350,7 @@ class LoginServices extends React.PureComponent {
underlayColor={themes[theme].buttonText}
>
- {service.authType === 'oauth' ? : null}
+ {service.authType === 'oauth' || service.authType === 'apple' ? : null}
{buttonText}
diff --git a/app/containers/MessageActions/index.js b/app/containers/MessageActions/index.js
index ff4c4dbf2..0ce32bb71 100644
--- a/app/containers/MessageActions/index.js
+++ b/app/containers/MessageActions/index.js
@@ -268,7 +268,7 @@ const MessageActions = React.memo(forwardRef(({
const handleDelete = (message) => {
showConfirmationAlert({
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
- callToAction: I18n.t('Delete'),
+ confirmationText: I18n.t('Delete'),
onPress: async() => {
try {
logEvent(events.ROOM_MSG_ACTION_DELETE);
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index cf1396240..f5c16bf88 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -46,6 +46,7 @@ import CommandsPreview from './CommandsPreview';
import { getUserSelector } from '../../selectors/login';
import Navigation from '../../lib/Navigation';
import { withActionSheet } from '../ActionSheet';
+import { sanitizeLikeString } from '../../lib/database/utils';
const imagePickerConfig = {
cropping: true,
@@ -129,7 +130,7 @@ class MessageBox extends Component {
this.options = [
{
title: I18n.t('Take_a_photo'),
- icon: 'image',
+ icon: 'camera-photo',
onPress: this.takePhoto
},
{
@@ -139,7 +140,7 @@ class MessageBox extends Component {
},
{
title: I18n.t('Choose_from_library'),
- icon: 'share',
+ icon: 'image',
onPress: this.chooseFromLibrary
},
{
@@ -491,8 +492,9 @@ class MessageBox extends Component {
const db = database.active;
if (keyword) {
const customEmojisCollection = db.collections.get('custom_emojis');
+ const likeString = sanitizeLikeString(keyword);
let customEmojis = await customEmojisCollection.query(
- Q.where('name', Q.like(`${ Q.sanitizeLikeString(keyword) }%`))
+ Q.where('name', Q.like(`${ likeString }%`))
).fetch();
customEmojis = customEmojis.slice(0, MENTIONS_COUNT_TO_DISPLAY);
const filteredEmojis = emojis.filter(emoji => emoji.indexOf(keyword) !== -1).slice(0, MENTIONS_COUNT_TO_DISPLAY);
@@ -504,8 +506,9 @@ class MessageBox extends Component {
getSlashCommands = debounce(async(keyword) => {
const db = database.active;
const commandsCollection = db.collections.get('slash_commands');
+ const likeString = sanitizeLikeString(keyword);
const commands = await commandsCollection.query(
- Q.where('id', Q.like(`${ Q.sanitizeLikeString(keyword) }%`))
+ Q.where('id', Q.like(`${ likeString }%`))
).fetch();
this.setState({ mentions: commands || [] });
}, 300)
@@ -734,8 +737,9 @@ class MessageBox extends Component {
const db = database.active;
const commandsCollection = db.collections.get('slash_commands');
const command = message.replace(/ .*/, '').slice(1);
+ const likeString = sanitizeLikeString(command);
const slashCommand = await commandsCollection.query(
- Q.where('id', Q.like(`${ Q.sanitizeLikeString(command) }%`))
+ Q.where('id', Q.like(`${ likeString }%`))
).fetch();
if (slashCommand.length > 0) {
logEvent(events.COMMAND_RUN);
diff --git a/app/containers/Passcode/PasscodeEnter.js b/app/containers/Passcode/PasscodeEnter.js
index 3721b9fee..ebd22f363 100644
--- a/app/containers/Passcode/PasscodeEnter.js
+++ b/app/containers/Passcode/PasscodeEnter.js
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import { useAsyncStorage } from '@react-native-community/async-storage';
-import RNUserDefaults from 'rn-user-defaults';
import PropTypes from 'prop-types';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
@@ -14,6 +13,7 @@ import {
} from '../../constants/localAuthentication';
import { resetAttempts, biometryAuth } from '../../utils/localAuthentication';
import { getLockedUntil, getDiff } from './utils';
+import UserPreferences from '../../lib/userPreferences';
import I18n from '../../i18n';
const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
@@ -26,7 +26,7 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }) => {
const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const fetchPasscode = async() => {
- const p = await RNUserDefaults.get(PASSCODE_KEY);
+ const p = await UserPreferences.getStringAsync(PASSCODE_KEY);
setPasscode(p);
};
diff --git a/app/containers/message/Content.js b/app/containers/message/Content.js
index 1d2f3012b..563c9ee98 100644
--- a/app/containers/message/Content.js
+++ b/app/containers/message/Content.js
@@ -9,6 +9,8 @@ import Markdown from '../markdown';
import { getInfoMessage } from './utils';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
+import Encrypted from './Encrypted';
+import { E2E_MESSAGE_TYPE } from '../../lib/encryption/constants';
const Content = React.memo((props) => {
if (props.isInfo) {
@@ -22,10 +24,13 @@ const Content = React.memo((props) => {
);
}
+ const isPreview = props.tmid && !props.isThreadRoom;
let content = null;
if (props.tmid && !props.msg) {
content = {I18n.t('Sent_an_attachment')};
+ } else if (props.isEncrypted) {
+ content = {I18n.t('Encrypted_message')};
} else {
const { baseUrl, user } = useContext(MessageContext);
content = (
@@ -35,8 +40,8 @@ const Content = React.memo((props) => {
getCustomEmoji={props.getCustomEmoji}
username={user.username}
isEdited={props.isEdited}
- numberOfLines={(props.tmid && !props.isThreadRoom) ? 1 : 0}
- preview={props.tmid && !props.isThreadRoom}
+ numberOfLines={isPreview ? 1 : 0}
+ preview={isPreview}
channels={props.channels}
mentions={props.mentions}
navToRoomInfo={props.navToRoomInfo}
@@ -47,6 +52,21 @@ const Content = React.memo((props) => {
);
}
+ // If this is a encrypted message and is not a preview
+ if (props.type === E2E_MESSAGE_TYPE && !isPreview) {
+ content = (
+
+
+ {content}
+
+
+
+ );
+ }
+
return (
{content}
@@ -59,9 +79,15 @@ const Content = React.memo((props) => {
if (prevProps.msg !== nextProps.msg) {
return false;
}
+ if (prevProps.type !== nextProps.type) {
+ return false;
+ }
if (prevProps.theme !== nextProps.theme) {
return false;
}
+ if (prevProps.isEncrypted !== nextProps.isEncrypted) {
+ return false;
+ }
if (!equal(prevProps.mentions, nextProps.mentions)) {
return false;
}
@@ -79,11 +105,13 @@ Content.propTypes = {
msg: PropTypes.string,
theme: PropTypes.string,
isEdited: PropTypes.bool,
+ isEncrypted: PropTypes.bool,
getCustomEmoji: PropTypes.func,
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
navToRoomInfo: PropTypes.func,
- useRealName: PropTypes.bool
+ useRealName: PropTypes.bool,
+ type: PropTypes.string
};
Content.displayName = 'MessageContent';
diff --git a/app/containers/message/Encrypted.js b/app/containers/message/Encrypted.js
new file mode 100644
index 000000000..38d3bf16b
--- /dev/null
+++ b/app/containers/message/Encrypted.js
@@ -0,0 +1,29 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+
+import Touchable from './Touchable';
+import { E2E_MESSAGE_TYPE } from '../../lib/encryption/constants';
+import { CustomIcon } from '../../lib/Icons';
+import { themes } from '../../constants/colors';
+import { BUTTON_HIT_SLOP } from './utils';
+import MessageContext from './Context';
+import styles from './styles';
+
+const Encrypted = React.memo(({ type, theme }) => {
+ if (type !== E2E_MESSAGE_TYPE) {
+ return null;
+ }
+
+ const { onEncryptedPress } = useContext(MessageContext);
+ return (
+
+
+
+ );
+});
+Encrypted.propTypes = {
+ type: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default Encrypted;
diff --git a/app/containers/message/RepliedThread.js b/app/containers/message/RepliedThread.js
index e4e06f038..7d02d7494 100644
--- a/app/containers/message/RepliedThread.js
+++ b/app/containers/message/RepliedThread.js
@@ -8,9 +8,10 @@ import { CustomIcon } from '../../lib/Icons';
import DisclosureIndicator from '../DisclosureIndicator';
import styles from './styles';
import { themes } from '../../constants/colors';
+import I18n from '../../i18n';
const RepliedThread = React.memo(({
- tmid, tmsg, isHeader, fetchThreadName, id, theme
+ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme
}) => {
if (!tmid || !isHeader) {
return null;
@@ -24,6 +25,10 @@ const RepliedThread = React.memo(({
let msg = shortnameToUnicode(tmsg);
msg = removeMarkdown(msg);
+ if (isEncrypted) {
+ msg = I18n.t('Encrypted_message');
+ }
+
return (
@@ -38,6 +43,9 @@ const RepliedThread = React.memo(({
if (prevProps.tmsg !== nextProps.tmsg) {
return false;
}
+ if (prevProps.isEncrypted !== nextProps.isEncrypted) {
+ return false;
+ }
if (prevProps.isHeader !== nextProps.isHeader) {
return false;
}
@@ -53,7 +61,8 @@ RepliedThread.propTypes = {
id: PropTypes.string,
isHeader: PropTypes.bool,
theme: PropTypes.string,
- fetchThreadName: PropTypes.func
+ fetchThreadName: PropTypes.func,
+ isEncrypted: PropTypes.bool
};
RepliedThread.displayName = 'MessageRepliedThread';
diff --git a/app/containers/message/index.js b/app/containers/message/index.js
index 207e5e70c..cd1ee0859 100644
--- a/app/containers/message/index.js
+++ b/app/containers/message/index.js
@@ -8,6 +8,7 @@ import debounce from '../../utils/debounce';
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
import messagesStatus from '../../constants/messagesStatus';
import { withTheme } from '../../theme';
+import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
class MessageContainer extends React.Component {
static propTypes = {
@@ -35,6 +36,7 @@ class MessageContainer extends React.Component {
getCustomEmoji: PropTypes.func,
onLongPress: PropTypes.func,
onReactionPress: PropTypes.func,
+ onEncryptedPress: PropTypes.func,
onDiscussionPress: PropTypes.func,
onThreadPress: PropTypes.func,
errorActionsShow: PropTypes.func,
@@ -53,6 +55,7 @@ class MessageContainer extends React.Component {
getCustomEmoji: () => {},
onLongPress: () => {},
onReactionPress: () => {},
+ onEncryptedPress: () => {},
onDiscussionPress: () => {},
onThreadPress: () => {},
errorActionsShow: () => {},
@@ -104,7 +107,7 @@ class MessageContainer extends React.Component {
onLongPress = () => {
const { archived, onLongPress, item } = this.props;
- if (this.isInfo || this.hasError || archived) {
+ if (this.isInfo || this.hasError || this.isEncrypted || archived) {
return;
}
if (onLongPress) {
@@ -133,6 +136,13 @@ class MessageContainer extends React.Component {
}
}
+ onEncryptedPress = () => {
+ const { onEncryptedPress } = this.props;
+ if (onEncryptedPress) {
+ onEncryptedPress();
+ }
+ }
+
onDiscussionPress = () => {
const { onDiscussionPress, item } = this.props;
if (onDiscussionPress) {
@@ -196,6 +206,12 @@ class MessageContainer extends React.Component {
return false;
}
+ get isEncrypted() {
+ const { item } = this.props;
+ const { t, e2e } = item;
+ return t === E2E_MESSAGE_TYPE && e2e !== E2E_STATUS.DONE;
+ }
+
get isInfo() {
const { item } = this.props;
return SYSTEM_MESSAGES.includes(item.t);
@@ -251,6 +267,7 @@ class MessageContainer extends React.Component {
onErrorPress: this.onErrorPress,
replyBroadcast: this.replyBroadcast,
onReactionPress: this.onReactionPress,
+ onEncryptedPress: this.onEncryptedPress,
onDiscussionPress: this.onDiscussionPress,
onReactionLongPress: this.onReactionLongPress
}}
@@ -295,6 +312,7 @@ class MessageContainer extends React.Component {
isThreadRoom={isThreadRoom}
isInfo={this.isInfo}
isTemp={this.isTemp}
+ isEncrypted={this.isEncrypted}
hasError={this.hasError}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}
diff --git a/app/containers/message/styles.js b/app/containers/message/styles.js
index 4ee6af08a..f83c64f0c 100644
--- a/app/containers/message/styles.js
+++ b/app/containers/message/styles.js
@@ -13,6 +13,9 @@ export default StyleSheet.create({
paddingHorizontal: 14,
flexDirection: 'column'
},
+ contentContainer: {
+ flex: 1
+ },
messageContent: {
flex: 1,
marginLeft: 46
@@ -163,5 +166,8 @@ export default StyleSheet.create({
},
readReceipt: {
lineHeight: 20
+ },
+ encrypted: {
+ justifyContent: 'center'
}
});
diff --git a/app/ee/LICENSE b/app/ee/LICENSE
new file mode 100644
index 000000000..38288b74c
--- /dev/null
+++ b/app/ee/LICENSE
@@ -0,0 +1,43 @@
+The Rocket.Chat Enterprise Edition (EE) license (the "EE License")
+Copyright (c) 2015-2020 Rocket.Chat Technologies Corp.
+
+With regard to the Rocket.Chat Software:
+
+This software and associated documentation files (the "Software") may only be
+used in production, if you (and any entity that you represent) have agreed to,
+and are in compliance with, the Rocket.Chat Subscription Terms of Service,
+available at https://rocket.chat/terms (the "EE Terms"), or other agreement
+governing the use of the Software, as agreed by you and Rocket.Chat, and otherwise
+have a valid Rocket.Chat Enterprise Edition subscription for the correct number
+of user seats. Subject to the foregoing sentence, you are free to modify this
+Software and publish patches to the Software. You agree that Rocket.Chat and/or
+its licensors (as applicable) retain all right, title and interest in and to all
+such modifications and/or patches, and all such modifications and/or patches may
+only be used, copied, modified, displayed, distributed, or otherwise exploited
+with a valid Rocket.Chat Enterprise Edition subscription for the correct number
+of user seats. Notwithstanding the foregoing, you may copy and modify the Software
+for development and testing purposes, without requiring a Subscription. You agree
+that Rocket.Chat and/or its licensors (as applicable) retain all right, title and
+interest in and to all such modifications. You are not granted any other rights
+beyond what is expressly stated herein. Subject to the foregoing, it is forbidden
+to copy, merge, publish, distribute, sublicense, and/or sell the Software.
+
+This EE License applies only to the part of this Software that is not distributed
+as part of Rocket.Chat Community Edition (CE). Any part of this Software distributed
+as part of Rocket.Chat CE or is served client-side as an image, font, cascading
+stylesheet (CSS), file which produces or is compiled, arranged, augmented, or combined
+into client-side JavaScript, in whole or in part, is copyrighted under the MIT Expat
+license. The full text of this EE License shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+For all third-party components incorporated into the Rocket.Chat Software,
+those components are licensed under the original license provided by the owner
+of the applicable component.
\ No newline at end of file
diff --git a/app/ee/README.md b/app/ee/README.md
new file mode 100644
index 000000000..568825f93
--- /dev/null
+++ b/app/ee/README.md
@@ -0,0 +1,4 @@
+### How to remove ee folder
+
+If you're willing to remove Enterprise Edition features from your app, follow this diff:
+https://github.com/RocketChat/Rocket.Chat.ReactNative/compare/develop...no-ee?expand=1
\ No newline at end of file
diff --git a/app/actions/inquiry.js b/app/ee/omnichannel/actions/inquiry.js
similarity index 93%
rename from app/actions/inquiry.js
rename to app/ee/omnichannel/actions/inquiry.js
index 0f3402119..ef3e88c13 100644
--- a/app/actions/inquiry.js
+++ b/app/ee/omnichannel/actions/inquiry.js
@@ -1,4 +1,4 @@
-import * as types from './actionsTypes';
+import * as types from '../../../actions/actionsTypes';
export function inquirySetEnabled(enabled) {
return {
diff --git a/app/ee/omnichannel/containers/OmnichannelStatus.js b/app/ee/omnichannel/containers/OmnichannelStatus.js
new file mode 100644
index 000000000..b0763d76e
--- /dev/null
+++ b/app/ee/omnichannel/containers/OmnichannelStatus.js
@@ -0,0 +1,84 @@
+import React, { memo, useState, useEffect } from 'react';
+import {
+ View, Text, StyleSheet, Switch
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import Touch from '../../../utils/touch';
+import { CustomIcon } from '../../../lib/Icons';
+import I18n from '../../../i18n';
+import styles from '../../../views/RoomsListView/styles';
+import { themes, SWITCH_TRACK_COLOR } from '../../../constants/colors';
+import { withTheme } from '../../../theme';
+import UnreadBadge from '../../../presentation/UnreadBadge';
+import RocketChat from '../../../lib/rocketchat';
+import { isOmnichannelStatusAvailable, changeLivechatStatus } from '../lib';
+
+const OmnichannelStatus = memo(({
+ searching, goQueue, theme, queueSize, inquiryEnabled, user
+}) => {
+ if (searching > 0 || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
+ return null;
+ }
+ const [status, setStatus] = useState(isOmnichannelStatusAvailable(user));
+
+ useEffect(() => {
+ setStatus(isOmnichannelStatusAvailable(user));
+ }, [user.statusLivechat]);
+
+ const toggleLivechat = async() => {
+ try {
+ setStatus(v => !v);
+ await changeLivechatStatus();
+ } catch {
+ setStatus(v => !v);
+ }
+ };
+
+ return (
+
+
+
+ {I18n.t('Omnichannel')}
+ {inquiryEnabled
+ ? (
+
+ )
+ : null}
+
+
+
+ );
+});
+
+OmnichannelStatus.propTypes = {
+ searching: PropTypes.bool,
+ goQueue: PropTypes.func,
+ queueSize: PropTypes.number,
+ inquiryEnabled: PropTypes.bool,
+ theme: PropTypes.string,
+ user: PropTypes.shape({
+ roles: PropTypes.array,
+ statusLivechat: PropTypes.string
+ })
+};
+
+export default withTheme(OmnichannelStatus);
diff --git a/app/ee/omnichannel/lib/index.js b/app/ee/omnichannel/lib/index.js
new file mode 100644
index 000000000..7492675eb
--- /dev/null
+++ b/app/ee/omnichannel/lib/index.js
@@ -0,0 +1,40 @@
+import subscribeInquiry from './subscriptions/inquiry';
+import RocketChat from '../../../lib/rocketchat';
+import EventEmitter from '../../../utils/events';
+
+export const isOmnichannelStatusAvailable = user => user?.statusLivechat === 'available';
+
+// RC 0.26.0
+export const changeLivechatStatus = () => RocketChat.methodCallWrapper('livechat:changeLivechatStatus');
+
+// RC 2.4.0
+export const getInquiriesQueued = () => RocketChat.sdk.get('livechat/inquiries.queued');
+
+// this inquiry is added to the db by the subscriptions stream
+// and will be removed by the queue stream
+// RC 2.4.0
+export const takeInquiry = inquiryId => RocketChat.methodCallWrapper('livechat:takeInquiry', inquiryId);
+
+class Omnichannel {
+ constructor() {
+ this.inquirySub = null;
+ EventEmitter.addEventListener('INQUIRY_SUBSCRIBE', this.subscribeInquiry);
+ EventEmitter.addEventListener('INQUIRY_UNSUBSCRIBE', this.unsubscribeInquiry);
+ }
+
+ subscribeInquiry = async() => {
+ console.log('[RCRN] Subscribing to inquiry');
+ this.inquirySub = await subscribeInquiry();
+ }
+
+ unsubscribeInquiry = () => {
+ if (this.inquirySub) {
+ console.log('[RCRN] Unsubscribing from inquiry');
+ this.inquirySub.stop();
+ this.inquirySub = null;
+ }
+ }
+}
+
+// eslint-disable-next-line no-unused-vars
+const omnichannel = new Omnichannel();
diff --git a/app/lib/methods/subscriptions/inquiry.js b/app/ee/omnichannel/lib/subscriptions/inquiry.js
similarity index 77%
rename from app/lib/methods/subscriptions/inquiry.js
rename to app/ee/omnichannel/lib/subscriptions/inquiry.js
index 014335dc1..02eafb8a9 100644
--- a/app/lib/methods/subscriptions/inquiry.js
+++ b/app/ee/omnichannel/lib/subscriptions/inquiry.js
@@ -1,12 +1,12 @@
-import log from '../../../utils/log';
-import store from '../../createStore';
-import RocketChat from '../../rocketchat';
+import log from '../../../../utils/log';
+import store from '../../../../lib/createStore';
+import RocketChat from '../../../../lib/rocketchat';
import {
inquiryRequest,
inquiryQueueAdd,
inquiryQueueUpdate,
inquiryQueueRemove
-} from '../../../actions/inquiry';
+} from '../../actions/inquiry';
const removeListener = listener => listener.stop();
@@ -63,9 +63,9 @@ export default function subscribeInquiry() {
}
};
- connectedListener = this.sdk.onStreamData('connected', handleConnection);
- disconnectedListener = this.sdk.onStreamData('close', handleConnection);
- queueListener = this.sdk.onStreamData(streamTopic, handleQueueMessageReceived);
+ connectedListener = RocketChat.onStreamData('connected', handleConnection);
+ disconnectedListener = RocketChat.onStreamData('close', handleConnection);
+ queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
try {
const { user } = store.getState().login;
@@ -74,13 +74,13 @@ export default function subscribeInquiry() {
const { departments } = result;
if (!departments.length || RocketChat.hasRole('livechat-manager')) {
- this.sdk.subscribe(streamTopic, 'public').catch(e => console.log(e));
+ RocketChat.subscribe(streamTopic, 'public').catch(e => console.log(e));
}
const departmentIds = departments.map(({ departmentId }) => departmentId);
departmentIds.forEach((departmentId) => {
// subscribe to all departments of the agent
- this.sdk.subscribe(streamTopic, `department/${ departmentId }`).catch(e => console.log(e));
+ RocketChat.subscribe(streamTopic, `department/${ departmentId }`).catch(e => console.log(e));
});
}
});
diff --git a/app/reducers/inquiry.js b/app/ee/omnichannel/reducers/inquiry.js
similarity index 94%
rename from app/reducers/inquiry.js
rename to app/ee/omnichannel/reducers/inquiry.js
index 230280c83..156c9633d 100644
--- a/app/reducers/inquiry.js
+++ b/app/ee/omnichannel/reducers/inquiry.js
@@ -1,4 +1,4 @@
-import { INQUIRY } from '../actions/actionsTypes';
+import { INQUIRY } from '../../../actions/actionsTypes';
const initialState = {
enabled: false,
diff --git a/app/sagas/inquiry.js b/app/ee/omnichannel/sagas/inquiry.js
similarity index 65%
rename from app/sagas/inquiry.js
rename to app/ee/omnichannel/sagas/inquiry.js
index 9ee7e27fe..6140e2f65 100644
--- a/app/sagas/inquiry.js
+++ b/app/ee/omnichannel/sagas/inquiry.js
@@ -1,24 +1,26 @@
import { put, takeLatest, select } from 'redux-saga/effects';
-import * as types from '../actions/actionsTypes';
-import RocketChat from '../lib/rocketchat';
+import * as types from '../../../actions/actionsTypes';
+import RocketChat from '../../../lib/rocketchat';
+import EventEmitter from '../../../utils/events';
import { inquirySuccess, inquiryFailure, inquirySetEnabled } from '../actions/inquiry';
+import { isOmnichannelStatusAvailable, getInquiriesQueued } from '../lib';
const handleRequest = function* handleRequest() {
try {
const routingConfig = yield RocketChat.getRoutingConfig();
- const statusLivechat = yield select(state => state.login.user.statusLivechat);
+ const user = yield select(state => state.login.user);
// if routingConfig showQueue is enabled and omnichannel is enabled
- const showQueue = routingConfig.showQueue && statusLivechat === 'available';
+ const showQueue = routingConfig.showQueue && isOmnichannelStatusAvailable(user);
if (showQueue) {
// get all the current chats on the queue
- const result = yield RocketChat.getInquiriesQueued();
+ const result = yield getInquiriesQueued();
if (result.success) {
const { inquiries } = result;
// subscribe to inquiry queue changes
- RocketChat.subscribeInquiry();
+ EventEmitter.emit('INQUIRY_SUBSCRIBE');
// put request result on redux state
yield put(inquirySuccess(inquiries));
diff --git a/app/selectors/inquiry.js b/app/ee/omnichannel/selectors/inquiry.js
similarity index 100%
rename from app/selectors/inquiry.js
rename to app/ee/omnichannel/selectors/inquiry.js
diff --git a/app/views/QueueListView.js b/app/ee/omnichannel/views/QueueListView.js
similarity index 82%
rename from app/views/QueueListView.js
rename to app/ee/omnichannel/views/QueueListView.js
index 301f09a0f..67ee16009 100644
--- a/app/views/QueueListView.js
+++ b/app/ee/omnichannel/views/QueueListView.js
@@ -4,20 +4,20 @@ import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import isEqual from 'react-fast-compare';
-import I18n from '../i18n';
-import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem';
-import { MAX_SIDEBAR_WIDTH } from '../constants/tablet';
-import { isTablet, isIOS } from '../utils/deviceInfo';
-import { getUserSelector } from '../selectors/login';
-import { withTheme } from '../theme';
-import { withDimensions } from '../dimensions';
-import SafeAreaView from '../containers/SafeAreaView';
-import { themes } from '../constants/colors';
-import StatusBar from '../containers/StatusBar';
-import { goRoom } from '../utils/goRoom';
-import { CloseModalButton } from '../containers/HeaderButton';
-import RocketChat from '../lib/rocketchat';
-import { logEvent, events } from '../utils/log';
+import I18n from '../../../i18n';
+import RoomItem, { ROW_HEIGHT } from '../../../presentation/RoomItem';
+import { MAX_SIDEBAR_WIDTH } from '../../../constants/tablet';
+import { isTablet, isIOS } from '../../../utils/deviceInfo';
+import { getUserSelector } from '../../../selectors/login';
+import { withTheme } from '../../../theme';
+import { withDimensions } from '../../../dimensions';
+import SafeAreaView from '../../../containers/SafeAreaView';
+import { themes } from '../../../constants/colors';
+import StatusBar from '../../../containers/StatusBar';
+import { goRoom } from '../../../utils/goRoom';
+import { CloseModalButton } from '../../../containers/HeaderButton';
+import RocketChat from '../../../lib/rocketchat';
+import { logEvent, events } from '../../../utils/log';
import { getInquiryQueueSelector } from '../selectors/inquiry';
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
diff --git a/app/i18n/index.js b/app/i18n/index.js
index f542614b6..ff3753514 100644
--- a/app/i18n/index.js
+++ b/app/i18n/index.js
@@ -2,31 +2,64 @@ import i18n from 'i18n-js';
import { I18nManager } from 'react-native';
import * as RNLocalize from 'react-native-localize';
-import en from './locales/en';
-import ru from './locales/ru';
-import fr from './locales/fr';
-import de from './locales/de';
-import nl from './locales/nl';
-import ptBR from './locales/pt-BR';
-import zhCN from './locales/zh-CN';
-import ptPT from './locales/pt-PT';
-import esES from './locales/es-ES';
-import it from './locales/it';
-import ja from './locales/ja';
+export const LANGUAGES = [
+ {
+ label: 'English',
+ value: 'en',
+ file: require('./locales/en').default
+ }, {
+ label: '简体中文',
+ value: 'zh-CN',
+ file: require('./locales/zh-CN').default
+ }, {
+ label: '繁體中文',
+ value: 'zh-TW',
+ file: require('./locales/zh-TW').default
+ }, {
+ label: 'Deutsch',
+ value: 'de',
+ file: require('./locales/de').default
+ }, {
+ label: 'Español (ES)',
+ value: 'es-ES',
+ file: require('./locales/es-ES').default
+ }, {
+ label: 'Français',
+ value: 'fr',
+ file: require('./locales/fr').default
+ }, {
+ label: 'Português (BR)',
+ value: 'pt-BR',
+ file: require('./locales/pt-BR').default
+ }, {
+ label: 'Português (PT)',
+ value: 'pt-PT',
+ file: require('./locales/pt-PT').default
+ }, {
+ label: 'Russian',
+ value: 'ru',
+ file: require('./locales/ru').default
+ }, {
+ label: 'Nederlands',
+ value: 'nl',
+ file: require('./locales/nl').default
+ }, {
+ label: 'Italiano',
+ value: 'it',
+ file: require('./locales/it').default
+ }, {
+ label: '日本語',
+ value: 'ja',
+ file: require('./locales/ja').default
+ }
+];
-i18n.translations = {
- en,
- ru,
- 'pt-BR': ptBR,
- 'zh-CN': zhCN,
- fr,
- de,
- 'pt-PT': ptPT,
- 'es-ES': esES,
- nl,
- it,
- ja
-};
+const translations = LANGUAGES.reduce((ret, item) => {
+ ret[item.value] = item.file;
+ return ret;
+}, {});
+
+i18n.translations = translations;
i18n.fallbacks = true;
const defaultLanguage = { languageTag: 'en', isRTL: false };
diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js
index 0d0188379..cce1fc069 100644
--- a/app/i18n/locales/de.js
+++ b/app/i18n/locales/de.js
@@ -487,7 +487,6 @@ export default {
Tap_to_view_servers_list: 'Hier tippen, um die Serverliste anzuzeigen',
Terms_of_Service: ' Nutzungsbedingungen',
Theme: 'Erscheinungsbild',
- The_URL_is_invalid: 'Die eingegebene URL ist ungültig. Überprüfe sie bitte noch einmal und versuche es erneut!',
The_user_wont_be_able_to_type_in_roomName: 'Dem Nutzer wird es nicht möglich sein in {{roomName}} zu schreiben',
The_user_will_be_able_to_type_in_roomName: 'Der Nutzer wird in {{roomName}} schreiben können',
There_was_an_error_while_action: 'Während {{action}} ist ein Fehler aufgetreten!',
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index 72ebbdb4e..18f5f22d4 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -131,6 +131,10 @@ export default {
Channels: 'Channels',
Chats: 'Chats',
Call_already_ended: 'Call already ended!',
+ Clear_cookies_alert: 'Do you want to clear all cookies?',
+ Clear_cookies_desc: 'This action will clear all login cookies, allowing you to login into other accounts.',
+ Clear_cookies_yes: 'Yes, clear cookies',
+ Clear_cookies_no: 'No, keep cookies',
Click_to_join: 'Click to Join!',
Close: 'Close',
Close_emoji_selector: 'Close emoji selector',
@@ -181,6 +185,8 @@ export default {
description: 'description',
Description: 'Description',
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
+ DESKTOP_NOTIFICATIONS: 'DESKTOP NOTIFICATIONS',
+ Desktop_Alert_info: 'These notifications are delivered in desktop',
Directory: 'Directory',
Direct_Messages: 'Direct Messages',
Disable_notifications: 'Disable notifications',
@@ -192,11 +198,19 @@ export default {
Do_you_have_an_account: 'Do you have an account?',
Do_you_have_a_certificate: 'Do you have a certificate?',
Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?',
+ E2E_How_It_Works_info1: 'You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.',
+ E2E_How_It_Works_info2: 'This is *end to end encryption* so the key to encode/decode your messages and they will not be saved on the server. For that reason *you need to store this password somewhere safe* which you can access later if you may need.',
+ E2E_How_It_Works_info3: 'If you proceed, it will be auto generated an E2E password.',
+ E2E_How_It_Works_info4: 'You can also setup a new password for your encryption key any time from any browser you have entered the existing E2E password.',
edit: 'edit',
edited: 'edited',
Edit: 'Edit',
Edit_Status: 'Edit Status',
Edit_Invite: 'Edit Invite',
+ End_to_end_encrypted_room: 'End to end encrypted room',
+ end_to_end_encryption: 'end to end encryption',
+ Email_Notification_Mode_All: 'Every Mention/DM',
+ Email_Notification_Mode_Disabled: 'Disabled',
Email_or_password_field_is_empty: 'Email or password field is empty',
Email: 'Email',
EMAIL: 'EMAIL',
@@ -204,6 +218,13 @@ export default {
Empty_title: 'Empty title',
Enable_Auto_Translate: 'Enable Auto-Translate',
Enable_notifications: 'Enable notifications',
+ Encrypted: 'Encrypted',
+ Encrypted_message: 'Encrypted message',
+ Enter_Your_E2E_Password: 'Enter Your E2E Password',
+ Enter_Your_Encryption_Password_desc1: 'This will allow you to access your encrypted private groups and direct messages.',
+ Enter_Your_Encryption_Password_desc2: 'You need to enter the password to encode/decode messages every place you use the chat.',
+ Encryption_error_title: 'Your encryption password seems wrong',
+ Encryption_error_desc: 'Wasn\'t possible to decode your encryption key to be imported.',
Everyone_can_access_this_channel: 'Everyone can access this channel',
Error_uploading: 'Error uploading',
Expiration_Days: 'Expiration (Days)',
@@ -232,6 +253,7 @@ export default {
Has_left_the_channel: 'Has left the channel',
Hide_System_Messages: 'Hide System Messages',
Hide_type_messages: 'Hide "{{type}}" messages',
+ How_It_Works: 'How It Works',
Message_HideType_uj: 'User Join',
Message_HideType_ul: 'User Leave',
Message_HideType_ru: 'User Removed',
@@ -245,6 +267,7 @@ export default {
Message_HideType_subscription_role_removed: 'Role No Longer Defined',
Message_HideType_room_archived: 'Room Archived',
Message_HideType_room_unarchived: 'Room Unarchived',
+ I_Saved_My_E2E_Password: 'I Saved My E2E Password',
IP: 'IP',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP',
@@ -335,6 +358,8 @@ export default {
Offline: 'Offline',
Oops: 'Oops!',
Omnichannel: 'Omnichannel',
+ Open_Livechats: 'Chats in Progress',
+ Omnichannel_enable_alert: 'You\'re not available on Omnichannel. Would you like to be available?',
Onboarding_description: 'A workspace is your team or organization’s space to collaborate. Ask the workspace admin for address to join or create one for your team.',
Onboarding_join_workspace: 'Join a workspace',
Onboarding_subtitle: 'Beyond Team Collaboration',
@@ -428,6 +453,10 @@ export default {
saving_profile: 'saving profile',
saving_settings: 'saving settings',
saved_to_gallery: 'Saved to gallery',
+ Save_Your_E2E_Password: 'Save Your E2E Password',
+ Save_Your_Encryption_Password: 'Save Your Encryption Password',
+ Save_Your_Encryption_Password_warning: 'This password is not stored anywhere so save it carefully somewhere else.',
+ Save_Your_Encryption_Password_info: 'Notice that you lose your password, there is no way to recover it and you will lose access to your messages.',
Search_Messages: 'Search Messages',
Search: 'Search',
Search_by: 'Search by',
@@ -488,7 +517,6 @@ export default {
Tap_to_view_servers_list: 'Tap to view servers list',
Terms_of_Service: ' Terms of Service ',
Theme: 'Theme',
- The_URL_is_invalid: 'Invalid URL or unable to establish a secure connection.\n{{contact}}',
The_user_wont_be_able_to_type_in_roomName: 'The user won\'t be able to type in {{roomName}}',
The_user_will_be_able_to_type_in_roomName: 'The user will be able to type in {{roomName}}',
There_was_an_error_while_action: 'There was an error while {{action}}!',
@@ -567,6 +595,7 @@ export default {
You: 'You',
Logged_out_by_server: 'You\'ve been logged out by the server. Please log in again.',
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.',
+ You_need_to_verifiy_your_email_address_to_get_notications: 'You need to verify your email address to get notifications',
Your_certificate: 'Your Certificate',
Your_message: 'Your message',
Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.',
@@ -574,11 +603,12 @@ export default {
Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.',
Your_invite_link_will_never_expire: 'Your invite link will never expire.',
Your_workspace: 'Your workspace',
+ Your_password_is: 'Your password is',
Version_no: 'Version: {{version}}',
You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!',
You_will_unset_a_certificate_for_this_server: 'You will unset a certificate for this server',
Change_Language: 'Change Language',
- Crash_report_disclaimer: 'We never track the content of your chats. The crash report only contains relevant information for us in order to identify problems and fix it.',
+ Crash_report_disclaimer: 'We never track the content of your chats. The crash report and analytics events only contains relevant information for us in order to identify and fix issues.',
Type_message: 'Type message',
Room_search: 'Rooms search',
Room_selection: 'Room selection 1...9',
@@ -624,5 +654,10 @@ export default {
After_seconds_set_by_admin: 'After {{seconds}} seconds (set by admin)',
Dont_activate: 'Don\'t activate now',
Queued_chats: 'Queued chats',
- Queue_is_empty: 'Queue is empty'
+ Queue_is_empty: 'Queue is empty',
+ Logout_from_other_logged_in_locations: 'Logout from other logged in locations',
+ You_will_be_logged_out_from_other_locations: 'You\'ll be logged out from other locations.',
+ Logged_out_of_other_clients_successfully: 'Logged out of other clients successfully',
+ Logout_failed: 'Logout failed!',
+ Log_analytics_events: 'Log analytics events'
};
diff --git a/app/i18n/locales/es-ES.js b/app/i18n/locales/es-ES.js
index 74993266d..e39b87d57 100644
--- a/app/i18n/locales/es-ES.js
+++ b/app/i18n/locales/es-ES.js
@@ -378,7 +378,6 @@ export default {
Tap_to_view_servers_list: 'Pulsa para ver la lista de servidores',
Terms_of_Service: 'Términos de servicio',
Theme: 'Tema',
- The_URL_is_invalid: 'URL inválida o no se puede establecer una conexión segura.\n{{contact}}',
There_was_an_error_while_action: 'Ha habido un error mientras {{action}}!',
This_room_is_blocked: 'La sala está bloqueada',
This_room_is_read_only: 'Esta sala es de sólo lectura',
diff --git a/app/i18n/locales/fr.js b/app/i18n/locales/fr.js
index ce638a0db..4e38ddd56 100644
--- a/app/i18n/locales/fr.js
+++ b/app/i18n/locales/fr.js
@@ -10,12 +10,15 @@ export default {
'error-could-not-change-email': 'Impossible de changer l\'adresse e-mail',
'error-could-not-change-name': 'Impossible de changer le nom',
'error-could-not-change-username': 'Impossible de changer le nom d\'utilisateur',
+ 'error-could-not-change-status': 'Impossible de changer le statut',
'error-delete-protected-role': 'Impossible de supprimer un rôle protégé',
'error-department-not-found': 'Département introuvable',
'error-direct-message-file-upload-not-allowed': 'Le partage de fichiers n\'est pas autorisé dans les messages directs',
'error-duplicate-channel-name': 'un canal avec nom {{channel_name}} existe',
'error-email-domain-blacklisted': 'Le domaine de messagerie est sur liste noire',
'error-email-send-failed': 'Erreur lors de la tentative d\'envoi d\'un courrier électronique: {{message}}',
+ 'error-save-image': 'Erreur en sauvegardant l\'image',
+ 'error-save-video': 'Erreur en sauvegardant la video',
'error-field-unavailable': '{{field}} est déjà utilisé: (',
'error-file-too-large': 'Le fichier est trop volumineux',
'error-importer-not-defined': 'L\'importateur n\'a pas été défini correctement, il manque la classe import.',
@@ -81,27 +84,39 @@ export default {
Add_Reaction: 'Ajouter une réaction',
Add_Server: 'Ajouter un serveur',
Add_users: 'Ajouter des utilisateurs',
+ Admin_Panel: 'Panneau d\'Administration',
+ Agent: 'Agent',
Alert: 'Alerte',
alert: 'alerte',
alerts: 'alertes',
All_users_in_the_channel_can_write_new_messages: 'Tous les utilisateurs du canal peuvent écrire de nouveaux messages',
+ A_meaningful_name_for_the_discussion_room: 'Un nom explicite pour la salle de discussion',
All: 'Tout',
+ All_Messages: 'Tous les messages',
Allow_Reactions: 'Autoriser les réactions',
Alphabetical: 'Alphabétique',
and_more: 'et plus',
and: 'et',
announcement: 'annonce',
Announcement: 'Annonce',
+ Apply_Your_Certificate: 'Valider le Certificat',
+ Applying_a_theme_will_change_how_the_app_looks: 'Valider un thème va modifier l\'affichage de l\'application.',
ARCHIVE: 'ARCHIVER',
archive: 'archiver',
are_typing: 'sont en train d\'écrire',
Are_you_sure_question_mark: 'Êtes-vous sûr ?',
Are_you_sure_you_want_to_leave_the_room: 'Êtes-vous sûr de vouloir quitter le salon {{room}}?',
+ Audio: 'Audio',
Authenticating: 'Authentifier',
+ Automatic: 'Automatique',
+ Auto_Translate: 'Traduction-Auto',
Avatar_changed_successfully: 'Avatar changé avec succès!',
Avatar_Url: 'URL de l\'avatar',
Away: 'absent',
+ Back: 'Arrière',
+ Black: 'Noir',
Block_user: 'Bloquer l\'Utilisateur',
+ Browser: 'Navigateur',
Broadcast_channel_Description: 'Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages, mais les autres utilisateurs pourront répondre.',
Broadcast_Channel: 'Canal de diffusion',
Busy: 'Occupé',
@@ -111,67 +126,141 @@ export default {
Cancel: 'Annuler',
changing_avatar: 'changer d\'avatar',
creating_channel: 'créer un canal',
+ creating_invite: 'création d\'une invitation',
Channel_Name: 'Nom du canal',
Channels: 'Canaux',
Chats: 'Chats',
+ Call_already_ended: 'L\'appel a déjà terminé !',
+ Click_to_join: 'Cliquez pour rejoindre!',
Close: 'Fermer',
Close_emoji_selector: 'Fermer le sélecteur d\'emoji',
+ Closing_chat: 'Fermeture du Salon de discussion',
+ Change_language_loading: 'Changement de la langue.',
+ Chat_closed_by_agent: 'Le salon de discussion a été fermé',
Choose: 'Choisir',
Choose_from_library: 'Choisissez parmi la bibliothèque',
+ Choose_file: 'Choisir un fichier',
+ Choose_where_you_want_links_be_opened: 'Choisissez ou vous souhaitez ouvrir vos liens',
Code: 'Code',
+ Code_or_password_invalid: 'Code ou mot de passe invalide',
Collaborative: 'Collaborative',
Confirm: 'Confirmer',
Connect: 'Se connecter',
Connected: 'Connecté',
+ connecting_server: 'connexion en cours au serveur',
Connecting: 'Connexion ...',
+ Contact_us: 'Contactez nous',
+ Contact_your_server_admin: 'Contactez l\'administrateur de votre serveur.',
Continue_with: 'Continuer avec',
Copied_to_clipboard: 'Copié dans le presse-papier!',
Copy: 'Copier',
+ Conversation: 'Conversation',
Permalink: 'Lien permanent',
+ Certificate_password: 'Mot de passe du Certificat',
+ Clear_cache: 'Effacer le cache local',
+ Clear_cache_loading: 'Effacement du cache.',
+ Whats_the_password_for_your_certificate: 'Quel est le mot de passe du Certificat ?',
Create_account: 'Créer un compte',
Create_Channel: 'Créer un canal',
+ Create_Direct_Messages: 'Créer un message direct',
+ Create_Discussion: 'Créer une Discussion',
Created_snippet: 'Créé un extrait',
Create_a_new_workspace: 'Créer un nouvel espace de travail',
Create: 'Créer',
+ Custom_Status: 'Statut Personnalisé',
+ Dark: 'Sombre',
+ Dark_level: 'Niveau d\'assombrissement',
+ Default: 'Défaut',
+ Default_browser: 'Navigateur par défaut',
Delete_Room_Warning: 'Supprimer une salle supprimera tous les messages postés dans la salle. Ça ne peut pas être annulé.',
+ Department: 'Département',
delete: 'supprimez',
Delete: 'Supprimez',
DELETE: 'SUPPRIMEZ',
deleting_room: 'effacement de la salle',
description: 'la description',
Description: 'La description',
- Disable_notifications: 'Désactiver les notifications',
+ DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
+ Directory: 'Répertoire',
Direct_Messages: 'Messages directs',
+ Disable_notifications: 'Désactiver les notifications',
+ Discussions: 'Discussions',
+ Discussion_Desc: 'Aide à garder un aperçu de ce qui se passe! En créant une discussion, un sous-canal de celui que vous avez sélectionné est créé et les deux sont liés.',
+ Discussion_name: 'Nom de la discussion',
+ Done: 'Fait',
Dont_Have_An_Account: 'Vous n\'avez pas de compte?',
+ Do_you_have_an_account: 'Avez-vous un compte?',
+ Do_you_have_a_certificate: 'Avez-vous un certificat?',
Do_you_really_want_to_key_this_room_question_mark: 'Voulez-vous vraiment {{key}} cette salle?',
edit: 'modifier',
+ edited: 'édité',
Edit: 'Modifier',
+ Edit_Status: 'Modifier le Statut',
+ Edit_Invite: 'Modifier l\'invitation',
Email_or_password_field_is_empty: 'Le champ e-mail ou mot de passe est vide',
Email: 'E-mail',
+ EMAIL: 'EMAIL',
email: 'e-mail',
+ Empty_title: 'Titre vide',
+ Enable_Auto_Translate: 'Activer la traduction-auto',
Enable_notifications: 'Activer les notifications',
Everyone_can_access_this_channel: 'Tout le monde peut accéder à ce canal',
Error_uploading: 'Erreur lors du téléchargement',
+ Expiration_Days: 'Expiration (Jours)',
+ Favorite: 'Favoris',
Favorites: 'Favoris',
Files: 'Fichiers',
File_description: 'Description du fichier',
File_name: 'Nom de fichier',
Finish_recording: 'Terminer l\'enregistrement',
+ Following_thread: 'Suivre le fil',
For_your_security_you_must_enter_your_current_password_to_continue: 'Pour votre sécurité, vous devez entrer votre mot de passe actuel pour continuer.',
Forgot_password_If_this_email_is_registered: 'Si cet e-mail est enregistré, nous vous enverrons des instructions pour réinitialiser votre mot de passe. Si vous ne recevez pas d\'e-mail sous peu, veuillez revenir et réessayer.',
Forgot_password: 'Mot de passe oublié',
Forgot_Password: 'Mot de passe oublié',
+ Forward: 'Faire suivre',
+ Forward_Chat: 'Faire suivre le canal de discussion',
+ Forward_to_department: 'Faire suivre au département',
+ Forward_to_user: 'Faire suivre a l\'utilisateur',
+ Full_table: 'Cliquez pour voir la table complète',
+ Generate_New_Link: 'Générer un nouveau lien',
Group_by_favorites: 'Grouper par favoris',
Group_by_type: 'Grouper par type',
+ Hide: 'Cacher',
Has_joined_the_channel: 'A rejoint le canal',
+ Has_joined_the_conversation: 'A rejoint la conversation',
Has_left_the_channel: 'A quitté la chaîne',
+ Hide_System_Messages: 'Masquer les messages système',
+ Hide_type_messages: 'Masquer les messages "{{type}}"',
+ Message_HideType_uj: 'L\'utilisateur a rejoint',
+ Message_HideType_ul: 'L\'utilisateur est parti',
+ Message_HideType_ru: 'Utilisateur éjecté',
+ Message_HideType_au: 'Utilisateur ajouté',
+ Message_HideType_mute_unmute: 'Utilisateur rendu muet / a retrouvé la parole',
+ Message_HideType_r: 'Le nom du salon a été changé',
+ Message_HideType_ut: 'L\'Utilisateur a rejoint la conversation',
+ Message_HideType_wm: 'Bienvenue',
+ Message_HideType_rm: 'Message supprimé',
+ Message_HideType_subscription_role_added: 'a été défini avec ce Rôle',
+ Message_HideType_subscription_role_removed: 'Ce Rôle n\'est plus défini',
+ Message_HideType_room_archived: 'Salon Archivé',
+ Message_HideType_room_unarchived: 'Salon Désarchivé',
+ IP: 'IP',
+ In_app: 'In-app',
+ IN_APP_AND_DESKTOP: 'IN-APP ET BUREAU',
+ In_App_and_Desktop_Alert_info: 'Affiche une bannière en haut de l\'écran lorsque l\'application est ouverte et affiche une notification sur le bureau',
Invisible: 'Invisible',
Invite: 'Inviter',
is_a_valid_RocketChat_instance: 'est une instance valide de Rocket.Chat',
is_not_a_valid_RocketChat_instance: 'n\'est pas une instance valide de Rocket.Chat',
is_typing: 'est en train d\'écrire',
+ Invalid_or_expired_invite_token: 'Jeton d\'invitation non valide ou expiré',
Invalid_server_version: 'Le serveur que vous essayez de connecter utilise une version qui n\'est plus prise en charge par l\'application: {{currentVersion}}.\n\nNous exigeons la version {{minVersion}}',
+ Invite_Link: 'Lien d\'invitation',
+ Invite_users: 'Inviter utilisateur',
Join: 'Rejoindre',
+ Join_our_open_workspace: 'Rejoignez notre espace de travail ouvert',
+ Join_your_workspace: 'Rejoignez votre espace de travail',
Just_invited_people_can_access_this_channel: 'Seuls les invités peuvent accéder à ce canal',
Language: 'Langue',
last_message: 'Dernier message',
@@ -179,11 +268,17 @@ export default {
leaving_room: 'En quittent le canal',
leave: 'quitter',
Legal: 'Légale',
+ Light: 'Lumière',
+ License: 'Licence',
Livechat: 'Livechat',
+ Livechat_edit: 'Livechat modification',
Login: 'Connexion',
Login_error: 'Vos identifiants ont été rejetés! Veuillez réessayer.',
Login_with: 'Se connecter avec',
+ Logging_out: 'Déconnexion.',
Logout: 'Se déconnecter',
+ Max_number_of_uses: 'Nombre maximum d\'utilisations',
+ Max_number_of_users_allowed_is_number: 'Le nombre maximum d\'utilisateurs autorisés est {{maxUsers}}',
members: 'membres',
Members: 'Membres',
Mentioned_Messages: 'Messages mentionnés',
@@ -193,7 +288,13 @@ export default {
Message_actions: 'Actions de message',
Message_pinned: 'Message épinglé',
Message_removed: 'Message supprimé',
+ Message_starred: 'Message suivi',
+ Message_unstarred: 'Message non suivi',
+ message: 'message',
+ messages: 'messages',
+ Message: 'Message',
Messages: 'Messages',
+ Message_Reported: 'Message signalé',
Microphone_Permission_Message: 'Rocket.Chat doit avoir accès à votre microphone pour pouvoir envoyer un message audio.',
Microphone_Permission: 'Permission de microphone',
Mute: 'Rendre muet',
@@ -203,57 +304,111 @@ export default {
N_users: '{{n}} utilisateurs',
name: 'nom',
Name: 'Nom',
+ Navigation_history: 'Historique de navigation',
+ Never: 'Jamais',
New_Message: 'Nouveau message',
New_Password: 'Nouveau mot de passe',
New_Server: 'Nouveau serveur',
Next: 'Suivant',
No_files: 'Aucun fichier',
+ No_limit: 'Pas de limites',
No_mentioned_messages: 'Aucun message mentionné',
No_pinned_messages: 'Aucun message épinglé',
No_results_found: 'Aucun résultat trouvé',
No_starred_messages: 'Pas de messages suivis',
+ No_thread_messages: 'Aucun fil de discussion',
+ No_label_provided: 'Aucun {{label}} fourni.',
No_Message: 'Aucun message',
+ No_messages_yet: 'Pas encore de messages',
No_Reactions: 'Aucune réaction',
+ No_Read_Receipts: 'Pas d\'accusé de lecture',
Not_logged: 'Non connecté',
+ Not_RC_Server: 'Ce n\'est pas un serveur Rocket.Chat.\n{{contact}}',
+ Nothing: 'Rien',
Nothing_to_save: 'Rien à enregistrer!',
Notify_active_in_this_room: 'Notifier les utilisateurs actifs dans cette salle',
Notify_all_in_this_room: 'Notifier tous dans cette salle',
+ Notifications: 'Notifications',
+ Notification_Duration: 'Durée de Notification',
+ Notification_Preferences: 'Préférences de Notification',
+ No_available_agents_to_transfer: 'Aucun agent disponible à qui transférer',
Offline: 'Hors ligne',
Oops: 'Oops!',
+ Omnichannel: 'Omnichannel',
+ Onboarding_description: 'Un espace de travail est l\'espace de collaboration de votre équipe ou organisation. Demandez à l\'administrateur de l\'espace de travail l\'adresse pour rejoindre ou créez-en une pour votre équipe.',
+ Onboarding_join_workspace: 'Rejoindre un espace de travail',
+ Onboarding_subtitle: 'Au-delà de la collaboration d\'équipe',
Onboarding_title: 'Bienvenue sur Rocket.Chat',
+ Onboarding_join_open_description: 'Rejoignez notre espace de travail ouvert pour discuter avec l\'équipe et la communauté Rocket.Chat.',
+ Onboarding_agree_terms: 'En continuant, vous acceptez Rocket.Chat',
+ Onboarding_less_options: 'Moins d\'options',
+ Onboarding_more_options: 'Plus d\'options',
Online: 'En ligne',
Only_authorized_users_can_write_new_messages: 'Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages.',
Open_emoji_selector: 'Ouvrir sélecteur emoji',
Open_Source_Communication: 'Communication Open Source',
+ Open_your_authentication_app_and_enter_the_code: 'Ouvrez votre application d\'authentification et entrez le code.',
+ OR: 'OU',
+ OS: 'OS',
+ Overwrites_the_server_configuration_and_use_room_config: 'Écrase la configuration du serveur et utilise la configuration du salon',
Password: 'Mot de passe',
+ Parent_channel_or_group: 'Chaîne ou groupe parent',
Permalink_copied_to_clipboard: 'Lien permanent copié dans le presse-papier!',
+ Phone: 'Téléphone',
Pin: 'Épingler',
Pinned_Messages: 'Messages épinglés',
pinned: 'épinglé',
Pinned: 'Épinglé',
+ Please_add_a_comment: 'Veuillez ajouter un commentaire',
Please_enter_your_password: 'Veuillez entrer votre mot de passe',
+ Please_wait: 'Attendez s\'il vous plaît',
+ Preferences: 'Préférences',
Preferences_saved: 'Préférences sauvegardées!',
Privacy_Policy: ' Politique de confidentialité',
Private_Channel: 'Canal privé',
Private_Groups: 'Groupes privés',
Private: 'Privé',
+ Processing: 'En traitement...',
Profile_saved_successfully: 'Profil enregistré avec succès!',
Profile: 'Profil',
Public_Channel: 'Canal Public',
Public: 'Public',
+ PUSH_NOTIFICATIONS: 'NOTIFICATIONS PUSH',
+ Push_Notifications_Alert_Info: 'Ces notifications vous sont livrées lorsque l\'application n\'est pas ouverte',
Quote: 'Citation',
Reactions_are_disabled: 'Les réactions sont désactivées',
Reactions_are_enabled: 'Les réactions sont activées',
Reactions: 'Réactions',
+ Read: 'Lecture',
+ Read_External_Permission_Message: 'Rocket.Chat doit accéder aux photos, aux médias et aux fichiers sur votre appareil',
+ Read_External_Permission: 'Permission de lecture des fichiers',
Read_Only_Channel: 'Chaîne en lecture seule',
Read_Only: 'Lecture seule',
+ Read_Receipt: 'Accusé de réception',
+ Receive_Group_Mentions: 'Recevoir des mentions de groupe',
+ Receive_Group_Mentions_Info: 'Recevoir les mentions @all et @here',
Register: 'S\'inscrire',
Repeat_Password: 'Répéter le mot de passe',
+ Replied_on: 'Répondu le:',
+ replies: 'réponses',
+ reply: 'répondre',
Reply: 'Répondre',
+ Report: 'Reporter',
+ Receive_Notification: 'Recevoir une notification',
+ Receive_notifications_from: 'Recevoir les notifications de {{name}}',
Resend: 'Renvoyer',
Reset_password: 'Réinitialiser le mot de passe',
resetting_password: 'réinitialisation du mot de passe',
RESET: 'RÉINITIALISER',
+ Return: 'Retour',
+ Review_app_title: 'Appréciez-vous cette application?',
+ Review_app_desc: 'Donnez-nous 5 étoiles sur {{store}}',
+ Review_app_yes: 'Bien sur!',
+ Review_app_no: 'Non',
+ Review_app_later: 'plus tard',
+ Review_app_unable_store: 'Impossible d\'ouvrir {{store}}',
+ Review_this_app: 'Donnez votre avis sur cette application',
+ Remove: 'Retirer',
Roles: 'Rôles',
Room_actions: 'Actions de canal',
Room_changed_announcement: 'Annonce de canal est changée en: {{announcement}} par {{userBy}}',
@@ -268,50 +423,91 @@ export default {
SAVE: 'ENREGISTRER',
Save_Changes: 'Sauvegarder les modifications',
Save: 'Sauvegarder',
+ Saved: 'Sauvé',
saving_preferences: 'sauvegardant les préférences',
saving_profile: 'enregistrement du profil',
saving_settings: 'enregistrement des paramètres',
+ saved_to_gallery: 'Sauvé dans la galerie',
Search_Messages: 'Rechercher des messages',
Search: 'Recherche',
+ Search_by: 'Recherche par',
+ Search_global_users: 'Rechercher des utilisateurs mondiaux',
+ Search_global_users_description: 'Si vous activez, vous pouvez rechercher n\'importe quel utilisateur d\'autres sociétés ou serveurs.',
+ Seconds: '{{second}} secondes',
Select_Avatar: 'Sélectionnez un avatar',
+ Select_Server: 'Sélectionnez un serveur',
Select_Users: 'Sélectionner des utilisateurs',
+ Select_a_Channel: 'Sélectionnez un canal',
+ Select_a_Department: 'Sélectionnez un département',
+ Select_an_option: 'Sélectionnez une option',
+ Select_a_User: 'Sélectionnez un utilisateur',
Send: 'Envoyer',
Send_audio_message: 'Envoyer un message audio',
+ Send_crash_report: 'Envoyer un rapport de plantage',
Send_message: 'Envoyer un message',
+ Send_me_the_code_again: 'Envoyez-moi à nouveau le code',
+ Send_to: 'Envoyer à...',
+ Sending_to: 'Envoi à',
+ Sent_an_attachment: 'Envoyé une pièce jointe',
Server: 'Serveur',
Servers: 'Serveurs',
+ Server_version: 'Version du serveur: {{version}}',
Set_username_subtitle: 'Le nom d\'utilisateur est utilisé pour permettre aux autres de vous mentionner dans les messages',
+ Set_custom_status: 'Définir un statut personnalisé',
+ Set_status: 'Définir le statut',
+ Status_saved_successfully: 'Statut enregistré avec succès!',
Settings: 'Paramètres',
Settings_succesfully_changed: 'Paramètres modifiés avec succès!',
Share: 'Partager',
+ Share_Link: 'Partager le lien',
+ Share_this_app: 'Partager cette application',
+ Show_more: 'Afficher plus..',
+ Show_Unread_Counter: 'Afficher le compteur non lu',
+ Show_Unread_Counter_Info: 'Le compteur non-lu est affiché sous forme de badge à droite de la chaîne, dans la liste',
Sign_in_your_server: 'Connectez-vous à votre serveur',
Sign_Up: 'S\'inscrire',
Some_field_is_invalid_or_empty: 'Certains champs sont invalides ou vides',
Sorting_by: 'Tri par {{key}}',
+ Sound: 'Son',
Star_room: 'Favoriser canal',
Star: 'Favoris',
Starred_Messages: 'Les messages favorisé',
starred: 'favorisé',
Starred: 'Favorisé',
Start_of_conversation: 'Début de conversation',
+ Start_a_Discussion: 'Lancer une discussion',
+ Started_discussion: 'A commencé une discussion:',
+ Started_call: 'Appel lancé par {{userBy}}',
Submit: 'Soumettre',
+ Table: 'Table',
+ Tags: 'Mots clés',
Take_a_photo: 'Prendre une photo',
+ Take_a_video: 'Prendre une vidéo',
+ Take_it: 'Prends-le!',
tap_to_change_status: 'Appuyez pour changer de statut',
Tap_to_view_servers_list: 'Appuyez pour afficher la liste des serveurs',
Terms_of_Service: ' Conditions d\'utilisation ',
- The_URL_is_invalid: 'L\'URL que vous avez entrée n\'est pas valide. Vérifiez et essayez à nouveau!',
+ Theme: 'Thème',
+ The_user_wont_be_able_to_type_in_roomName: 'L\'utilisateur ne pourra pas écrire dans {{roomName}}',
+ The_user_will_be_able_to_type_in_roomName: 'L\'utilisateur pourra écrire dans {{roomName}}',
There_was_an_error_while_action: 'Il y avait une erreur en {{action}}!',
This_room_is_blocked: 'Cette canal est bloquée',
This_room_is_read_only: 'Cette canal est en lecture seule',
+ Thread: 'Fil de discutions',
+ Threads: 'Fils de discutions',
Timezone: 'Fuseau horaire',
+ To: 'A',
topic: 'sujet',
Topic: 'Sujet',
+ Translate: 'Traduire',
Try_again: 'Réessayer',
Two_Factor_Authentication: 'Authentification à deux facteurs',
Type_the_channel_name_here: 'Tapez le nom de canal ici',
unarchive: 'désarchiver',
UNARCHIVE: 'DÉSARCHIVER',
Unblock_user: 'Débloquer l\'utilisateur',
+ Unfavorite: 'Supprimer des favoris',
+ Unfollowed_thread: 'Ne plus suivre ce fil',
Unmute: 'Rendre La parole',
unmuted: 'Rendu la parole',
Unpin: 'Détacher',
@@ -319,24 +515,45 @@ export default {
Unread: 'Non lu',
Unread_on_top: 'Non lu sur le dessus',
Unstar: 'Unstar',
+ Updating: 'Mise à jour...',
Uploading: 'Téléchargement',
Upload_file_question_mark: 'Télécharger le fichier?',
+ User: 'Utilisateur',
+ Users: 'Utilisateurs',
User_added_by: 'L\'utilisateur {{userAdded}} a été ajouté par {{userBy}}',
+ User_Info: 'Info d\'utilisateur',
User_has_been_key: 'L\'utilisateur a été {{key}}!',
User_is_no_longer_role_by_: '{{user}} n\'est plus {{role}} par {{userBy}}',
User_muted_by: 'L\'utilisateur {{userMuted}} a été rendu muet par {{userBy}}',
User_removed_by: 'L\'utilisateur {{userRemoved}} a été retiré par {{userBy}}',
User_sent_an_attachment: '{{user}} envoyé une pièce jointe',
- User_unmuted_by: 'L\'utilisateur {{userBy}} a rendu la parole a {{userUnmuted}} ',
+ User_unmuted_by: 'L\'utilisateur {{userBy}} a rendu la parole a {{userUnmuted}}',
User_was_set_role_by_: '{{user}} l\'utilisateur a été défini {{role}} par {{userBy}}',
Username_is_empty: 'Nom d\'utilisateur est vide',
Username: 'Nom d\'utilisateur',
Username_or_email: 'Nom d\'utilisateur ou address e-mail',
+ Uses_server_configuration: 'Utilise la configuration du serveur',
+ Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Habituellement, une discussion commence par une question, comme "Comment télécharger une image?"',
Validating: 'Validation',
+ Registration_Succeeded: 'Inscription réussie!',
+ Verify: 'Vérifier',
+ Verify_email_title: 'Inscription réussie!',
+ Verify_email_desc: 'Nous vous avons envoyé un e-mail pour confirmer votre inscription. Si vous ne recevez pas d\'e-mail sous peu, veuillez revenir et réessayer.',
+ Verify_your_email_for_the_code_we_sent: 'Vérifiez votre e-mail pour le code que nous avons envoyé',
Video_call: 'Appel vidéo',
+ View_Original: 'Voir l\'original',
Voice_call: 'Appel vocal',
+ Waiting_for_network: 'En attente du réseau ...',
+ Websocket_disabled: 'Le Websocket est désactivé pour ce serveur.\n{{contact}}',
Welcome: 'Bienvenue',
+ What_are_you_doing_right_now: 'Qu\'es ce que vous faites actuellement?',
Whats_your_2fa: 'Quel est votre code 2FA?',
+ Without_Servers: 'Sans serveurs',
+ Workspaces: 'Espaces de travail',
+ Would_you_like_to_return_the_inquiry: 'Souhaitez-vous retourner la demande?',
+ Write_External_Permission_Message: 'Rocket.Chat a besoin d\'accéder à votre galerie pour que vous puissiez enregistrer des images.',
+ Write_External_Permission: 'Autorisation de la galerie',
+ Yes: 'Oui',
Yes_action_it: 'Oui, {{action}} le!',
Yesterday: 'Hier',
You_are_in_preview_mode: 'Vous êtes en mode de prévisualisation',
@@ -344,7 +561,67 @@ export default {
You_can_search_using_RegExp_eg: 'Vous pouvez rechercher à l\'aide de RegExp. e.g. `/^text$/i`',
You_colon: 'Vous: ',
you_were_mentioned: 'vous avez été mentionné',
+ You_were_removed_from_channel: 'Vous avez été retiré de{{channel}}',
you: 'vous',
You: 'Vous',
- You_will_not_be_able_to_recover_this_message: 'Vous ne serez pas en mesure de récupérer ce message!'
+ Logged_out_by_server: 'Vous avez été déconnecté par le serveur. Veuillez vous reconnecter.',
+ You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Vous devez accéder à au moins un serveur Rocket.Chat pour partager quelque chose.',
+ Your_certificate: 'Votre Certificat',
+ Your_message: 'Votre message',
+ Your_invite_link_will_expire_after__usesLeft__uses: 'Votre lien d\'invitation expirera après {{usesLeft}} utilisations.',
+ Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Votre lien d\'invitation expirera le {{date}} ou après {{usesLeft}} utilisations.',
+ Your_invite_link_will_expire_on__date__: 'Votre lien d\'invitation expirera le {{date}}.',
+ Your_invite_link_will_never_expire: 'Votre lien d\'invitation n\'expirera jamais.',
+ Your_workspace: 'Votre espace de travail',
+ Version_no: 'Version: {{version}}',
+ You_will_not_be_able_to_recover_this_message: 'Vous ne pourrez pas récupérer ce message!',
+ You_will_unset_a_certificate_for_this_server: 'Vous allez annuler un certificat pour ce serveur',
+ Change_Language: 'Changer la Langue',
+ Crash_report_disclaimer: 'Nous ne suivons jamais le contenu de vos chats. Le rapport de plantage ne contient que des informations pertinentes pour nous afin d\'identifier les problèmes et de les résoudre.',
+ Type_message: 'Écrire un message',
+ Room_search: 'Recherche de salon',
+ Room_selection: 'Sélection du Salon 1...9',
+ Next_room: 'Salon Suivant',
+ Previous_room: 'Salon Précédent',
+ New_room: 'Nouveau salon',
+ Upload_room: 'Envoyer sur un salon',
+ Search_messages: 'Recherche de messages',
+ Scroll_messages: 'Défiler messages',
+ Reply_latest: 'Répondre au dernier',
+ Reply_in_Thread: 'Répondre dans le fil',
+ Server_selection: 'Sélection du serveur',
+ Server_selection_numbers: 'Sélection du Serveur 1...9',
+ Add_server: 'Ajouter serveur',
+ New_line: 'Nouvelle ligne',
+ You_will_be_logged_out_of_this_application: 'Vous serez déconnecté de cette application.',
+ Clear: 'Effacer',
+ This_will_clear_all_your_offline_data: 'Cela effacera toutes vos données hors-ligne.',
+ This_will_remove_all_data_from_this_server: 'Cela supprimera toutes les données de ce serveur.',
+ Mark_unread: 'Marquer comme non lu',
+ Wait_activation_warning: 'Avant de pouvoir vous connecter, votre compte doit être activé manuellement par un administrateur.',
+ Screen_lock: 'Verrouillage d\'écran',
+ Local_authentication_biometry_title: 'Authentifier',
+ Local_authentication_biometry_fallback: 'Utiliser le mot de passe',
+ Local_authentication_unlock_option: 'Déverrouiller avec mot de passe',
+ Local_authentication_change_passcode: 'Changer le code',
+ Local_authentication_info: 'Remarque: si vous oubliez le code, vous devrez supprimer et réinstaller l\'application.',
+ Local_authentication_facial_recognition: 'reconnaissance faciale',
+ Local_authentication_fingerprint: 'empreinte digitale',
+ Local_authentication_unlock_with_label: 'Déverrouiller avec {{label}}',
+ Local_authentication_auto_lock_60: 'Après 1 minute',
+ Local_authentication_auto_lock_300: 'Après 5 minutes',
+ Local_authentication_auto_lock_900: 'Après 15 minutes',
+ Local_authentication_auto_lock_1800: 'Après 30 minutes',
+ Local_authentication_auto_lock_3600: 'Après 1 heure',
+ Passcode_enter_title: 'Entrez votre mot de passe',
+ Passcode_choose_title: 'Choisissez votre nouveau mot de passe',
+ Passcode_choose_confirm_title: 'Confirmez votre nouveau mot de passe',
+ Passcode_choose_error: 'Les codes secrets ne correspondent pas. Réessayer.',
+ Passcode_choose_force_set: 'Code d\'accès requis par l\'administrateur',
+ Passcode_app_locked_title: 'App verrouillée',
+ Passcode_app_locked_subtitle: 'Réessayez dans {{timeLeft}} secondes',
+ After_seconds_set_by_admin: 'Après {{seconds}} secondes (défini par l\'administrateur)',
+ Dont_activate: 'Ne pas activer maintenant',
+ Queued_chats: 'Discussions en file d\'attente',
+ Queue_is_empty: 'La file d\'attente est vide'
};
diff --git a/app/i18n/locales/it.js b/app/i18n/locales/it.js
index f56c21e79..0fdafa7ce 100644
--- a/app/i18n/locales/it.js
+++ b/app/i18n/locales/it.js
@@ -10,6 +10,7 @@ export default {
'error-could-not-change-email': 'Impossibile cambiare l\'indirizzo e-mail',
'error-could-not-change-name': 'Impossibile cambiare nome',
'error-could-not-change-username': 'Impossibile cambiare username',
+ 'error-could-not-change-status': 'Impossibile cambiare lo stato',
'error-delete-protected-role': 'Impossibile eliminare un ruolo protetto',
'error-department-not-found': 'Reparto non trovato',
'error-direct-message-file-upload-not-allowed': 'La condivisione di file non è autorizzata nei messaggi diretti',
@@ -17,13 +18,14 @@ export default {
'error-email-domain-blacklisted': 'Il dominio e-mail è nella lista nera',
'error-email-send-failed': 'Errore nel tentativo di invio e-mail: {{message}}',
'error-save-image': 'Errore nel salvataggio dell\'immagine',
+ 'error-save-video': 'Errore nel salvataggio del video',
'error-field-unavailable': '{{field}} è già in uso :(',
'error-file-too-large': 'File troppo grande',
'error-importer-not-defined': 'L\'importatore non è stato definito correttamente: classe Import mancante.',
'error-input-is-not-a-valid-field': '{{input}} non è valido come {{field}}',
'error-invalid-actionlink': 'Link azione non valido',
'error-invalid-arguments': 'Parametri non validi',
- 'error-invalid-asset': 'Asset non valido',
+ 'error-invalid-asset': 'Risorsa non valida',
'error-invalid-channel': 'Canale non valido.',
'error-invalid-channel-start-with-chars': 'Canale non valido. Inizia con @ o #',
'error-invalid-custom-field': 'Campo personalizzato non valido',
@@ -83,10 +85,12 @@ export default {
Add_Server: 'Aggiungi server',
Add_users: 'Aggiungi utenti',
Admin_Panel: 'Amministrazione',
+ Agent: 'Agente',
Alert: 'Avviso',
alert: 'avviso',
alerts: 'avvisi',
All_users_in_the_channel_can_write_new_messages: 'Tutti gli utenti nel canale possono scrivere messaggi',
+ A_meaningful_name_for_the_discussion_room: 'Un nome significativo per il canale di discussione',
All: 'Tutti',
All_Messages: 'Tutti i messaggi',
Allow_Reactions: 'Permetti reazioni',
@@ -112,6 +116,7 @@ export default {
Back: 'Indietro',
Black: 'Nero',
Block_user: 'Blocca utente',
+ Browser: 'Browser',
Broadcast_channel_Description: 'Solo gli utenti autorizzati possono scrivere messaggi, ma gli altri utenti saranno in grado di rispondere',
Broadcast_Channel: 'Canale broadcast',
Busy: 'Occupato',
@@ -126,13 +131,22 @@ export default {
Channels: 'Canali',
Chats: 'Chat',
Call_already_ended: 'Chiamata già terminata!',
+ Clear_cookies_alert: 'Vuoi cancellare tutti i cookie?',
+ Clear_cookies_desc: 'Questo cancellerà tutti i coockie di login, permettendoti di accedere con altri account.',
+ Clear_cookies_yes: 'Si, cancella i cookie',
+ Clear_cookies_no: 'No, mantieni i cookie',
Click_to_join: 'Clicca per unirti!',
Close: 'Chiudi',
Close_emoji_selector: 'Chiudi selettore emoji',
+ Closing_chat: 'Chiudendo la chat',
+ Change_language_loading: 'Cambiando la lingua.',
+ Chat_closed_by_agent: 'Chat chiusa dall\'agente',
Choose: 'Scegli',
Choose_from_library: 'Scegli dalla libreria',
Choose_file: 'Scegli file',
+ Choose_where_you_want_links_be_opened: 'Scegli dove vuoi che vengano aperti i link',
Code: 'Codice',
+ Code_or_password_invalid: 'Codice o password non validi',
Collaborative: 'Collaborativo',
Confirm: 'Conferma',
Connect: 'Connetti',
@@ -142,20 +156,28 @@ export default {
Contact_us: 'Contattaci',
Contact_your_server_admin: 'Contatta l\'amministratore.',
Continue_with: 'Continua con',
- Copied_to_clipboard: 'Copiato in Appunti!',
+ Copied_to_clipboard: 'Copiato negli appunti!',
Copy: 'Copia',
Permalink: 'Permalink',
+ Conversation: 'Conversazione',
Certificate_password: 'Password certificato',
+ Clear_cache: 'Cancella la cache locale',
+ Clear_cache_loading: 'Cancellando la cache.',
Whats_the_password_for_your_certificate: 'Qual\'è la password del tuo certificato?',
Create_account: 'Crea un account',
Create_Channel: 'Crea canale',
+ Create_Direct_Messages: 'Crea Messaggi Privati',
+ Create_Discussion: 'Crea una Discussione',
Created_snippet: 'Snippet creato',
Create_a_new_workspace: 'Crea un nuovo workspace',
Create: 'Crea',
+ Custom_Status: 'Stato personalizzato',
Dark: 'Scuro',
Dark_level: 'Contrasto',
Default: 'Predefinito',
+ Default_browser: 'Browser predefinito',
Delete_Room_Warning: 'Eliminare una stanza cancellerà tutti i messaggi in essa contenuti. Questa azione non può essere annullata.',
+ Department: 'Dipartimento',
delete: 'elimina',
Delete: 'Elimina',
DELETE: 'ELIMINA',
@@ -163,23 +185,46 @@ export default {
description: 'descrizione',
Description: 'Descrizione',
DESKTOP_OPTIONS: 'OPZIONI DESKTOP',
+ DESKTOP_NOTIFICATIONS: 'NOTIFICHE DESKTOP',
+ Desktop_Alert_info: 'Queste notifiche vengono inviate sul client desktop',
Directory: 'Rubrica',
Direct_Messages: 'Messaggi diretti',
Disable_notifications: 'Disabilita notifiche',
Discussions: 'Discussioni',
+ Discussion_Desc: 'Aiuta a mantenere una panoramica di ciò che sta succedendo! Creando una discussione verrà creato un sotto-canale di quello selezionato ed entrambi saranno collegati',
+ Discussion_name: 'Nome della discussione',
+ Done: 'Fatto',
Dont_Have_An_Account: 'Non hai un account?',
+ Do_you_have_an_account: 'Hai un account?',
Do_you_have_a_certificate: 'Hai un certificato?',
Do_you_really_want_to_key_this_room_question_mark: 'Sei sicuro di voler {{key}} questa stanza?',
+ E2E_How_It_Works_info1: 'Ora puoi creare gruppi e messaggi privati crittografati. Puoi anche crittografare quelli già esistenti.',
+ E2E_How_It_Works_info2: 'Questa è *crittografia end-to-end*, quindi la chiave per cifrare/decifrare i messaggi non verrà salvata sul server. Per questo motivo *devi salvare questa password in un luogo sicuro* dove poterla recuperare in seguito qualora necessario.',
+ E2E_How_It_Works_info3: 'Procedendo verrà generata automaticamente una password E2E.',
+ E2E_How_It_Works_info4: 'Puoi impostare una nuova password per la chiave di cifratura in qualsiasi momento da uno dei browser dove hai inserito la password E2E esistente.',
edit: 'modifica',
edited: 'modificato',
Edit: 'Modifica',
+ Edit_Status: 'Modifica Stato',
Edit_Invite: 'Modifica invito',
+ End_to_end_encrypted_room: 'Stanza crittografata end to end',
+ end_to_end_encryption: 'Crittografia end to end',
+ Email_Notification_Mode_All: 'Ogni Menzione/Messaggio Privato',
+ Email_Notification_Mode_Disabled: 'Disabilitato',
Email_or_password_field_is_empty: 'Il campo e-mail o password sono vuoti',
Email: 'E-mail',
EMAIL: 'E-MAIL',
email: 'e-mail',
+ Empty_title: 'Titolo vuoto',
Enable_Auto_Translate: 'Abilita traduzione automatica',
Enable_notifications: 'Abilita notifiche',
+ Encrypted: 'Crittografato',
+ Encrypted_message: 'Messaggio crittografato',
+ Enter_Your_E2E_Password: 'Inserisci la tua password E2E',
+ Enter_Your_Encryption_Password_desc1: 'Potrai così accedere ai tuoi gruppi privati e messaggi privati crittografati.',
+ Enter_Your_Encryption_Password_desc2: 'Devi inserire la password per cifrare/decifrare i messaggi ovunque usi la chat.',
+ Encryption_error_title: 'La tua password di cifratura sembra errata',
+ Encryption_error_desc: 'Non è stato possibile importare la tua chiave di cifratura.',
Everyone_can_access_this_channel: 'Tutti hanno accesso a questo canale',
Error_uploading: 'Errore nel caricamento di',
Expiration_Days: 'Scadenza (giorni)',
@@ -194,6 +239,10 @@ export default {
Forgot_password_If_this_email_is_registered: 'Se questa e-mail è registrata, manderemo istruzioni su come resettare la tua password. Se non ricevi nulla, torna qui e riprova di nuovo.',
Forgot_password: 'Password dimenticata',
Forgot_Password: 'Password dimenticata',
+ Forward: 'Inoltra',
+ Forward_Chat: 'Inoltra Chat',
+ Forward_to_department: 'Inoltra a dipartimento',
+ Forward_to_user: 'Inoltra ad udente',
Full_table: 'Clicca per la tabella completa',
Generate_New_Link: 'Genera nuovo link',
Group_by_favorites: 'Raggruppa per preferiti',
@@ -202,6 +251,25 @@ export default {
Has_joined_the_channel: 'si è unito al canale',
Has_joined_the_conversation: 'si è unito alla conversazione',
Has_left_the_channel: 'ha lasciato il canale',
+ Hide_System_Messages: 'Nascondi messaggi di sistema',
+ Hide_type_messages: 'Nascondi messaggi di "{{type}}"',
+ How_It_Works: 'Come funziona',
+ Message_HideType_uj: 'Ingresso Utente',
+ Message_HideType_ul: 'Uscita Utente',
+ Message_HideType_ru: 'Rimozione Utente',
+ Message_HideType_au: 'Aggiunta Utente',
+ Message_HideType_mute_unmute: 'Microfono Utente attivato / disattivato',
+ Message_HideType_r: 'Nome Stanza cambiato',
+ Message_HideType_ut: 'Ingresso Utente in Conversazione',
+ Message_HideType_wm: 'Benvenuto',
+ Message_HideType_rm: 'Rimozione Messaggio',
+ Message_HideType_subscription_role_added: 'Creazione ruolo',
+ Message_HideType_subscription_role_removed: 'Rimozione ruolo',
+ Message_HideType_room_archived: 'Stanza archiviata',
+ Message_HideType_room_unarchived: 'Stanza ripristinata dall\'archivio',
+ I_Saved_My_E2E_Password: 'Ho salvato la mia Password E2E',
+ IP: 'Indirizzo IP',
+ In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP E DESKTOP',
In_App_and_Desktop_Alert_info: 'Mostra una notifica in cima allo schermo quando l\'app è aperta, e mostra una notifica sul desktop',
Invisible: 'Invisibile',
@@ -214,6 +282,8 @@ export default {
Invite_Link: 'Link di invito',
Invite_users: 'Invita utenti',
Join: 'Entra',
+ Join_our_open_workspace: 'Unisciti al nostro workspace',
+ Join_your_workspace: 'Unisciti al tuo workspace',
Just_invited_people_can_access_this_channel: 'Solo le persone invitate possono accedere a questo canale',
Language: 'Lingua',
last_message: 'ultimo messaggio',
@@ -224,11 +294,14 @@ export default {
Light: 'Chiaro',
License: 'Licenza',
Livechat: 'Livechat',
+ Livechat_edit: 'Modifica Livechat',
Login: 'Accedi',
Login_error: 'Le tue credenziali sono state rifiutate! Prova di nuovo.',
Login_with: 'Accedi con',
+ Logging_out: 'Disconnettendo.',
Logout: 'Disconnetti',
Max_number_of_uses: 'Max numero di utilizzi',
+ Max_number_of_users_allowed_is_number: 'Il numero massimo di utenti ammessi è {{maxUsers}}',
members: 'membri',
Members: 'Membri',
Mentioned_Messages: 'Messaggi menzionati',
@@ -236,10 +309,13 @@ export default {
Mentions: 'Menzioni',
Message_accessibility: 'Messaggio da {{user}} alle {{time}}: {{message}}',
Message_actions: 'Azioni messaggio',
- Message_pinned: 'Messaggio attaccato',
+ Message_pinned: 'Messaggio appuntato',
Message_removed: 'Messaggio rimosso',
+ Message_starred: 'Messaggio importante',
+ Message_unstarred: 'Messaggio non importante',
message: 'messaggio',
messages: 'messaggi',
+ Message: 'Messaggio',
Messages: 'Messaggi',
Message_Reported: 'Messaggio segnalato',
Microphone_Permission_Message: 'Rocket.Chat richiede l\'accesso al microfono per inviare messaggi audio.',
@@ -251,6 +327,7 @@ export default {
N_users: '{{n}} utenti',
name: 'nome',
Name: 'Nome',
+ Navigation_history: 'Cronologia di navigazione',
Never: 'Mai',
New_Message: 'Nuovo messaggio',
New_Password: 'Nuova password',
@@ -263,6 +340,7 @@ export default {
No_results_found: 'Nessun risultato',
No_starred_messages: 'Nessun messaggio preferito',
No_thread_messages: 'Nessun messaggio thread',
+ No_label_provided: 'Nessun {{label}} fornito.',
No_Message: 'Nessun messaggio',
No_messages_yet: 'Non ci sono ancora messaggi',
No_Reactions: 'Nessuna reazione',
@@ -276,20 +354,39 @@ export default {
Notifications: 'Notifiche',
Notification_Duration: 'Durata notifiche',
Notification_Preferences: 'Impostazioni notifiche',
+ No_available_agents_to_transfer: 'Nessun agente disponibile a cui trasferire',
Offline: 'Offline',
Oops: 'Oops!',
+ Omnichannel: 'Omnichannel',
+ Open_Livechats: 'Chat in corso',
+ Omnichannel_enable_alert: 'Non sei ancora su Onmichannel. Vuoi attivarlo?',
+ Onboarding_description: 'Un workspace è lo spazio dove il tuo team o la tua organizzazione possono collaborare. Chiedi l\'indirizzo all\'amministratore del tuo workspace oppure creane uno per il tuo team.',
+ Onboarding_join_workspace: 'Unisciti',
+ Onboarding_subtitle: 'Oltre la collaborazione di gruppo',
Onboarding_title: 'Benvenuto in Rocket.Chat',
+ Onboarding_join_open_description: 'Unisciti al nostro workspace per parlare con il team e la community di Rocket.Chat.',
+ Onboarding_agree_terms: 'Procedendo, accetti Rocket.Chat',
+ Onboarding_less_options: 'Meno opzioni',
+ Onboarding_more_options: 'Più opzioni',
Online: 'Online',
Only_authorized_users_can_write_new_messages: 'Solo gli utenti autorizzati possono scrivere nuovi messaggi',
Open_emoji_selector: 'Apri selettore emoji',
Open_Source_Communication: 'Comunicazione open-source',
+ Open_your_authentication_app_and_enter_the_code: 'Apri l\'app di autenticazione e inserisci il codice.',
+ OR: 'O',
+ OS: 'SO',
+ Overwrites_the_server_configuration_and_use_room_config: 'Sovrascrive la configurazione del server in favore di quella della stanza',
Password: 'Password',
+ Parent_channel_or_group: 'Gruppo o canale originario',
Permalink_copied_to_clipboard: 'Permalink copiato negli appunti!',
- Pin: 'Attacca',
- Pinned_Messages: 'Messaggi attaccati',
- pinned: 'attaccato',
- Pinned: 'Attaccati',
+ Phone: 'Telefono',
+ Pin: 'Appunta',
+ Pinned_Messages: 'Messaggi appuntati',
+ pinned: 'appuntati',
+ Pinned: 'Appuntati',
+ Please_add_a_comment: 'Per favore, aggiungi un commento',
Please_enter_your_password: 'Per favore, inserisci la tua password',
+ Please_wait: 'Si prega di attendere.',
Preferences: 'Impostazioni',
Preferences_saved: 'Impostazioni salvate!',
Privacy_Policy: ' Privacy Policy',
@@ -308,6 +405,8 @@ export default {
Reactions_are_enabled: 'Le reazioni sono abilitate',
Reactions: 'Reazioni',
Read: 'Letto',
+ Read_External_Permission_Message: 'Rocket.Chat deve accedere alle foto, media, e documenti sul tuo dispositivo',
+ Read_External_Permission: 'Permesso di Lettura della Memoria',
Read_Only_Channel: 'Canale in sola lettura',
Read_Only: 'Sola lettura',
Read_Receipt: 'Conferma di lettura',
@@ -326,6 +425,15 @@ export default {
Reset_password: 'Ripristina password',
resetting_password: 'ripristinando password',
RESET: 'RIPRISTINA',
+ Return: 'Ritorno',
+ Review_app_title: 'Ti piace questa app?',
+ Review_app_desc: 'Dacci 5 stesse su {{store}}',
+ Review_app_yes: 'Certo!',
+ Review_app_no: 'No',
+ Review_app_later: 'In seguito',
+ Review_app_unable_store: 'Impossibile aprire {{store}}',
+ Review_this_app: 'Recensisci questa app',
+ Remove: 'Rimuovi',
Roles: 'Ruoli',
Room_actions: 'Azioni stanza',
Room_changed_announcement: 'Annuncio stanza cambiato in: {{announcement}} da {{userBy}}',
@@ -340,10 +448,15 @@ export default {
SAVE: 'SALVA',
Save_Changes: 'Salva cambiamenti',
Save: 'Salva',
+ Saved: 'Salvato',
saving_preferences: 'salvataggio impostazioni',
saving_profile: 'salvataggio profilo',
saving_settings: 'salvataggio impostazioni',
saved_to_gallery: 'Salvato in Galleria',
+ Save_Your_E2E_Password: 'Salva la tua Password E2E',
+ Save_Your_Encryption_Password: 'Salva la tua Password di cifratura',
+ Save_Your_Encryption_Password_warning: 'Questa password non è salvata da nessuna parte, conservala con cura.',
+ Save_Your_Encryption_Password_info: 'Nota: se dovessi perdere la tua password, non c\'è alcun modo per recuperarla e perderesti l\'accesso ai tuoi messaggi.',
Search_Messages: 'Cerca messaggi',
Search: 'Cerca',
Search_by: 'Cerca per',
@@ -353,21 +466,31 @@ export default {
Select_Avatar: 'Seleziona avatar',
Select_Server: 'Seleziona server',
Select_Users: 'Seleziona utenti',
+ Select_a_Channel: 'Seleziona un Canale',
+ Select_a_Department: 'Seleziona un Dipartimento',
+ Select_an_option: 'Seleziona un\' opzione',
+ Select_a_User: 'Seleziona un Utente',
Send: 'Invia',
Send_audio_message: 'Invia messaggio audio',
- Send_crash_report: 'Invia crash report',
+ Send_crash_report: 'Invia report sui crash',
Send_message: 'Invia messaggio',
+ Send_me_the_code_again: 'Inviami nuovamente il codice',
Send_to: 'Invia a...',
+ Sending_to: 'Invio a',
Sent_an_attachment: 'Inviato un allegato',
Server: 'Server',
Servers: 'Servers',
Server_version: 'Versione server: {{version}}',
Set_username_subtitle: 'Il nome utente viene utilizzato per permettere ad altri di menzionarti nei messaggi',
+ Set_custom_status: 'Imposta stato personalizzato',
+ Set_status: 'Imposta stato',
+ Status_saved_successfully: 'Stato salvato correttamente!',
Settings: 'Impostazioni',
Settings_succesfully_changed: 'Impostazioni modificate correttamente!',
Share: 'Condividi',
Share_Link: 'Condividi link',
Share_this_app: 'Condividi questa app',
+ Show_more: 'Mostra altri..',
Show_Unread_Counter: 'Mostra contatore messaggi non letti',
Show_Unread_Counter_Info: 'Il contatore viene mostrato come un\'etichetta alla destra del canale, nella lista',
Sign_in_your_server: 'Accedi al tuo server',
@@ -381,17 +504,21 @@ export default {
starred: 'preferiti',
Starred: 'Preferiti',
Start_of_conversation: 'Inizio della conversazione',
+ Start_a_Discussion: 'Avvia una Discussione',
Started_discussion: 'Discussione iniziata:',
Started_call: 'Chiamata iniziata da {{userBy}}',
Submit: 'Invia',
Table: 'Tabella',
+ Tags: 'Tag',
Take_a_photo: 'Scatta una foto',
Take_a_video: 'Registra un video',
+ Take_it: 'Prendi!',
tap_to_change_status: 'tocca per cambiare stato',
Tap_to_view_servers_list: 'Tocca per vedere la lista server',
Terms_of_Service: ' Termini di servizio ',
Theme: 'Tema',
- The_URL_is_invalid: 'URL non valido o errore nello stabilimento di una connessione sicura.\n{{contact}}',
+ The_user_wont_be_able_to_type_in_roomName: 'L\'utente non potrà scrivere in {{roomName}}',
+ The_user_will_be_able_to_type_in_roomName: 'L\'utente potrà scrivere in {{roomName}}',
There_was_an_error_while_action: 'Si è verificato un errore nel {{action}}!',
This_room_is_blocked: 'Questa stanza è bloccata',
This_room_is_read_only: 'Questa stanza è in sola lettura',
@@ -420,6 +547,7 @@ export default {
Updating: 'Aggiornamento...',
Uploading: 'Caricamento',
Upload_file_question_mark: 'Carica file?',
+ User: 'Utente',
Users: 'Utenti',
User_added_by: 'Utente {{userAdded}} aggiunto da {{userBy}}',
User_Info: 'Informazioni utente',
@@ -433,16 +561,28 @@ export default {
Username_is_empty: 'Username vuoto',
Username: 'Username',
Username_or_email: 'Username o email',
+ Uses_server_configuration: 'Usa la configurazione del server',
+ Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Una discussione inizia solitamente con una domanda, ad esempio: "Come posso caricare una foto?"',
Validating: 'Validazione',
+ Registration_Succeeded: 'Registrazione completata!',
+ Verify: 'Verifica',
+ Verify_email_title: 'Verifica completata!',
+ Verify_email_desc: 'Ti abbiamo inviato una e-mail per confermare la tua registrazione. Se non la ricevi, ritorna qui e riprova',
+ Verify_your_email_for_the_code_we_sent: 'Controlla l\'e-mail con il codice che ti abbiamo inviato',
Video_call: 'Videochiamata',
View_Original: 'Mostra originale',
Voice_call: 'Chiamata vocale',
- Websocket_disabled: 'Websocket è disabilitato per questo server.\n{{contact}}',
+ Waiting_for_network: 'In attesa di connessione ...',
+ Websocket_disabled: 'Websocket disabilitata per questo server.\n{{contact}}',
Welcome: 'Benvenuto',
+ What_are_you_doing_right_now: 'Cosa stai facendo in questo momento?',
Whats_your_2fa: 'Qual\'è il tuo codice 2FA?',
Without_Servers: 'Senza server',
+ Workspaces: 'Workspace',
+ Would_you_like_to_return_the_inquiry: 'Vorresti ritirare la tua domanda?',
Write_External_Permission_Message: 'Rocket.Chat ha bisogno dell\'accesso alla galleria per salvare le immagini.',
Write_External_Permission: 'Permesso galleria',
+ Yes: 'Si',
Yes_action_it: 'Sì, {{action}}!',
Yesterday: 'Ieri',
You_are_in_preview_mode: 'Sei in modalità anteprima',
@@ -450,16 +590,23 @@ export default {
You_can_search_using_RegExp_eg: 'Puoi usare espressioni regolari. es. `/^testo$/i`',
You_colon: 'Tu: ',
you_were_mentioned: 'sei stato menzionato',
+ You_were_removed_from_channel: 'Sei stato rimosso da {{channel}}',
you: 'tu',
You: 'Tu',
+ Logged_out_by_server: 'Sei stato disconnesso dal server. Esegui nuovamente l\'accesso.',
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Devi accedere ad almeno un server Rocket.Chat prima di condividere qualcosa.',
+ You_need_to_verifiy_your_email_address_to_get_notications: 'Devi verificare il tuo indirizzo e-mail per ricevere le notifiche',
Your_certificate: 'Il tuo certificato',
+ Your_message: 'Il tuo messaggio',
Your_invite_link_will_expire_after__usesLeft__uses: 'Il tuo link di invito scadrà dopo {{usesLeft}} utilizzi.',
Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Il tuo link di invito scadrà il {{date}} oppure dopo {{usesLeft}} utilizzi.',
Your_invite_link_will_expire_on__date__: 'Il tuo link di invito scadrà il {{date}}.',
Your_invite_link_will_never_expire: 'Il tuo link di invito non scadrà mai.',
+ Your_workspace: 'Il tuo workspace',
+ Your_password_is: 'La tua password è',
Version_no: 'Versione: {{version}}',
You_will_not_be_able_to_recover_this_message: 'Non sarai in grado di ripristinare questo messaggio!',
+ You_will_unset_a_certificate_for_this_server: 'Rimuoverai un certificato per questo server',
Change_Language: 'Cambia lingua',
Crash_report_disclaimer: 'Non registreremo mai il contenuto delle tue chat. Il crash report contiene solo informazioni necessarie per l\'identificazione e la risoluzione dei problemi.',
Type_message: 'Scrivi messaggio',
@@ -470,10 +617,47 @@ export default {
New_room: 'Nuova stanza',
Upload_room: 'Carica nella stanza',
Search_messages: 'Cerca messaggi',
- Scroll_messages: 'Scroll messaggi',
+ Scroll_messages: 'Scorri i messaggi',
Reply_latest: 'Rispondi all\'ultimo',
+ Reply_in_Thread: 'Rispondi nella discussione',
Server_selection: 'Selezione server',
Server_selection_numbers: 'Selezione server 1...9',
Add_server: 'Aggiungi server',
- New_line: 'Nuova linea'
+ New_line: 'Nuova linea',
+ You_will_be_logged_out_of_this_application: 'Verrai disconnesso da questa applicazione.',
+ Clear: 'Cancella',
+ This_will_clear_all_your_offline_data: 'Questo cancellerà tutti i tuoi dati offline.',
+ This_will_remove_all_data_from_this_server: 'Questo rimuoverà tutti i dati dal server.',
+ Mark_unread: 'Segna come non letto',
+ Wait_activation_warning: 'Prima di poter accedere, il tuo account deve essere attivato manualmente da un amministratore.',
+ Screen_lock: 'Blocco schermo',
+ Local_authentication_biometry_title: 'Autenticazione',
+ Local_authentication_biometry_fallback: 'Usa passcode',
+ Local_authentication_unlock_option: 'Sblocca con Passcode',
+ Local_authentication_change_passcode: 'Cambia Passcode',
+ Local_authentication_info: 'Nota: se dimentichi il Passcode, dovrai cancellare e installare nuovamente l\'app.',
+ Local_authentication_facial_recognition: 'riconoscimento facciale',
+ Local_authentication_fingerprint: 'impronta digitale',
+ Local_authentication_unlock_with_label: 'Sblocca con {{label}}',
+ Local_authentication_auto_lock_60: 'Dopo 1 minuto',
+ Local_authentication_auto_lock_300: 'Dopo 5 minuti',
+ Local_authentication_auto_lock_900: 'Dopo 15 minuti',
+ Local_authentication_auto_lock_1800: 'Dopo 30 minuti',
+ Local_authentication_auto_lock_3600: 'Dopo 1 ora',
+ Passcode_enter_title: 'Inserisci il passcode',
+ Passcode_choose_title: 'Scegli il tuo nuovo passcode',
+ Passcode_choose_confirm_title: 'Conferma il tuo nuovo passcode',
+ Passcode_choose_error: 'I passcode non corrispondono. Riprova.',
+ Passcode_choose_force_set: 'Passcode richiesto dall\'admin',
+ Passcode_app_locked_title: 'App bloccata',
+ Passcode_app_locked_subtitle: 'Riprova tra {{timeLeft}} secondi',
+ After_seconds_set_by_admin: 'Dopo {{seconds}} secondi (impostati dall\'admin)',
+ Dont_activate: 'Non attivare ora',
+ Queued_chats: 'Chat in coda',
+ Queue_is_empty: 'La coda è vuota',
+ Logout_from_other_logged_in_locations: 'Disconnetti da altre postazioni',
+ You_will_be_logged_out_from_other_locations: 'Verrai disconnesso dalle altre postazioni.',
+ Logged_out_of_other_clients_successfully: 'Disconnesso dalle altre postazioni con successo',
+ Logout_failed: 'Disconnessione fallita!',
+ Log_analytics_events: 'Invia statistiche anonime'
};
diff --git a/app/i18n/locales/ja.js b/app/i18n/locales/ja.js
index b3cc25a54..5ccad587b 100644
--- a/app/i18n/locales/ja.js
+++ b/app/i18n/locales/ja.js
@@ -436,8 +436,6 @@ export default {
Tap_to_view_servers_list: 'タップしてサーバーリストを見る',
Terms_of_Service: ' 利用規約 ',
Theme: 'テーマ',
- The_URL_is_invalid:
- '不正なURLか、セキュアな接続を確立できませんでした。\n{{contact}}',
There_was_an_error_while_action: '{{action}}の最中にエラーが発生しました!',
This_room_is_blocked: 'このルームはブロックされています。',
This_room_is_read_only: 'このルームは読み取り専用です。',
diff --git a/app/i18n/locales/nl.js b/app/i18n/locales/nl.js
index 39cecb99c..14959c7b6 100644
--- a/app/i18n/locales/nl.js
+++ b/app/i18n/locales/nl.js
@@ -398,7 +398,6 @@ export default {
Tap_to_view_servers_list: 'Tik om een server lijst te weergeven',
Terms_of_Service: ' Servicevoorwaarden ',
Theme: 'Thema',
- The_URL_is_invalid: 'Ongeldige URL of niet mogelijk een veilige verbinding op te zetten.\n{{contact}}',
There_was_an_error_while_action: 'Er was eer fout tijdens {{action}}!',
This_room_is_blocked: 'Deze kamer is geblokkeerd',
This_room_is_read_only: 'Deze kamer is alleen-lezen',
diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js
index 8ed4e76da..9b1c7d861 100644
--- a/app/i18n/locales/pt-BR.js
+++ b/app/i18n/locales/pt-BR.js
@@ -135,6 +135,10 @@ export default {
Change_language_loading: 'Alterando idioma.',
Call_already_ended: 'A chamada já terminou!',
Clear_cache_loading: 'Limpando cache.',
+ Clear_cookies_alert: 'Você quer limpar seus cookies?',
+ Clear_cookies_desc: 'Esta ação limpará todos os cookies de login permitindo que você faça login em outras contas.',
+ Clear_cookies_yes: 'Sim, limpar cookies',
+ Clear_cookies_no: 'Não, manter cookies',
Click_to_join: 'Clique para participar!',
Close: 'Fechar',
Close_emoji_selector: 'Fechar seletor de emojis',
@@ -175,6 +179,8 @@ export default {
deleting_room: 'excluindo sala',
Direct_Messages: 'Mensagens Diretas',
DESKTOP_OPTIONS: 'OPÇÕES DE ÁREA DE TRABALHO',
+ DESKTOP_NOTIFICATIONS: 'NOTIFICAÇÕES DE ÁREA DE TRABALHO',
+ Desktop_Alert_info: 'Essas notificações são entregues a você na área de trabalho',
Directory: 'Diretório',
description: 'descrição',
Description: 'Descrição',
@@ -186,17 +192,32 @@ export default {
Dont_Have_An_Account: 'Não tem uma conta?',
Do_you_have_an_account: 'Você tem uma conta?',
Do_you_really_want_to_key_this_room_question_mark: 'Você quer realmente {{key}} esta sala?',
+ E2E_How_It_Works_info1: 'Agora você pode criar grupos privados criptografados e mensagens diretas. Você também pode alterar grupos privados existentes ou DMs para criptografados.',
+ E2E_How_It_Works_info2: 'Esta é a criptografia *ponta a ponta*, portanto, a chave para codificar/decodificar suas mensagens e elas não serão salvas no servidor. Por esse motivo *você precisa armazenar esta senha em algum lugar seguro* que você pode acessar mais tarde se precisar.',
+ E2E_How_It_Works_info3: 'Se você continuar, será gerada automaticamente uma senha E2E.',
+ E2E_How_It_Works_info4: 'Você também pode configurar uma nova senha para sua chave de criptografia a qualquer momento em qualquer navegador em que tenha inserido a senha E2E existente.',
edit: 'editar',
edited: 'editado',
Edit: 'Editar',
Edit_Invite: 'Editar convite',
Edit_Status: 'Editar Status',
+ End_to_end_encrypted_room: 'Sala criptografada de ponta a ponta',
+ end_to_end_encryption: 'criptografia de ponta a ponta',
Email_or_password_field_is_empty: 'Email ou senha estão vazios',
Email: 'Email',
email: 'e-mail',
Empty_title: 'Título vazio',
+ Email_Notification_Mode_All: 'Cada Menção / Mensagem Direta',
+ Email_Notification_Mode_Disabled: 'Desativado',
Enable_Auto_Translate: 'Ativar a tradução automática',
Enable_notifications: 'Habilitar notificações',
+ Encrypted: 'Criptografado',
+ Encrypted_message: 'Mensagem criptografada',
+ Enter_Your_E2E_Password: 'Digite Sua Senha E2E',
+ Enter_Your_Encryption_Password_desc1: 'Isso permitirá que você acesse seus grupos privados e mensagens diretas criptografadas.',
+ Enter_Your_Encryption_Password_desc2: 'Você precisa inserir a senha para codificar/decodificar mensagens em todos os lugares em que usar o chat.',
+ Encryption_error_title: 'Sua senha de criptografia parece errada',
+ Encryption_error_desc: 'Não foi possível decodificar sua chave de criptografia para ser importada.',
Everyone_can_access_this_channel: 'Todos podem acessar este canal',
Error_uploading: 'Erro subindo',
Expiration_Days: 'Expira em (dias)',
@@ -310,6 +331,9 @@ export default {
Not_RC_Server: 'Este não é um servidor Rocket.Chat.\n{{contact}}',
No_available_agents_to_transfer: 'Nenhum agente disponível para transferência',
Offline: 'Offline',
+ Omnichannel: 'Omnichannel',
+ Open_Livechats: 'Bate-papos em Andamento',
+ Omnichannel_enable_alert: 'Você não está disponível no Omnichannel. Você quer ficar disponível?',
Oops: 'Ops!',
Onboarding_description: 'Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.',
Onboarding_join_workspace: 'Entre numa workspace',
@@ -399,6 +423,10 @@ export default {
saving_profile: 'salvando perfil',
saving_settings: 'salvando configurações',
saved_to_gallery: 'Salvo na galeria',
+ Save_Your_E2E_Password: 'Salve sua senha E2E',
+ Save_Your_Encryption_Password: 'Salve Sua Senha de Criptografia',
+ Save_Your_Encryption_Password_warning: 'Esta senha não é armazenada em nenhum lugar, portanto, salve-a com cuidado em outro lugar.',
+ Save_Your_Encryption_Password_info: 'Observe que se você perder sua senha, não há como recuperá-la e você perderá o acesso às suas mensagens.',
Search_Messages: 'Buscar Mensagens',
Search: 'Buscar',
Search_by: 'Buscar por',
@@ -447,7 +475,6 @@ export default {
Theme: 'Tema',
The_user_wont_be_able_to_type_in_roomName: 'O usuário não poderá digitar em {{roomName}}',
The_user_will_be_able_to_type_in_roomName: 'O usuário poderá digitar em {{roomName}}',
- The_URL_is_invalid: 'A URL fornecida é inválida ou incapaz de estabelecer uma conexão segura.\n{{contact}}',
There_was_an_error_while_action: 'Aconteceu um erro {{action}}!',
This_room_is_blocked: 'Este quarto está bloqueado',
This_room_is_read_only: 'Este quarto é apenas de leitura',
@@ -508,6 +535,7 @@ export default {
You_are_offline: 'Você está offline',
You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`',
Your_message: 'Sua mensagem',
+ You_need_to_verifiy_your_email_address_to_get_notications: 'Você precisa confirmar seu endereço de e-mail para obter notificações',
You_colon: 'Você: ',
you_were_mentioned: 'você foi mencionado',
You_were_removed_from_channel: 'Você foi removido de {{channel}}',
@@ -524,7 +552,7 @@ export default {
Write_External_Permission_Message: 'Rocket.Chat precisa de acesso à sua galeria para salvar imagens',
Write_External_Permission: 'Acesso à Galeria',
Yes: 'Sim',
- Crash_report_disclaimer: 'Nós não rastreamos o conteúdo das suas conversas. O relatório de erros apenas contém informações relevantes para identificarmos problemas e corrigí-los.',
+ Crash_report_disclaimer: 'Nós não rastreamos o conteúdo das suas conversas. O relatório de erros e os eventos do analytics apenas contém informações relevantes para identificarmos problemas e corrigí-los.',
Type_message: 'Digitar mensagem',
Room_search: 'Busca de sala',
Room_selection: 'Selecionar sala 1...9',
@@ -569,5 +597,10 @@ export default {
After_seconds_set_by_admin: 'Após {{seconds}} segundos (Configurado pelo adm)',
Dont_activate: 'Não ativar agora',
Queued_chats: 'Bate-papos na fila',
- Queue_is_empty: 'A fila está vazia'
+ Queue_is_empty: 'A fila está vazia',
+ Logout_from_other_logged_in_locations: 'Sair de outros locais logados',
+ You_will_be_logged_out_from_other_locations: 'Você perderá a sessão de outros clientes',
+ Logged_out_of_other_clients_successfully: 'Desconectado de outros clientes com sucesso',
+ Logout_failed: 'Falha ao desconectar!',
+ Log_analytics_events: 'Logar eventos no analytics'
};
diff --git a/app/i18n/locales/pt-PT.js b/app/i18n/locales/pt-PT.js
index a702951bd..50448ef22 100644
--- a/app/i18n/locales/pt-PT.js
+++ b/app/i18n/locales/pt-PT.js
@@ -301,7 +301,6 @@ export default {
tap_to_change_status: 'toque para alterar o estado',
Tap_to_view_servers_list: 'Toque para ver a lista de servidores',
Terms_of_Service: ' Termos do Serviço ',
- The_URL_is_invalid: 'O URL que você inseriu é inválido. Verifique e tente novamente, por favor!',
There_was_an_error_while_action: 'Houve um erro enquanto {{action}}!',
This_room_is_blocked: 'Esta sala está bloqueada',
This_room_is_read_only: 'Esta sala é apenas de leitura',
diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js
index 53579a7cd..15d5cde09 100644
--- a/app/i18n/locales/ru.js
+++ b/app/i18n/locales/ru.js
@@ -371,7 +371,6 @@ export default {
Tap_to_view_servers_list: 'Нажмите, чтобы просмотреть список серверов',
Terms_of_Service: ' Условия использования ',
Theme: 'Тема',
- The_URL_is_invalid: 'IНеверный URL или невозможно установить безопасное соединение.\n{{contact}}',
There_was_an_error_while_action: 'Произошла ошибка в процессе {{action}}!',
This_room_is_blocked: 'Этот канал заблокирован',
This_room_is_read_only: 'Этот канал доступен только для чтения',
diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js
index 068125219..3ec736e62 100644
--- a/app/i18n/locales/zh-CN.js
+++ b/app/i18n/locales/zh-CN.js
@@ -1,108 +1,123 @@
export default {
'1_person_reacted': '1 人回复了',
'1_user': '1 位用户',
- 'error-action-not-allowed': '不允许 {{action}}',
- 'error-application-not-found': '没有找到应用',
- 'error-archived-duplicate-name': '已存在名为 {{room_name}} 的已归档频道',
- 'error-avatar-invalid-url': '头像地址不合法: {{url}}',
- 'error-avatar-url-handling': '将 {{username}} 的头像设置为URL({{url}})时发送了错误',
- 'error-cant-invite-for-direct-room': '私人对话中不能邀请其他用户',
- 'error-could-not-change-email': 'email 不能修改',
- 'error-could-not-change-name': 'name 不能修改',
- 'error-could-not-change-username': 'username 不能修改',
- 'error-delete-protected-role': '不能删除被保护角色',
- 'error-department-not-found': '部门没有找到',
- 'error-direct-message-file-upload-not-allowed': '私人对话中不能分享文件',
- 'error-duplicate-channel-name': '已经存在名为 {{channel_name}} 的频道',
- 'error-email-domain-blacklisted': '邮箱域名已被禁止',
- 'error-email-send-failed': '正尝试将错误发送到email: {{message}}',
+ 'error-action-not-allowed': '{{action}} 不允許',
+ 'error-application-not-found': '找不到应用程式',
+ 'error-archived-duplicate-name': '已有一个名为「{{room_name}}」的封存频道',
+ 'error-avatar-invalid-url': '无效的头像网址:{{url}}',
+ 'error-avatar-url-handling': '错误,无法将 {{username}} 的头像设置为URL({{url}})',
+ 'error-cant-invite-for-direct-room': '无法邀请使用者进入私讯',
+ 'error-could-not-change-email': '无法更改电子邮件',
+ 'error-could-not-change-name': '无法更改名称',
+ 'error-could-not-change-username': '无法更改使用者名称',
+ 'error-could-not-change-status': '无法更改状态',
+ 'error-delete-protected-role': '无法删除受保护的角色',
+ 'error-department-not-found': '找不到部门',
+ 'error-direct-message-file-upload-not-allowed': '私人对话中不允许档案分享',
+ 'error-duplicate-channel-name': '名为「{{channel_name}}」的频道已存在',
+ 'error-email-domain-blacklisted': '电子邮件网域被禁用',
+ 'error-email-send-failed': '尝试发送电子邮件时出错:{{message}}',
+ 'error-save-image': '错误,无法保存图片',
+ 'error-save-video': '错误,无法保存視頻',
'error-field-unavailable': '{{field}} 已被使用 :(',
'error-file-too-large': '文件太大',
- 'error-importer-not-defined': 'importer 定义不正确, Import class 丢失.',
+ 'error-importer-not-defined': '没有正确定义,它缺少汇入类型',
'error-input-is-not-a-valid-field': '{{input}} 不是合法的 {{field}}',
- 'error-invalid-actionlink': '无效的 action link',
+ 'error-invalid-actionlink': '无效的操作链接',
'error-invalid-arguments': '无效的参数',
'error-invalid-asset': '无效的资源',
'error-invalid-channel': '无效的频道',
- 'error-invalid-channel-start-with-chars': '无效的频道. 已 @ 或 # 开始',
- 'error-invalid-custom-field': '无效的自定义字段',
- 'error-invalid-custom-field-name': '无效的自定义字段名. 只能包含 字母, 数字, 中线(-) and 下划线(_).',
+ 'error-invalid-channel-start-with-chars': '无效的频道,请以 @ 或 # 开头',
+ 'error-invalid-custom-field': '无效的自订字段',
+ 'error-invalid-custom-field-name': '无效的自订字段名. 只能包含字母、数字、中线(-)及底线(_).',
'error-invalid-date': '无效的日期',
'error-invalid-description': '无效的描述',
'error-invalid-domain': '无效的域名',
- 'error-invalid-email': '无效的 email {{emai}}',
- 'error-invalid-email-address': '无效的 email 地址',
+ 'error-invalid-email': '无效的电子邮件{{emai}}',
+ 'error-invalid-email-address': '无效的邮件地址',
'error-invalid-file-height': '无效的文件长度',
'error-invalid-file-type': '无效的文件类型',
'error-invalid-file-width': '无效的文件宽度',
- 'error-invalid-from-address': '你从该地址通知了一个无效的人.',
- 'error-invalid-integration': '无效的 integration',
- 'error-invalid-message': '无效的 message',
- 'error-invalid-method': '无效的 method',
- 'error-invalid-name': '无效的 name',
- 'error-invalid-password': '无效的 password',
- 'error-invalid-redirectUri': '无效的 redirectUri',
+ 'error-invalid-from-address': '无效的地址',
+ 'error-invalid-integration': '无效的整合',
+ 'error-invalid-message': '无效的信息',
+ 'error-invalid-method': '无效的方法',
+ 'error-invalid-name': '无效的名称',
+ 'error-invalid-password': '无效的密码',
+ 'error-invalid-redirectUri': '无效的转址',
'error-invalid-role': '无效的角色',
- 'error-invalid-room': '无效的房间',
- 'error-invalid-room-name': '{{room_name}} 不是合法的房间名',
- 'error-invalid-room-type': '{{type}} 不是合法的房间类型.',
+ 'error-invalid-room': '无效的聊天室',
+ 'error-invalid-room-name': '{{room_name}} 不是合法的聊天室名称',
+ 'error-invalid-room-type': '{{type}} 不是合法的聊天室类型.',
'error-invalid-settings': '无效的设置',
'error-invalid-subscription': '无效的订阅',
'error-invalid-token': '无效的 token',
- 'error-invalid-triggerWords': '无效的 triggerWords',
- 'error-invalid-urls': '无效的 URLs',
- 'error-invalid-user': '无效的 user',
- 'error-invalid-username': '无效的 username',
- 'error-invalid-webhook-response': 'webhook 地址响应状态不是200',
- 'error-message-deleting-blocked': '消息删除被阻塞',
- 'error-message-editing-blocked': '消息编辑被阻塞',
- 'error-message-size-exceeded': '消息大小超过了最大允许大小',
- 'error-missing-unsubscribe-link': '您必须提供[取消订阅]链接.',
+ 'error-invalid-triggerWords': '无效的关键字',
+ 'error-invalid-urls': '无效的网址',
+ 'error-invalid-user': '无效的使用者',
+ 'error-invalid-username': '无效的使用者名称',
+ 'error-invalid-webhook-response': 'webhook 网址以200以外的状态响应',
+ 'error-message-deleting-blocked': '信息删除已停用',
+ 'error-message-editing-blocked': '信息编辑已停用',
+ 'error-message-size-exceeded': '信息大小超出上限',
+ 'error-missing-unsubscribe-link': '您必须提供[取消订阅]链接。',
'error-no-tokens-for-this-user': '这个用户没有Token',
'error-not-allowed': '不允许',
'error-not-authorized': '未授权',
- 'error-push-disabled': 'Push 被禁用',
+ 'error-push-disabled': '推播已停用',
'error-remove-last-owner': '这是最后一个所有者。请在删除这个之前设置一个新所有者。',
- 'error-role-in-use': '无法删除角色,因为它在使用中',
- 'error-role-name-required': '角色名称是必需的',
- 'error-the-field-is-required': '字段 {{field}} 是必需的。',
+ 'error-role-in-use': '无法删除正在使用中的角色',
+ 'error-role-name-required': '角色名称是必須的',
+ 'error-the-field-is-required': '字段 {{field}} 是必須的。',
'error-too-many-requests': '错误,请求太多。请慢一点。在再次尝试之前,必须等待{{seconds}}秒。',
'error-user-is-not-activated': '用户未被激活',
- 'error-user-has-no-roles': '用户没有角色',
+ 'error-user-has-no-roles': '用户未设定角色',
'error-user-limit-exceeded': '尝试邀请到 #channel_name 的用户数量超过了管理员设置的限制',
- 'error-user-not-in-room': '用户不在这个房间',
- 'error-user-registration-custom-field': 'error-user-registration-custom-field',
- 'error-user-registration-disabled': '禁用用户注册',
- 'error-user-registration-secret': '用户注册只允许通过私密URL',
- 'error-you-are-last-owner': '你是最后一个所有者。请在离开房间前安排好新主人。',
+ 'error-user-not-in-room': '用户不在这个聊天室',
+ 'error-user-registration-custom-field': '无效的自订注册栏位',
+ 'error-user-registration-disabled': '已停用用户注册',
+ 'error-user-registration-secret': '只能透过加密网址进行用戶注册',
+ 'error-you-are-last-owner': '您是最后的拥有者。请删除此人之前设置一个新的拥有者。',
Actions: '操作',
- activity: '活动',
- Activity: '按活动排序',
- Add_Reaction: '增加回复',
- Add_Server: '添加服务器',
- Add_users: '添加用户',
+ activity: '活动时间',
+ Activity: '按活动时间排序',
+ Add_Reaction: '增加表情貼',
+ Add_Server: '創建服务器',
+ Add_users: '創建用户',
+ Admin_Panel: '仪表板',
+ Agent: '代理',
Alert: '警告',
alert: '警告',
alerts: '警告',
All_users_in_the_channel_can_write_new_messages: '频道中的所有用户都可以发送新消息',
+ A_meaningful_name_for_the_discussion_room: '取一个有意义的讨论区的名称',
All: '所有',
- Allow_Reactions: '允许的回复',
- Alphabetical: '字母顺序排列',
+ All_Messages: '全部信息',
+ Allow_Reactions: '允许表情贴',
+ Alphabetical: '按名称排列',
and_more: '和更多的',
and: '和',
announcement: '公告',
Announcement: '公告',
- ARCHIVE: '存档',
- archive: '存档',
+ Apply_Your_Certificate: '使用自己的凭证',
+ Applying_a_theme_will_change_how_the_app_looks: '套用主题将会改变 App 的外观',
+ ARCHIVE: '封存',
+ archive: '封存',
are_typing: '正在输入',
Are_you_sure_question_mark: '你确定吗?',
- Are_you_sure_you_want_to_leave_the_room: '你确定要离开房间 {{room}} 吗?',
+ Are_you_sure_you_want_to_leave_the_room: '你确定要离开聊天室 {{room}} 吗?',
+ Audio: '音讯',
Authenticating: '正在验证身份',
+ Automatic: '自动',
+ Auto_Translate: '自动翻译',
Avatar_changed_successfully: '头像更新成功!',
Avatar_Url: '头像地址',
Away: '离开',
- Block_user: '阻止用户',
- Broadcast_channel_Description: '只有经过授权的用户才能写新消息,但其他用户可以回复',
+ Back: '返回',
+ Black: '黑色',
+ Block_user: '封锁用户',
+ Browser: '浏览器',
+ Broadcast_channel_Description: '只有经过授权的用户才能写新信息,但其他用户可以回复',
Broadcast_Channel: '广播频道',
Busy: '忙碌',
By_proceeding_you_are_agreeing: '继续操作,请同意我们的',
@@ -111,234 +126,537 @@ export default {
Cancel: '取消',
changing_avatar: '更改头像',
creating_channel: '创建频道',
+ creating_invite: '创建邀请',
Channel_Name: '频道名',
Channels: '频道',
Chats: '聊天',
+ Call_already_ended: '通话已经结束!',
+ Clear_cookies_alert: '是否清除所有 cookies?',
+ Clear_cookies_desc: '本操作将清除所有登入 cookies,以登入其他帐号',
+ Clear_cookies_yes: '是,清除 cookies',
+ Clear_cookies_no: '否,保留 cookies',
+ Click_to_join: '点击以参与',
Close: '关闭',
Close_emoji_selector: '关闭emoji选择器',
+ Closing_chat: '结束聊天',
+ Change_language_loading: '切换语言',
+ Chat_closed_by_agent: '聊天已被客服关闭',
Choose: '选择',
Choose_from_library: '选择库',
+ Choose_file: '选择文件',
+ Choose_where_you_want_links_be_opened: '请选择您要将链接开启在',
Code: '代码',
+ Code_or_password_invalid: '验证码或密码不正确',
Collaborative: '协作',
Confirm: '确认',
Connect: '连接',
Connected: '已连接',
+ connecting_server: '连接至服务器',
Connecting: '连接中',
+ Contact_us: '联系我们',
+ Contact_your_server_admin: '请联络系统管理员',
Continue_with: '继续采用',
Copied_to_clipboard: '复制到剪贴板',
Copy: '复制',
+ Conversation: '对话',
Permalink: '永久链接',
+ Certificate_password: '凭证密码',
+ Clear_cache: '清除本机资料',
+ Clear_cache_loading: '清除快取',
+ Whats_the_password_for_your_certificate: '您的凭证密码是?',
Create_account: '创建账户',
Create_Channel: '创建频道',
+ Create_Direct_Messages: '新增私人讯息',
+ Create_Discussion: '新增论坛',
+ Created_snippet: '新增程式码片段',
Create_a_new_workspace: '创建一个新的工作区',
Create: '创建',
- Delete_Room_Warning: '删除一个房间将删除房间内的所有消息。这是不可逆转的.',
+ Custom_Status: '自订状态',
+ Dark: '深色',
+ Dark_level: '深色程度',
+ Default: '默認',
+ Default_browser: '预设浏览器',
+ Delete_Room_Warning: '删除聊天室将一同删除聊天室内的所有消息。这是不可逆转的.',
+ Department: '部门',
delete: '删除',
Delete: '删除',
DELETE: '删除',
+ deleting_room: '正在删除聊天室',
description: '描述',
Description: '描述',
- Disable_notifications: '禁用消息通知',
- Direct_Messages: '私信',
+ DESKTOP_OPTIONS: '桌面选项',
+ DESKTOP_NOTIFICATIONS: '桌面通知',
+ Desktop_Alert_info: '这些通知将发送至桌面',
+ Directory: '目录',
+ Direct_Messages: '私訊',
+ Disable_notifications: '禁用信息通知',
+ Discussions: '论坛',
+ Discussion_Desc: '帮助保持事态更新! 通过创建讨论,一个和所选频道双向关联的子频道将会被创建。',
+ Discussion_name: '论坛名称',
+ Done: '完毕',
Dont_Have_An_Account: '还没有账号?',
- Do_you_really_want_to_key_this_room_question_mark: '你真的想要{{key}}这个房间吗?',
+ Do_you_have_an_account: '是否拥有帐号?',
+ Do_you_have_a_certificate: '是否拥有凭证?',
+ Do_you_really_want_to_key_this_room_question_mark: '您真的想要{{key}}这个聊天室吗?',
+ E2E_How_It_Works_info1: '您现在可以创建加密的专用组和直接消息。您也可以将现有的私人组或直接信息加密。',
+ E2E_How_It_Works_info2: '这是端到端加密,因此编码/解码邮件的密钥不会保存在服务器上。因此,您需要将密码存储在安全的地方。您需要在希望使用端到端加密的其他设备上输入。',
+ E2E_How_It_Works_info3: '如果继续,将自动生成一组 E2E 密码',
+ E2E_How_It_Works_info4: '这是系统自动生成的密码,您可以为您的密钥设置一个新密码(您可以随时从任何浏览器输入现有密码)。',
edit: '编辑',
- deleting_room: '正抹去房间',
+ edited: '已编辑',
Edit: '编辑',
+ Edit_Status: '编辑状态',
+ Edit_Invite: '编辑邀请',
+ End_to_end_encrypted_room: '端到端加密聊天室',
+ end_to_end_encryption: '端到端加密',
+ Email_Notification_Mode_All: '每次被提及或私讯',
+ Email_Notification_Mode_Disabled: '禁用',
Email_or_password_field_is_empty: '邮件或密码字段为空',
Email: '邮箱',
+ EMAIL: 'EMAIL',
email: '邮箱',
- Enable_notifications: '开启消息通知',
- Everyone_can_access_this_channel: '每个人都可以进入这个频道',
+ Empty_title: '空白标题',
+ Enable_Auto_Translate: '开启自动翻译',
+ Enable_notifications: '开启信息通知',
+ Encrypted: '已加密',
+ Encrypted_message: '加密信息',
+ Enter_Your_E2E_Password: '输入您的 E2E 密码',
+ Enter_Your_Encryption_Password_desc1: '这将会允许您存取您的加密私人群组和私訊',
+ Enter_Your_Encryption_Password_desc2: '您需要在任何使用此聊天的平台输入密码,以编码/解码您的信息',
+ Encryption_error_title: '您的加密密码似乎有误',
+ Encryption_error_desc: '无法使用汇入的加密密钥来解密',
+ Everyone_can_access_this_channel: '每个人都可以访问此频道',
Error_uploading: '错误上传',
- Favorites: '最喜欢的',
+ Expiration_Days: '到期 (日)',
+ Favorite: '收藏',
+ Favorites: '收藏',
Files: '文件',
+ File_description: '文件描述',
+ File_name: '文件名称',
Finish_recording: '完成录制',
- For_your_security_you_must_enter_your_current_password_to_continue: '为了安全起见,您必须输入当前密码才能继续',
- Forgot_password_If_this_email_is_registered: '如果这封邮件已注册,我们将发送如何重置密码的说明。如果您没有立即收到电子邮件,请回来再试一次。',
+ Following_thread: '追踪的讨论串',
+ For_your_security_you_must_enter_your_current_password_to_continue: '出于安全考虑,您必须输入您的密码以便继续操作',
+ Forgot_password_If_this_email_is_registered: '如果这封邮件已注册,我们将发送如何重置密码的说明。如果您没有立即收到电子邮件,请再试一次。',
Forgot_password: '忘记密码',
Forgot_Password: '忘记密码',
- Group_by_favorites: '按喜好分组',
+ Forward: '转发',
+ Forward_Chat: '转发聊天',
+ Forward_to_department: '转发到部门',
+ Forward_to_user: '转发给用戶',
+ Full_table: '点击以查看完整表格',
+ Generate_New_Link: '产生新的链接',
+ Group_by_favorites: '收藏优先',
Group_by_type: '按类型分组',
+ Hide: '隐藏',
Has_joined_the_channel: '已加入频道',
+ Has_joined_the_conversation: '已经加入此对话',
Has_left_the_channel: '已离开频道',
- I_have_an_account: '我有帐户',
- Invisible: '看不见的',
+ Hide_System_Messages: '隐藏系统信息',
+ Hide_type_messages: '隐藏 "{{type}}" 信息',
+ How_It_Works: '运作方式',
+ Message_HideType_uj: '隐藏“用戶加入”信息',
+ Message_HideType_ul: '隐藏“用戶离开”信息',
+ Message_HideType_ru: '隐藏“用戶已删除”信息',
+ Message_HideType_au: '隐藏“用戶已增加”信息',
+ Message_HideType_mute_unmute: '隐藏“用戶静音/取消静音”信息',
+ Message_HideType_r: '隐藏“房间名称已更改”的信息',
+ Message_HideType_ut: '隐藏“用戶已加入对话”的信息',
+ Message_HideType_wm: '隐藏“欢迎”的信息',
+ Message_HideType_rm: '隐藏“已删除信息”的信息',
+ Message_HideType_subscription_role_added: '隐藏“已设置角色”的信息',
+ Message_HideType_subscription_role_removed: '隐藏“不再定义的角色”的信息',
+ Message_HideType_room_archived: '隐藏“聊天室已封存”的信息',
+ Message_HideType_room_unarchived: '隐藏“聊天室未封存”的信息',
+ I_Saved_My_E2E_Password: '保存我的 E2E 密码',
+ IP: 'IP',
+ In_app: 'App 内',
+ IN_APP_AND_DESKTOP: 'App 内及桌面',
+ In_App_and_Desktop_Alert_info: '打开应用程序时,在屏幕顶部显示横幅,并在桌面上显示通知',
+ Invisible: '隐身',
Invite: '邀请',
is_a_valid_RocketChat_instance: '是一个有效的 Rocket.Chat 实例',
is_not_a_valid_RocketChat_instance: '不是有效的 Rocket.Chat 实例',
is_typing: '正在输入',
+ Invalid_or_expired_invite_token: '无效或到期的邀请 token',
+ Join_your_workspace: '加入您的工作区',
+ Invite_Link: '邀请链接',
+ Invite_users: '邀请用戶',
Join: '加入',
- Just_invited_people_can_access_this_channel: '刚被邀请的人可以进入这个频道',
+ Join_our_open_workspace: '加入开放工作区',
+ Just_invited_people_can_access_this_channel: '仅有被邀请的人可以进入这个频道',
Language: '语言',
- last_message: '最后一条消息',
+ last_message: '最后一条信息',
Leave_channel: '离开频道',
- leaving_room: '离开房间',
+ leaving_room: '离开聊天室',
leave: '离开',
Legal: '合法',
+ Light: '浅色',
+ License: '授权条款',
Livechat: '即时聊天',
- Login: '登录',
+ Livechat_edit: '即时聊天编辑',
+ Login: '登陆',
Login_error: '你的凭证被拒绝了! 请再试一次',
Login_with: '登陆为',
+ Logging_out: '正在登出',
Logout: '注销',
+ Max_number_of_uses: '最大使用次数',
+ Max_number_of_users_allowed_is_number: '允许使用者上限数量',
+ members: '成员',
Members: '成员',
Mentioned_Messages: '提到的信息',
mentioned: '提到',
Mentions: '提到',
Message_accessibility: '{{time}}来自{{user}}的消息: {{message}}',
- Message_actions: '消息操作',
- Message_pinned: '消息被钉住',
- Message_removed: '消息被删除',
- Messages: '消息',
- Microphone_Permission_Message: 'Rocket.Chat需要访问您的麦克风,以便您可以发送音频消息。',
+ Message_actions: '信息操作',
+ Message_pinned: '信息被钉选',
+ Message_removed: '信息被删除',
+ Message_starred: '信息被标注',
+ Message_unstarred: '信息被取消标注',
+ message: '信息',
+ messages: '信息',
+ Message: '信息',
+ Messages: '信息',
+ Message_Reported: '信息已举报',
+ Microphone_Permission_Message: 'Rocket.Chat需要存取您的麦克风,以便发送音频信息。',
Microphone_Permission: '麦克风授权',
Mute: '静音',
muted: '被静音',
My_servers: '我的服务器',
N_people_reacted: '{{n}} 人回复',
N_users: '{{n}} 位用户',
- name: '名字',
- Name: '名字',
- New_in_RocketChat_question_mark: '还没有账号?',
- New_Message: '新消息',
+ name: '名称',
+ Name: '名称',
+ Navigation_history: '浏览历史记录',
+ Never: '从不',
+ New_Message: '新信息',
New_Password: '新密码',
New_Server: '新服务器',
Next: '下一步',
No_files: '没有文件',
+ No_limit: '没有限制',
No_mentioned_messages: '没有提到的信息',
- No_pinned_messages: '没有固定的消息',
- No_snippeted_messages: '没有代码片段的消息',
+ No_pinned_messages: '没有钉选的消息',
+ No_results_found: '没有搜寻结果',
No_starred_messages: '没有加星标的消息',
- No_Message: '没有消息',
- No_Reactions: '没有回复',
+ No_thread_messages: '没有讨论串信息',
+ No_label_provided: '没有提供 {{label}}',
+ No_Message: '没有信息',
+ No_messages_yet: '当前未有信息',
+ No_Reactions: '没有表情貼',
+ No_Read_Receipts: '没有已读人员',
Not_logged: '没有记录',
+ Not_RC_Server: '这不是一个 Rocket.Chat server.\\n{{contact}}',
+ Nothing: '没有东西',
Nothing_to_save: '什么都没有保存!',
- Notify_active_in_this_room: '通知这个房间的活跃用户',
- Notify_all_in_this_room: '通知这个房间的所有人',
+ Notify_active_in_this_room: '通知这个聊天室的活跃用户',
+ Notify_all_in_this_room: '通知这个聊天室的所有人',
+ Notifications: '通知',
+ Notification_Duration: '通知持续时间',
+ Notification_Preferences: '通知偏好设置',
+ No_available_agents_to_transfer: '没有可用的代理进行传输',
Offline: '离线',
Oops: '哎呀!',
+ Omnichannel: 'Omnichannel',
+ Open_Livechats: '打开即时聊天',
+ Omnichannel_enable_alert: '您尚未启用 Omnichannel,是否想要启用?',
+ Onboarding_description: '工作區是團隊或組織協作的空間。向工作区管理员询问要加入的地址或为您的团队创建一个。',
+ Onboarding_join_workspace: '加入一个工作区',
+ Onboarding_subtitle: '超越团队合作',
Onboarding_title: '欢迎来到 Rocket.Chat',
+ Onboarding_join_open_description: '加入我们的开放工作区以与 Rocket.Chat 团队及社群交谈',
+ Onboarding_agree_terms: '继续,即表示您同意 Rocket.Chat',
+ Onboarding_less_options: '较少选项',
+ Onboarding_more_options: '较多选项',
Online: '在线',
Only_authorized_users_can_write_new_messages: '只有经过授权的用户才能写新消息',
- Open_emoji_selector: '打开emoji选择器',
+ Open_emoji_selector: '打开 emoji 选择器',
Open_Source_Communication: '开源沟通',
+ Open_your_authentication_app_and_enter_the_code: '打开您的验证应用程式并输入代码。您也可以使用其中一个备用代码。',
+ OR: '或',
+ OS: '作业系统',
+ Overwrites_the_server_configuration_and_use_room_config: '覆写服务器设置和使用聊天室设置',
Password: '密码',
+ Parent_channel_or_group: '父频道或群组',
Permalink_copied_to_clipboard: '永久链接已复制到剪贴板!',
- Pin: '钉住',
- Pinned_Messages: '被钉的消息',
- pinned: '被钉住',
- Pinned: '被钉',
+ Phone: '电话',
+ Pin: '钉选',
+ Pinned_Messages: '钉选信息',
+ pinned: '已被钉选',
+ Pinned: '被钉选',
+ Please_add_a_comment: '请增加评论',
Please_enter_your_password: '请输入密码',
+ Please_wait: '请稍候',
+ Preferences: '偏好设置',
Preferences_saved: '偏好已保存!',
- Privacy_Policy: ' 隐私策略',
+ Privacy_Policy: '隐私政策',
Private_Channel: '私人频道',
Private_Groups: '私人群组',
Private: '私有的',
+ Processing: '处理中',
Profile_saved_successfully: '个人资料保存成功!',
Profile: '个人资料',
Public_Channel: '公共频道',
Public: '公共',
+ PUSH_NOTIFICATIONS: '推送通知',
+ Push_Notifications_Alert_Info: '这些通知将在未开启 App 时发送给您',
Quote: '引用',
- Reactions_are_disabled: '回复被禁用',
- Reactions_are_enabled: '回复被启用',
- Reactions: '回复',
+ Reactions_are_disabled: '表情貼被禁用',
+ Reactions_are_enabled: '表情貼被启用',
+ Reactions: '表情貼',
+ Read: '读取',
+ Read_External_Permission_Message: 'Rocket.Chat 需要存取您装置上的相片、多媒体及文件',
+ Read_External_Permission: '读取媒体权限',
Read_Only_Channel: '只读频道',
Read_Only: '只读',
+ Read_Receipt: '查看已读人员',
+ Receive_Group_Mentions: '接收群组提及',
+ Receive_Group_Mentions_Info: '接收@all和@here提及',
Register: '注册',
Repeat_Password: '重复输入密码',
+ Replied_on: '回覆在',
+ replies: '回覆',
+ reply: '回复',
Reply: '回复',
+ Report: '举报',
+ Receive_Notification: '接收通知',
+ Receive_notifications_from: '接收来自 {{name}} 的通知',
Resend: '重新发送',
Reset_password: '重置密码',
+ resetting_password: '正在重置密码',
RESET: '重置',
+ Return: '返回',
+ Review_app_title: '对此 App 满意吗?',
+ Review_app_desc: '请在 {{store}} 给予我们 5 星好评',
+ Review_app_yes: '没问题',
+ Review_app_no: '婉拒',
+ Review_app_later: '之后再说',
+ Review_app_unable_store: '无法开启 {{store}}',
+ Review_this_app: '评分此 App',
+ Remove: '移除',
Roles: '角色',
- Room_actions: '房间操作',
- Room_changed_announcement: '{{userBy}}将房间通知改为:{{announcement}}',
- Room_changed_description: '{{userBy}}将房间说明改为:{{description}}',
- Room_changed_privacy: '{{userBy}}将房间类型改为:{{type}}',
- Room_changed_topic: '{{userBy}}将房间主题改为:{{topic}}',
- Room_Files: '房间文件',
- Room_Info_Edit: '房间信息编辑',
- Room_Info: '房间信息',
- Room_Members: '房间成员',
- Room_name_changed: '{{userBy}}将房间名称改为:{{{name}}',
+ Room_actions: '聊天室操作',
+ Room_changed_announcement: '{{userBy}}将聊天室通知改为:{{announcement}}',
+ Room_changed_description: '{{userBy}}将聊天室说明改为:{{description}}',
+ Room_changed_privacy: '{{userBy}}将聊天室类型改为:{{type}}',
+ Room_changed_topic: '{{userBy}}将聊天室主题改为:{{topic}}',
+ Room_Files: '聊天室文件',
+ Room_Info_Edit: '聊天室信息编辑',
+ Room_Info: '聊天室信息',
+ Room_Members: '聊天室成员',
+ Room_name_changed: '{{userBy}} 将聊天室名称改为:{{{name}}',
SAVE: '保存',
Save_Changes: '保存更改',
Save: '保存',
- saving_preferences: '保存偏好',
+ Saved: '保存',
+ saving_preferences: '保存偏好設置',
saving_profile: '保存配置文件',
saving_settings: '保存设置',
- Search_Messages: '搜索消息',
+ saved_to_gallery: '储存至图片库',
+ Save_Your_E2E_Password: '储存您的端对端密码',
+ Save_Your_Encryption_Password: '储存您的加密密码',
+ Save_Your_Encryption_Password_warning: '此密码未被储存在任何地方,为此您必须安全存放您的密码',
+ Save_Your_Encryption_Password_info: '请记住,如果你遗失了您的密码,您将无法存取您的信息并不可恢复',
+ Search_Messages: '搜索信息',
Search: '搜索',
+ Search_by: '搜寻',
+ Search_global_users: '搜寻全域用户',
+ Search_global_users_description: '如果启用,您将可以搜寻其他公司、服务器上的任何用戶',
+ Seconds: '{{second}} 秒',
Select_Avatar: '选择头像',
+ Select_Server: '选择伺服器',
Select_Users: '选择用户',
+ Select_a_Channel: '选择一个频道',
+ Select_a_Department: '选择一个部门',
+ Select_an_option: '选择一个选项',
+ Select_a_User: '选择一个用戶',
Send: '发送',
Send_audio_message: '发送音频信息',
- Send_message: '发送消息',
+ Send_crash_report: '送出当机报告',
+ Send_message: '发送信息',
+ Send_me_the_code_again: '再次发送代码给我',
+ Send_to: '发送到',
+ Sending_to: '正发送到',
+ Sent_an_attachment: '发送附件',
Server: '服务器',
Servers: '服务器',
+ Server_version: '服务器版本',
Set_username_subtitle: '用户名用于允许其他人在邮件中提及您',
+ Set_custom_status: '设定自订状态',
+ Set_status: '设定状态',
+ Status_saved_successfully: '状态储存成功',
Settings: '设置',
Settings_succesfully_changed: '更改设置成功!',
Share: '分享',
+ Share_Link: '分享链接',
+ Share_this_app: '分享此 app',
+ Show_more: '显示更多',
+ Show_Unread_Counter: '显示未读信息数量',
+ Show_Unread_Counter_Info: '显示未读信息数量资讯',
Sign_in_your_server: '登录你的服务器',
Sign_Up: '注册',
Some_field_is_invalid_or_empty: '某些字段无效或为空',
Sorting_by: '按{{key}}排序',
- Star_room: '将房间标星',
- Star: '加星标',
- Starred_Messages: '加星标的消息',
- starred: '被加星标',
- Starred: '星标',
- Start_of_conversation: '开始交流',
+ Sound: '声音',
+ Star_room: '将聊天室标记',
+ Star: '标记',
+ Starred_Messages: '标记的信息',
+ starred: '被标记',
+ Starred: '标记',
+ Start_of_conversation: '开始对话',
+ Start_a_Discussion: '开始论坛',
+ Started_discussion: '已开始的论坛',
+ Started_call: '{{userBy}} 开始的通话',
Submit: '提交',
+ Table: '表格',
+ Tags: '标签',
Take_a_photo: '拍照',
+ Take_a_video: '录影',
+ Take_it: '拿去!',
tap_to_change_status: '点按即可更改状态',
Tap_to_view_servers_list: '点击查看服务器列表',
- Terms_of_Service: ' 服务条款 ',
- The_URL_is_invalid: '您输入的URL无效。请检查一下,再试一次!',
+ Terms_of_Service: '服务条款',
+ Theme: '布景主题',
+ The_user_wont_be_able_to_type_in_roomName: '此用户将无法在 {{roomName}} 中输入',
+ The_user_will_be_able_to_type_in_roomName: '此用户将可以在 {{roomName}} 中输入',
There_was_an_error_while_action: '{{action}}出现错误!',
- This_room_is_blocked: '这个房间被锁了',
- This_room_is_read_only: '这个房间是只读的',
+ This_room_is_blocked: '这个聊天室被锁了',
+ This_room_is_read_only: '这个聊天室是只读的',
+ Thread: '讨论串',
+ Threads: '讨论串',
Timezone: '时区',
+ To: '到',
topic: '主题',
Topic: '主题',
+ Translate: '翻译',
Try_again: '再试一次',
Two_Factor_Authentication: '双重认证',
Type_the_channel_name_here: '在这里输入频道名称',
- unarchive: '解除封存',
- UNARCHIVE: '解除封存',
+ unarchive: '取消封存',
+ UNARCHIVE: '取消封存',
Unblock_user: '解锁用户',
+ Unfavorite: '取消收藏',
+ Unfollowed_thread: '取消追踪讨论',
Unmute: '取消静音',
unmuted: '静音状态',
- Unpin: '拔掉',
- unread_messages: '未读',
- Unread: '未读的',
- Unread_on_top: '未读在上面',
- Unstar: '取消星号标记',
+ Unpin: '取消钉选',
+ unread_messages: '未读信息',
+ Unread: '未读',
+ Unread_on_top: '未读优先',
+ Unstar: '取消标记',
+ Updating: '正在更新',
Uploading: '正在上传',
Upload_file_question_mark: '上传文件?',
+ User: '用戶',
+ Users: '用戶',
User_added_by: '由{{userBy}}添加的用户 {{useradd}}',
+ User_Info: '用戶资讯',
User_has_been_key: '用户已被{{key}}!',
User_is_no_longer_role_by_: '{{userBy}}将角色 {{role}} 从用户 {{user}} 身上移除',
User_muted_by: '用户 {{userMuted}} 被 {{userBy}} 静音',
User_removed_by: '用户 {{userRemoved}} 被 {{userBy}} 移除',
+ User_sent_an_attachment: '{{user}} 寄送了一个附件',
User_unmuted_by: '用户 {{userUnmuted}} 被 {{userBy}} 取消静音',
User_was_set_role_by_: '用户 {{user}} 被 {{userBy}} 设置角色 {{role}}',
Username_is_empty: '用户名是空的',
Username: '用户名',
Username_or_email: '用户名或邮箱',
+ Uses_server_configuration: '使用服务器设置',
+ Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常, 一个讨论会由一个问题开始, 例如 \\"如何上传图片?\\"',
Validating: '正在验证',
+ Registration_Succeeded: '注册成功',
+ Verify: '验证',
+ Verify_email_title: '注册成功',
+ Verify_email_desc: '我们已经送出一封电子邮件,以确认您的注册。如果您没有很快收到,请再试一次。',
+ Verify_your_email_for_the_code_we_sent: '检查您的电子邮件以取得我们发送的代码',
Video_call: '视频电话',
+ View_Original: '检视原文',
Voice_call: '语音电话',
+ Waiting_for_network: '等待网路连接',
+ Websocket_disabled: 'Websocket 已于此伺服器上禁用。 \\n{{contact}}',
Welcome: '欢迎',
+ What_are_you_doing_right_now: '现在在做些什么?',
+ Whats_your_2fa: '您的 2FA 代码是?',
+ Without_Servers: '未连接至服务器',
+ Workspaces: '工作区',
+ Would_you_like_to_return_the_inquiry: '你想回覆询问吗?',
+ Write_External_Permission_Message: 'Rocket.Chat 需要您图片库的存取权限以储存图片。',
+ Write_External_Permission: '图片库权限',
+ Yes: '是',
Yes_action_it: '是的,{{action}}它!',
Yesterday: '昨天',
You_are_in_preview_mode: '您处于预览模式',
You_are_offline: '您处于离线状态',
You_can_search_using_RegExp_eg: '您可以使用RegExp进行搜索。 例如`/^text$/i`',
- You_colon: '你: ',
+ You_colon: '你:',
you_were_mentioned: '你被提到了',
- You_will_not_be_able_to_recover_this_message: '您将无法恢复此消息!',
+ You_were_removed_from_channel: '您已从 {{channel}} 中被踢除',
you: '你',
- Your_server: '你的服务器'
+ You: '你',
+ Logged_out_by_server: '服务器端已将你注销,请重新登入',
+ You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要访问至少一台Rocket.Chat服务器才能共享某些内容。',
+ You_need_to_verifiy_your_email_address_to_get_notications: '您需要先验证您的邮箱以启用通知',
+ Your_certificate: '你的证书',
+ Your_message: '你的信息',
+ Your_invite_link_will_expire_after__usesLeft__uses: '您的邀请链接将在{{usesLeft}}使用后到期。',
+ Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀请链接将于{{date}}或{{usesLeft}}使用后到期。',
+ Your_invite_link_will_expire_on__date__: '您的邀请链接将于{{date}}到期。',
+ Your_invite_link_will_never_expire: '您的邀请链接永久有效。',
+ Your_workspace: '您的工作区',
+ Your_password_is: '您的密码',
+ Version_no: '版本: {{version}}',
+ You_will_not_be_able_to_recover_this_message: '您将无法恢复此信息!',
+ You_will_unset_a_certificate_for_this_server: '您将取消此服务器的凭证设定',
+ Change_Language: '切换语言',
+ Crash_report_disclaimer: '我们从不追踪您的聊天内容。 崩溃报告和分析事件仅包含有关我们的信息,以便识别和修复问题。',
+ Type_message: '输入信息',
+ Room_search: '搜索聊天室',
+ Room_selection: '选择房间(输入 1...9)',
+ Next_room: '下一个聊天室',
+ Previous_room: '上一个聊天室',
+ New_room: '新聊天室',
+ Upload_room: '上传至聊天室',
+ Search_messages: '搜索信息',
+ Scroll_messages: '信息滚动',
+ Reply_latest: '回覆最新信息',
+ Reply_in_Thread: '回覆讨论',
+ Server_selection: '选择服务器',
+ Server_selection_numbers: '选择服务器(输入 1...9)',
+ Add_server: '創建服务器',
+ New_line: '新行',
+ You_will_be_logged_out_of_this_application: '您即将登出',
+ Clear: '清除',
+ This_will_clear_all_your_offline_data: '这将清除您的所有离线资料。',
+ This_will_remove_all_data_from_this_server: '这将从该服务器中删除所有数据。',
+ Mark_unread: '标记未读',
+ Wait_activation_warning: '您的帐号必须由管理员手动启用后才能登入。',
+ Screen_lock: '鎖屏',
+ Local_authentication_biometry_title: '验证',
+ Local_authentication_biometry_fallback: '使用通关密码',
+ Local_authentication_unlock_option: '以通关密码解锁',
+ Local_authentication_change_passcode: '变更通关密码',
+ Local_authentication_info: '注: 如果您忘记了通关密码,将需要移除并重新安装此 App',
+ Local_authentication_facial_recognition: '脸部辨识',
+ Local_authentication_fingerprint: '指纹辨识',
+ Local_authentication_unlock_with_label: '以 {{label}} 解锁',
+ Local_authentication_auto_lock_60: '1分钟后',
+ Local_authentication_auto_lock_300: '5分钟后',
+ Local_authentication_auto_lock_900: '15分钟后',
+ Local_authentication_auto_lock_1800: '半小时后',
+ Local_authentication_auto_lock_3600: '一小时后',
+ Passcode_enter_title: '请输入通关密码',
+ Passcode_choose_title: '请输入新通关密码',
+ Passcode_choose_confirm_title: '请确认新通关密码',
+ Passcode_choose_error: '不正确的通关密码,请再试一次',
+ Passcode_choose_force_set: '管理员设置必填',
+ Passcode_app_locked_title: 'App 已锁定',
+ Passcode_app_locked_subtitle: '{{timeLeft}} 秒后再进行尝试',
+ After_seconds_set_by_admin: '{{seconds}} 秒 (管理员设定)',
+ Dont_activate: '現在不要激活',
+ Queued_chats: '聊天队列',
+ Queue_is_empty: '队列是空的',
+ Logout_from_other_logged_in_locations: '注销其他已登陆的设备',
+ You_will_be_logged_out_from_other_locations: '您将于其他设备上注销',
+ Logged_out_of_other_clients_successfully: '成功登出其他客户端',
+ Logout_failed: '注销失败',
+ Log_analytics_events: '日志分析事件'
};
diff --git a/app/i18n/locales/zh-TW.js b/app/i18n/locales/zh-TW.js
new file mode 100644
index 000000000..9583245bb
--- /dev/null
+++ b/app/i18n/locales/zh-TW.js
@@ -0,0 +1,662 @@
+export default {
+ '1_person_reacted': '1 人回覆了',
+ '1_user': '1 位使用者',
+ 'error-action-not-allowed': '{{action}} 不允許',
+ 'error-application-not-found': '找不到應用程式',
+ 'error-archived-duplicate-name': '已有一個名為「{{room_name}}」的封存頻道',
+ 'error-avatar-invalid-url': '無效的大頭貼網址:{{url}}',
+ 'error-avatar-url-handling': '錯誤,無法將 {{username}} 的大頭貼設置為URL({{url}})',
+ 'error-cant-invite-for-direct-room': '無法邀請使用者進入私訊',
+ 'error-could-not-change-email': '無法更改電子郵件',
+ 'error-could-not-change-name': '無法更改名稱',
+ 'error-could-not-change-username': '無法更改使用者名稱',
+ 'error-could-not-change-status': '無法更改狀態',
+ 'error-delete-protected-role': '無法刪除受保護的角色',
+ 'error-department-not-found': '找不到部門',
+ 'error-direct-message-file-upload-not-allowed': '私人對話中不允許檔案分享',
+ 'error-duplicate-channel-name': '名為「{{channel_name}}」的頻道已存在',
+ 'error-email-domain-blacklisted': '電子郵件網域被禁用',
+ 'error-email-send-failed': '嘗試發送電子郵件時出錯:{{message}}',
+ 'error-save-image': '錯誤,無法儲存圖片',
+ 'error-save-video': '錯誤,無法儲存影片',
+ 'error-field-unavailable': '{{field}} 已被使用 :(',
+ 'error-file-too-large': '檔案太大',
+ 'error-importer-not-defined': '沒有正確定義,它缺少匯入類型',
+ 'error-input-is-not-a-valid-field': '{{input}}不是有效的{{field}}',
+ 'error-invalid-actionlink': '無效的操作連結',
+ 'error-invalid-arguments': '無效的參數',
+ 'error-invalid-asset': '無效的資源',
+ 'error-invalid-channel': '無效的頻道',
+ 'error-invalid-channel-start-with-chars': '無效的頻道,請以 @ 或 # 開頭',
+ 'error-invalid-custom-field': '無效的自訂欄位',
+ 'error-invalid-custom-field-name': '無效的自訂欄位名稱。只能包含字母、數字、連字符(-)及下底線(_).',
+ 'error-invalid-date': '無效的日期',
+ 'error-invalid-description': '無效的描述',
+ 'error-invalid-domain': '無效的域名',
+ 'error-invalid-email': '無效的電子郵件{{email}}',
+ 'error-invalid-email-address': '無效的郵件地址',
+ 'error-invalid-file-height': '無效的檔案高度',
+ 'error-invalid-file-type': '無效的檔案類型',
+ 'error-invalid-file-width': '無效的檔案寬度',
+ 'error-invalid-from-address': '無效的地址',
+ 'error-invalid-integration': '無效的整合',
+ 'error-invalid-message': '無效的訊息',
+ 'error-invalid-method': '無效的方法',
+ 'error-invalid-name': '無效的名稱',
+ 'error-invalid-password': '無效的密碼',
+ 'error-invalid-redirectUri': '無效的轉址',
+ 'error-invalid-role': '無效的角色',
+ 'error-invalid-room': '無效的聊天室',
+ 'error-invalid-room-name': '{{room_name}} 不是一個有效的聊天室名稱',
+ 'error-invalid-room-type': '{{type}} 不是有效的聊天室類型',
+ 'error-invalid-settings': '無效的設置',
+ 'error-invalid-subscription': '無效的訂閱',
+ 'error-invalid-token': '無效的 token',
+ 'error-invalid-triggerWords': '無效的關鍵字',
+ 'error-invalid-urls': '無效的網址',
+ 'error-invalid-user': '無效的使用者',
+ 'error-invalid-username': '無效的使用者名稱',
+ 'error-invalid-webhook-response': 'webhook 網址以200以外的狀態響應',
+ 'error-message-deleting-blocked': '訊息刪除已停用',
+ 'error-message-editing-blocked': '訊息編輯已停用',
+ 'error-message-size-exceeded': '訊息大小超出上限',
+ 'error-missing-unsubscribe-link': '您必須提供[取消訂閱]連結。',
+ 'error-no-tokens-for-this-user': '這名使用者沒有Token',
+ 'error-not-allowed': '不允許',
+ 'error-not-authorized': '未授權',
+ 'error-push-disabled': '推播已停用',
+ 'error-remove-last-owner': '這是最後的擁有者。請在刪除此人之前設置一個新的擁有者。',
+ 'error-role-in-use': '無法刪除正在使用中的角色',
+ 'error-role-name-required': '角色名稱是必須的',
+ 'error-the-field-is-required': '字段 {{field}} 是必須的。',
+ 'error-too-many-requests': '錯誤,請求過多。請稍候{{seconds}}秒後再進行嘗試。',
+ 'error-user-is-not-activated': '使用者尚未啟用',
+ 'error-user-has-no-roles': '使用者尚未設定角色',
+ 'error-user-limit-exceeded': '嘗試邀請到 #channel_name 的使用者數量超過了管理員設置的限制',
+ 'error-user-not-in-room': '使用者不在這個聊天室',
+ 'error-user-registration-custom-field': '無效的自訂註冊欄位',
+ 'error-user-registration-disabled': '使用者註冊已停用',
+ 'error-user-registration-secret': '只能透過加密網址進行使用者註冊',
+ 'error-you-are-last-owner': '您是最後的擁有者。請刪除此人之前設置一個新的擁有者。',
+ Actions: '操作',
+ activity: '活動時間',
+ Activity: '以活動時間排序',
+ Add_Reaction: '增加表情貼',
+ Add_Server: '新增伺服器',
+ Add_users: '新增使用者',
+ Admin_Panel: '管理者面板',
+ Agent: '代理',
+ Alert: '警告',
+ alert: '警告',
+ alerts: '警告',
+ All_users_in_the_channel_can_write_new_messages: '頻道中的所有使用者都可以發送新訊息',
+ A_meaningful_name_for_the_discussion_room: '取一個有意義的討論區名稱',
+ All: '所有',
+ All_Messages: '全部訊息',
+ Allow_Reactions: '允許表情貼',
+ Alphabetical: '依名稱排列',
+ and_more: '和更多的',
+ and: '和',
+ announcement: '公告',
+ Announcement: '公告',
+ Apply_Your_Certificate: '使用自己的憑證',
+ Applying_a_theme_will_change_how_the_app_looks: '套用主題將會改變 App 的外觀',
+ ARCHIVE: '封存',
+ archive: '封存',
+ are_typing: '正在輸入',
+ Are_you_sure_question_mark: '你確定嗎?',
+ Are_you_sure_you_want_to_leave_the_room: '你確定要離開聊天室 {{room}} 嗎?',
+ Audio: '音訊',
+ Authenticating: '正在驗證身份',
+ Automatic: '自動',
+ Auto_Translate: '自動翻譯',
+ Avatar_changed_successfully: '大頭貼更新成功!',
+ Avatar_Url: '大頭貼地址',
+ Away: '離開',
+ Back: '返回',
+ Black: '黑色',
+ Block_user: '封鎖用戶',
+ Browser: '瀏覽器',
+ Broadcast_channel_Description: '只有經過授權的使用者才能發送新訊息,但其他使用者可以回覆',
+ Broadcast_Channel: '廣播頻道',
+ Busy: '忙碌',
+ By_proceeding_you_are_agreeing: '若要繼續操作,請同意我們的',
+ Cancel_editing: '取消編輯',
+ Cancel_recording: '取消錄製',
+ Cancel: '取消',
+ changing_avatar: '更改大頭貼',
+ creating_channel: '新建頻道',
+ creating_invite: '建立邀請',
+ Channel_Name: '頻道名稱',
+ Channels: '頻道',
+ Chats: '聊天',
+ Call_already_ended: '通話已經結束!',
+ Clear_cookies_alert: '是否清除所有 cookies?',
+ Clear_cookies_desc: '本操作將清除所有登入 cookies,以登入其他帳號',
+ Clear_cookies_yes: '是,清除 cookies',
+ Clear_cookies_no: '否,保留 cookies',
+ Click_to_join: '點擊以參與',
+ Close: '關閉',
+ Close_emoji_selector: '關閉emoji選擇器',
+ Closing_chat: '結束聊天',
+ Change_language_loading: '切換語言',
+ Chat_closed_by_agent: '聊天已被客服關閉',
+ Choose: '選擇',
+ Choose_from_library: '選擇庫',
+ Choose_file: '選擇檔案',
+ Choose_where_you_want_links_be_opened: '請選擇您要將連結開啟在',
+ Code: '程式碼',
+ Code_or_password_invalid: '驗證碼或密碼不正確',
+ Collaborative: '協作',
+ Confirm: '確認',
+ Connect: '連接',
+ Connected: '已連接',
+ connecting_server: '連線至伺服器',
+ Connecting: '連接中',
+ Contact_us: '聯絡我們',
+ Contact_your_server_admin: '請聯絡系統管理員',
+ Continue_with: '繼續採用',
+ Copied_to_clipboard: '複製到剪貼簿',
+ Copy: '複製',
+ Conversation: '對話',
+ Permalink: '永久連結',
+ Certificate_password: '憑證密碼',
+ Clear_cache: '清除本機資料',
+ Clear_cache_loading: '清除快取',
+ Whats_the_password_for_your_certificate: '您的憑證密碼是?',
+ Create_account: '新建帳戶',
+ Create_Channel: '新建頻道',
+ Create_Direct_Messages: '新增私人訊息',
+ Create_Discussion: '新增論壇',
+ Created_snippet: '新增程式碼片段',
+ Create_a_new_workspace: '建立一個新的工作區',
+ Create: '建立',
+ Custom_Status: '自訂狀態',
+ Dark: '深色',
+ Dark_level: '深色程度',
+ Default: '預設',
+ Default_browser: '預設瀏覽器',
+ Delete_Room_Warning: '刪除聊天室將連帶刪除聊天室內的所有訊息。這是不可逆的操作。',
+ Department: '部門',
+ delete: '刪除',
+ Delete: '刪除',
+ DELETE: '刪除',
+ deleting_room: '正在刪除聊天室',
+ description: '描述',
+ Description: '描述',
+ DESKTOP_OPTIONS: '桌面選項',
+ DESKTOP_NOTIFICATIONS: '桌面通知',
+ Desktop_Alert_info: '這些通知將發送至桌面',
+ Directory: '目錄',
+ Direct_Messages: '私訊',
+ Disable_notifications: '禁用訊息通知',
+ Discussions: '論壇',
+ Discussion_Desc: '幫助保持對正在發生的事情的概述!通過建立討論,將建立您選擇的子通道,並且兩者都是連接的。',
+ Discussion_name: '論壇名稱',
+ Done: '完畢',
+ Dont_Have_An_Account: '還未擁有帳號?',
+ Do_you_have_an_account: '是否擁有帳號?',
+ Do_you_have_a_certificate: '是否擁有憑證?',
+ Do_you_really_want_to_key_this_room_question_mark: '您真的想要{{key}}這個聊天室嗎?',
+ E2E_How_It_Works_info1: '您現在可以建立加密私人群組和私人訊息。您也可以變更已存在的私人群組或私訊來加密',
+ E2E_How_It_Works_info2: '這是點對點的加密,所以金鑰是用來加密/解密,您的訊息也不會儲存到伺服器上。為了這個原因您必須安全存放您的密碼。您會希望使用 E2E 加密輸入到其他裝置。',
+ E2E_How_It_Works_info3: '如果繼續,將自動產生一組 E2E 密碼',
+ E2E_How_It_Works_info4: '這是自動產生的密碼,在任何時間從任何瀏覽器您可以設定新的密碼給您的加密金鑰您可以輸入已存在的密碼。',
+ edit: '編輯',
+ edited: '已編輯',
+ Edit: '編輯',
+ Edit_Status: '編輯狀態',
+ Edit_Invite: '編輯邀請',
+ End_to_end_encrypted_room: '點對點加密聊天室',
+ end_to_end_encryption: '點對點加密',
+ Email_Notification_Mode_All: '每次被標記或私訊',
+ Email_Notification_Mode_Disabled: '禁用',
+ Email_or_password_field_is_empty: '電子郵件或密碼字段為空',
+ Email: '電子郵件',
+ EMAIL: 'EMAIL',
+ email: '電子郵件',
+ Empty_title: '空白標題',
+ Enable_Auto_Translate: '開啟自動翻譯',
+ Enable_notifications: '開啟訊息通知',
+ Encrypted: '已加密',
+ Encrypted_message: '加密訊息',
+ Enter_Your_E2E_Password: '輸入您的 E2E 密碼',
+ Enter_Your_Encryption_Password_desc1: '這將會允許您存取您的加密私人群組和私訊',
+ Enter_Your_Encryption_Password_desc2: '您需要在任何使用此聊天的平台輸入密碼,以加/解密您的訊息',
+ Encryption_error_title: '您的加密密碼似乎有誤',
+ Encryption_error_desc: '無法使用匯入的加密金鑰來解密',
+ Everyone_can_access_this_channel: '所有人皆可存取此頻道',
+ Error_uploading: '錯誤上傳',
+ Expiration_Days: '到期 (日)',
+ Favorite: '我的最愛',
+ Favorites: '我的最愛',
+ Files: '檔案',
+ File_description: '檔案描述',
+ File_name: '檔案名稱',
+ Finish_recording: '完成錄製',
+ Following_thread: '追蹤的討論串',
+ For_your_security_you_must_enter_your_current_password_to_continue: '為了您的安全,您必須重新輸入密碼才能繼續',
+ Forgot_password_If_this_email_is_registered: '如果這封郵件已註冊,我們將發送如何重置密碼的說明。如果您沒有立即收到電子郵件,請再試一次。',
+ Forgot_password: '忘記密碼',
+ Forgot_Password: '忘記密碼',
+ Forward: '轉發',
+ Forward_Chat: '轉發聊天',
+ Forward_to_department: '轉發到部門',
+ Forward_to_user: '轉發給使用者',
+ Full_table: '點擊以查看完整表格',
+ Generate_New_Link: '產生新的連結',
+ Group_by_favorites: '我的最愛優先',
+ Group_by_type: '以類型分組',
+ Hide: '隱藏',
+ Has_joined_the_channel: '已加入頻道',
+ Has_joined_the_conversation: '已經加入此對話',
+ Has_left_the_channel: '已離開頻道',
+ Hide_System_Messages: '隱藏系統訊息',
+ Hide_type_messages: '隱藏 "{{type}}" 訊息',
+ How_It_Works: '運作方式',
+ Message_HideType_uj: '隱藏“使用者加入”訊息',
+ Message_HideType_ul: '隱藏“使用者離開”訊息',
+ Message_HideType_ru: '隱藏“使用者已刪除”訊息',
+ Message_HideType_au: '隱藏“使用者已增加”訊息',
+ Message_HideType_mute_unmute: '隱藏“使用者靜音/取消靜音”訊息',
+ Message_HideType_r: '隱藏“聊天室名稱已更改”的訊息',
+ Message_HideType_ut: '隱藏“使用者已加入對話”的訊息',
+ Message_HideType_wm: '隱藏“歡迎”的訊息',
+ Message_HideType_rm: '隱藏“已刪除訊息”的訊息',
+ Message_HideType_subscription_role_added: '隱藏“已設置角色”的訊息',
+ Message_HideType_subscription_role_removed: '隱藏“不再定義的角色”的訊息',
+ Message_HideType_room_archived: '隱藏“聊天室已封存”的訊息',
+ Message_HideType_room_unarchived: '隱藏“聊天室未封存”的訊息',
+ I_Saved_My_E2E_Password: '儲存我的 E2E 密碼',
+ IP: 'IP',
+ In_app: 'App 內',
+ IN_APP_AND_DESKTOP: 'App 內及桌面',
+ In_App_and_Desktop_Alert_info: '當在應用程序打開時,螢幕頂端顯示橫幅,並在桌面上顯示通知',
+ Invisible: '隱身',
+ Invite: '邀請',
+ is_a_valid_RocketChat_instance: '是一個有效的 Rocket.Chat 實例',
+ is_not_a_valid_RocketChat_instance: '不是有效的 Rocket.Chat 實例',
+ is_typing: '正在輸入',
+ Invalid_or_expired_invite_token: '無效或到期的邀請 token',
+ Join_your_workspace: '加入您的工作區',
+ Invite_Link: '邀請連結',
+ Invite_users: '邀請使用者',
+ Join: '加入',
+ Join_our_open_workspace: '加入開放工作區',
+ Just_invited_people_can_access_this_channel: '僅有受邀對象能存取此頻道',
+ Language: '語言',
+ last_message: '最後一則訊息',
+ Leave_channel: '離開頻道',
+ leaving_room: '離開聊天室',
+ leave: '離開',
+ Legal: '合法',
+ Light: '淺色',
+ License: '授權條款',
+ Livechat: '即時聊天',
+ Livechat_edit: '即時聊天編輯',
+ Login: '登入',
+ Login_error: '你的憑證被拒絕了! 請再試一次',
+ Login_with: '登入為',
+ Logging_out: '正在登出',
+ Logout: '登出',
+ Max_number_of_uses: '最大使用次數',
+ Max_number_of_users_allowed_is_number: '允許使用者上限數量',
+ members: '成員',
+ Members: '成員',
+ Mentioned_Messages: '提到的訊息',
+ mentioned: '提到',
+ Mentions: '提到',
+ Message_accessibility: '{{time}}來自{{user}}的訊息: {{message}}',
+ Message_actions: '訊息操作',
+ Message_pinned: '訊息被釘選',
+ Message_removed: '訊息被刪除',
+ Message_starred: '訊息被標註',
+ Message_unstarred: '訊息被取消標註',
+ message: '訊息',
+ messages: '訊息',
+ Message: '訊息',
+ Messages: '訊息',
+ Message_Reported: '訊息已檢舉',
+ Microphone_Permission_Message: 'Rocket.Chat需要存取您的麥克風,以便發送聲音訊息。',
+ Microphone_Permission: '麥克風授權',
+ Mute: '靜音',
+ muted: '被靜音',
+ My_servers: '我的伺服器',
+ N_people_reacted: '{{n}} 人回复',
+ N_users: '{{n}} 位使用者',
+ name: '名稱',
+ Name: '名稱',
+ Navigation_history: '瀏覽歷史記錄',
+ Never: '從不',
+ New_Message: '新訊息',
+ New_Password: '新密碼',
+ New_Server: '新伺服器',
+ Next: '下一步',
+ No_files: '沒有檔案',
+ No_limit: '沒有限制',
+ No_mentioned_messages: '沒有提及的訊息',
+ No_pinned_messages: '沒有釘選的訊息',
+ No_results_found: '沒有搜尋結果',
+ No_starred_messages: '沒有標記的訊息',
+ No_thread_messages: '沒有討論串訊息',
+ No_label_provided: '沒有提供 {{label}}',
+ No_Message: '沒有訊息',
+ No_messages_yet: '當前未有訊息',
+ No_Reactions: '沒有表情貼',
+ No_Read_Receipts: '沒有已讀人員',
+ Not_logged: '沒有記錄',
+ Not_RC_Server: '這不是一個 Rocket.Chat server.\\n{{contact}}',
+ Nothing: '沒有東西',
+ Nothing_to_save: '沒有可儲存的東西!',
+ Notify_active_in_this_room: '通知這個聊天室的活躍使用者',
+ Notify_all_in_this_room: '通知這個聊天室的所有人',
+ Notifications: '通知',
+ Notification_Duration: '通知持續時間',
+ Notification_Preferences: '通知偏好設定',
+ No_available_agents_to_transfer: '沒有可用的代理進行傳輸',
+ Offline: '離線',
+ Oops: '哎呀!',
+ Omnichannel: 'Omnichannel',
+ Open_Livechats: '打開即時聊天',
+ Omnichannel_enable_alert: '您尚未啟用 Omnichannel,是否想要啟用?',
+ Onboarding_description: '工作區是團隊或組織協作的空間。向工作區管理員詢問要加入的地址或為您的團隊創建一個。',
+ Onboarding_join_workspace: '加入一個工作區',
+ Onboarding_subtitle: '超越團隊合作',
+ Onboarding_title: '歡迎來到 Rocket.Chat',
+ Onboarding_join_open_description: '加入我們的開放工作區以與 Rocket.Chat 團隊及社群交談',
+ Onboarding_agree_terms: '繼續,即表示您同意 Rocket.Chat',
+ Onboarding_less_options: '較少選項',
+ Onboarding_more_options: '較多選項',
+ Online: '上線',
+ Only_authorized_users_can_write_new_messages: '只有經過授權的使用者才能寫新訊息',
+ Open_emoji_selector: '打開 emoji 選擇器',
+ Open_Source_Communication: '開源溝通',
+ Open_your_authentication_app_and_enter_the_code: '打開您的驗證應用程式並輸入代碼。您也可以使用其中一個備用代碼。',
+ OR: '或',
+ OS: '作業系統',
+ Overwrites_the_server_configuration_and_use_room_config: '覆寫伺服器設置和使用聊天室設置',
+ Password: '密碼',
+ Parent_channel_or_group: '父頻道或群組',
+ Permalink_copied_to_clipboard: '永久鏈接已複製到剪貼簿!',
+ Phone: '電話',
+ Pin: '釘選',
+ Pinned_Messages: '釘選訊息',
+ pinned: '已被釘選',
+ Pinned: '被釘選',
+ Please_add_a_comment: '請增加評論',
+ Please_enter_your_password: '請輸入密碼',
+ Please_wait: '請稍候',
+ Preferences: '偏好設定',
+ Preferences_saved: '偏好設定已被儲存!',
+ Privacy_Policy: '隱私政策',
+ Private_Channel: '私人頻道',
+ Private_Groups: '私人群組',
+ Private: '私有的',
+ Processing: '處理中',
+ Profile_saved_successfully: '個人資料儲存成功!',
+ Profile: '個人資料',
+ Public_Channel: '公共頻道',
+ Public: '公共',
+ PUSH_NOTIFICATIONS: '推送通知',
+ Push_Notifications_Alert_Info: '這些通知將在未開啟 App 時發送給您',
+ Quote: '引用',
+ Reactions_are_disabled: '表情貼被禁用',
+ Reactions_are_enabled: '表情貼被啟用',
+ Reactions: '表情貼',
+ Read: '讀取',
+ Read_External_Permission_Message: 'Rocket.Chat 需要存取您裝置上的相片、多媒體及檔案',
+ Read_External_Permission: '讀取媒體權限',
+ Read_Only_Channel: '唯讀頻道',
+ Read_Only: '唯讀',
+ Read_Receipt: '查看已讀人員',
+ Receive_Group_Mentions: '接收群組提及',
+ Receive_Group_Mentions_Info: '接收@all和@here提及',
+ Register: '註冊',
+ Repeat_Password: '重複輸入密碼',
+ Replied_on: '回覆在',
+ replies: '回覆',
+ reply: '回覆',
+ Reply: '回覆',
+ Report: '檢舉',
+ Receive_Notification: '接收通知',
+ Receive_notifications_from: '接收來自 {{name}} 的通知',
+ Resend: '重新發送',
+ Reset_password: '重置密碼',
+ resetting_password: '正在重置密碼',
+ RESET: '重置',
+ Return: '返回',
+ Review_app_title: '對此 App 滿意嗎?',
+ Review_app_desc: '請在 {{store}} 給予我們 5 星好評',
+ Review_app_yes: '沒問題',
+ Review_app_no: '婉拒',
+ Review_app_later: '之後再說',
+ Review_app_unable_store: '無法開啟 {{store}}',
+ Review_this_app: '評分此 App',
+ Remove: '移除',
+ Roles: '角色',
+ Room_actions: '聊天室操作',
+ Room_changed_announcement: '{{userBy}}將聊天室通知改為:{{announcement}}',
+ Room_changed_description: '{{userBy}}將聊天室說明改為:{{description}}',
+ Room_changed_privacy: '{{userBy}}將聊天室類型改為:{{type}}',
+ Room_changed_topic: '{{userBy}}將聊天室主題改為:{{topic}}',
+ Room_Files: '聊天室檔案',
+ Room_Info_Edit: '修改聊天室資訊',
+ Room_Info: '聊天室資訊',
+ Room_Members: '聊天室成員',
+ Room_name_changed: '{{userBy}} 將聊天室名稱改為:{{{name}}',
+ SAVE: '儲存',
+ Save_Changes: '儲存更改',
+ Save: '儲存',
+ Saved: '保存',
+ saving_preferences: '儲存偏好設定',
+ saving_profile: '儲存配置文件',
+ saving_settings: '儲存設定',
+ saved_to_gallery: '儲存至圖片庫',
+ Save_Your_E2E_Password: '儲存您的點對點密碼',
+ Save_Your_Encryption_Password: '儲存您的加密密碼',
+ Save_Your_Encryption_Password_warning: '此密碼未被儲存在任何地方,為此您必須安全存放您的密碼',
+ Save_Your_Encryption_Password_info: '請記住,如果你遺失了您的密碼,您將無法存取您的訊息並不可恢復',
+ Search_Messages: '搜尋訊息',
+ Search: '搜尋',
+ Search_by: '搜尋',
+ Search_global_users: '搜尋全域使用者',
+ Search_global_users_description: '如果啟用,您將可以搜尋其他公司、伺服器上的任何使用者',
+ Seconds: '{{second}} 秒',
+ Select_Avatar: '選擇大頭貼',
+ Select_Server: '選擇伺服器',
+ Select_Users: '選擇使用者',
+ Select_a_Channel: '選擇一個頻道',
+ Select_a_Department: '選擇一個部門',
+ Select_an_option: '選擇一個選項',
+ Select_a_User: '選擇一個使用者',
+ Send: '發送',
+ Send_audio_message: '發送語音訊息',
+ Send_crash_report: '送出當機報告',
+ Send_message: '發送訊息',
+ Send_me_the_code_again: '再次發送代碼給我',
+ Send_to: '發送到',
+ Sending_to: '正發送到',
+ Sent_an_attachment: '發送附件',
+ Server: '伺服器',
+ Servers: '伺服器',
+ Server_version: '伺服器版本',
+ Set_username_subtitle: '使用者名用於允許其他人在郵件中提及您',
+ Set_custom_status: '設定自訂狀態',
+ Set_status: '設定狀態',
+ Status_saved_successfully: '狀態儲存成功',
+ Settings: '設定',
+ Settings_succesfully_changed: '更改設定成功!',
+ Share: '分享',
+ Share_Link: '分享連結',
+ Share_this_app: '分享此 app',
+ Show_more: '顯示更多',
+ Show_Unread_Counter: '顯示未讀訊息數量',
+ Show_Unread_Counter_Info: '顯示未讀訊息數量資訊',
+ Sign_in_your_server: '登錄你的伺服器',
+ Sign_Up: '註冊',
+ Some_field_is_invalid_or_empty: '某些字段無效或為空',
+ Sorting_by: '以{{key}}排序',
+ Sound: '聲音',
+ Star_room: '標記聊天室',
+ Star: '標記',
+ Starred_Messages: '標記的訊息',
+ starred: '被標記',
+ Starred: '標記的',
+ Start_of_conversation: '開始對話',
+ Start_a_Discussion: '開始論壇',
+ Started_discussion: '已開始的論壇',
+ Started_call: '{{userBy}} 開始的通話',
+ Submit: '送出',
+ Table: '表格',
+ Tags: '標籤',
+ Take_a_photo: '拍照',
+ Take_a_video: '錄影',
+ Take_it: '拿去!',
+ tap_to_change_status: '點擊即可更改狀態',
+ Tap_to_view_servers_list: '點擊查看伺服器列表',
+ Terms_of_Service: '服務條款',
+ Theme: '佈景主題',
+ The_user_wont_be_able_to_type_in_roomName: '此使用者將無法在 {{roomName}} 中輸入',
+ The_user_will_be_able_to_type_in_roomName: '此使用者將可以在 {{roomName}} 中輸入',
+ There_was_an_error_while_action: '{{action}}出現錯誤!',
+ This_room_is_blocked: '這個聊天室已被鎖定',
+ This_room_is_read_only: '這個聊天室是唯讀的',
+ Thread: '討論串',
+ Threads: '討論串',
+ Timezone: '時區',
+ To: '到',
+ topic: '主題',
+ Topic: '主題',
+ Translate: '翻譯',
+ Try_again: '再試一次',
+ Two_Factor_Authentication: '雙重認證',
+ Type_the_channel_name_here: '在這裡輸入頻道名稱',
+ unarchive: '取消封存',
+ UNARCHIVE: '取消封存',
+ Unblock_user: '取消封鎖使用者',
+ Unfavorite: '取消我的最愛',
+ Unfollowed_thread: '取消追蹤討論',
+ Unmute: '取消靜音',
+ unmuted: '靜音狀態',
+ Unpin: '取消釘選',
+ unread_messages: '未讀訊息',
+ Unread: '未讀',
+ Unread_on_top: '未讀優先',
+ Unstar: '取消標記',
+ Updating: '正在更新',
+ Uploading: '正在上傳',
+ Upload_file_question_mark: '上傳文件?',
+ User: '使用者',
+ Users: '使用者',
+ User_added_by: '由{{userBy}}添加的使用者 {{useradd}}',
+ User_Info: '使用者資訊',
+ User_has_been_key: '使用者已被{{key}}!',
+ User_is_no_longer_role_by_: '{{userBy}}將角色 {{role}} 從使用者 {{user}} 身上移除',
+ User_muted_by: '使用者 {{userMuted}} 被 {{userBy}} 靜音',
+ User_removed_by: '使用者 {{userRemoved}} 被 {{userBy}} 移除',
+ User_sent_an_attachment: '{{user}} 寄送了一個附件',
+ User_unmuted_by: '使用者 {{userUnmuted}} 被 {{userBy}} 取消靜音',
+ User_was_set_role_by_: '使用者 {{user}} 被 {{userBy}} 設置角色 {{role}}',
+ Username_is_empty: '使用者名稱是空的',
+ Username: '使用者名稱',
+ Username_or_email: '使用者名稱或郵箱',
+ Uses_server_configuration: '使用伺服器設定',
+ Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常,討論會由一個問題開始,像是 \\"如何上傳一個圖片?\\"',
+ Validating: '正在驗證',
+ Registration_Succeeded: '註冊成功',
+ Verify: '驗證',
+ Verify_email_title: '註冊成功',
+ Verify_email_desc: '我們已經送出一封電子郵件,以確認您的註冊。如果您沒有很快收到,請再試一次。',
+ Verify_your_email_for_the_code_we_sent: '檢查您的電子郵件以取得我們發送的代碼',
+ Video_call: '視訊通話',
+ View_Original: '檢視原文',
+ Voice_call: '語音通話',
+ Waiting_for_network: '等待網路連線',
+ Websocket_disabled: 'Websocket 已於此伺服器上禁用。\\n{{contact}}',
+ Welcome: '歡迎',
+ What_are_you_doing_right_now: '現在在做些什麼?',
+ Whats_your_2fa: '您的 2FA 代碼是?',
+ Without_Servers: '未連接至伺服器',
+ Workspaces: '工作區',
+ Would_you_like_to_return_the_inquiry: '你想回覆詢問嗎?',
+ Write_External_Permission_Message: 'Rocket.Chat 需要您圖片庫的存取權限以儲存圖片。',
+ Write_External_Permission: '圖片庫權限',
+ Yes: '是',
+ Yes_action_it: '是的,{{action}}它!',
+ Yesterday: '昨天',
+ You_are_in_preview_mode: '您處於預覽模式',
+ You_are_offline: '您處於離線狀態',
+ You_can_search_using_RegExp_eg: '您可以使用RegExp進行搜索。例如`/^text$/i`',
+ You_colon: '你:',
+ you_were_mentioned: '你被提到了',
+ You_were_removed_from_channel: '您已從 {{channel}} 中被踢除',
+ you: '你',
+ You: '你',
+ Logged_out_by_server: '伺服器端已將你登出,請重新登入',
+ You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要至少連接一個 Rocket.Chat 伺服器才能共享某些内容。',
+ You_need_to_verifiy_your_email_address_to_get_notications: '您需要先驗證您的電子郵件以啟用通知',
+ Your_certificate: '你的證書',
+ Your_message: '你的訊息',
+ Your_invite_link_will_expire_after__usesLeft__uses: '您的邀請連結將在{{usesLeft}}使用後到期。',
+ Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀請連結將於{{date}}或{{usesLeft}}使用後到期。',
+ Your_invite_link_will_expire_on__date__: '您的邀請連結將於{{date}}到期。',
+ Your_invite_link_will_never_expire: '您的邀請連結永久有效。',
+ Your_workspace: '您的工作區',
+ Your_password_is: '您的密碼',
+ Version_no: '版本: {{version}}',
+ You_will_not_be_able_to_recover_this_message: '您將無法恢復此訊息!',
+ You_will_unset_a_certificate_for_this_server: '您將取消此伺服器的憑證設定',
+ Change_Language: '切換語言',
+ Crash_report_disclaimer: '我們從不追踪您的聊天內容。 崩潰報告和分析事件僅包含有關我們的信息,以便識別和修復問題。',
+ Type_message: '輸入訊息',
+ Room_search: '搜索聊天室',
+ Room_selection: '選擇房間(輸入 1...9)',
+ Next_room: '下一個聊天室',
+ Previous_room: '上一個聊天室',
+ New_room: '新聊天室',
+ Upload_room: '上傳至聊天室',
+ Search_messages: '搜尋訊息',
+ Scroll_messages: '訊息滾動',
+ Reply_latest: '回覆最新訊息',
+ Reply_in_Thread: '回覆討論',
+ Server_selection: '選擇伺服器',
+ Server_selection_numbers: '選擇伺服器(輸入 1...9)',
+ Add_server: '新增伺服器',
+ New_line: '新行',
+ You_will_be_logged_out_of_this_application: '您即將登出',
+ Clear: '清除',
+ This_will_clear_all_your_offline_data: '這將清除您的所有離線資料。',
+ This_will_remove_all_data_from_this_server: '這將從伺服器中刪除所有資料。',
+ Mark_unread: '標記未讀',
+ Wait_activation_warning: '您的帳號必須由管理員手動啟用後才能登入。',
+ Screen_lock: '螢幕鎖定',
+ Local_authentication_biometry_title: '驗證',
+ Local_authentication_biometry_fallback: '使用通關密碼',
+ Local_authentication_unlock_option: '以通關密碼解鎖',
+ Local_authentication_change_passcode: '變更通關密碼',
+ Local_authentication_info: '註: 如果您忘記了通關密碼,將需要移除並重新安裝此 App',
+ Local_authentication_facial_recognition: '臉部辨識',
+ Local_authentication_fingerprint: '指紋辨識',
+ Local_authentication_unlock_with_label: '以 {{label}} 解鎖',
+ Local_authentication_auto_lock_60: '1分鐘後',
+ Local_authentication_auto_lock_300: '5分鐘後',
+ Local_authentication_auto_lock_900: '15分鐘後',
+ Local_authentication_auto_lock_1800: '半小時後',
+ Local_authentication_auto_lock_3600: '一小時後',
+ Passcode_enter_title: '請輸入通關密碼',
+ Passcode_choose_title: '請輸入新通關密碼',
+ Passcode_choose_confirm_title: '請確認新通關密碼',
+ Passcode_choose_error: '不正確的通關密碼,請再試一次',
+ Passcode_choose_force_set: '管理員設定必填',
+ Passcode_app_locked_title: 'App 已鎖定',
+ Passcode_app_locked_subtitle: '{{timeLeft}} 秒後再進行嘗試',
+ After_seconds_set_by_admin: '{{seconds}} 秒 (管理員設定)',
+ Dont_activate: '現在不要啟用',
+ Queued_chats: '聊天佇列',
+ Queue_is_empty: '佇列是空的',
+ Logout_from_other_logged_in_locations: '登出其他已登入的設備',
+ You_will_be_logged_out_from_other_locations: '您將於其他設備上登出',
+ Logged_out_of_other_clients_successfully: '成功登出其他客戶端',
+ Logout_failed: '登出失敗',
+ Log_analytics_events: '日誌分析事件'
+};
diff --git a/app/index.js b/app/index.js
index e4d9e52cf..4455a2f14 100644
--- a/app/index.js
+++ b/app/index.js
@@ -2,7 +2,6 @@ import React from 'react';
import { Linking, Dimensions } from 'react-native';
import { AppearanceProvider } from 'react-native-appearance';
import { Provider } from 'react-redux';
-import RNUserDefaults from 'rn-user-defaults';
import { KeyCommandsEmitter } from 'react-native-keycommands';
import RNScreens from 'react-native-screens';
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
@@ -13,6 +12,7 @@ import {
subscribeTheme,
unsubscribeTheme
} from './utils/theme';
+import UserPreferences from './lib/userPreferences';
import EventEmitter from './utils/events';
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
import { deepLinkingOpen } from './actions/deepLinking';
@@ -36,7 +36,7 @@ import Toast from './containers/Toast';
import InAppNotification from './containers/InAppNotification';
import { ActionSheetProvider } from './containers/ActionSheet';
import debounce from './utils/debounce';
-
+import { isFDroidBuild } from './constants/environment';
RNScreens.enableScreens();
@@ -65,7 +65,9 @@ export default class Root extends React.Component {
constructor(props) {
super(props);
this.init();
- this.initCrashReport();
+ if (!isFDroidBuild) {
+ this.initCrashReport();
+ }
const { width, height, scale } = Dimensions.get('window');
this.state = {
theme: defaultTheme(),
@@ -106,7 +108,7 @@ export default class Root extends React.Component {
}
init = async() => {
- RNUserDefaults.objectForKey(THEME_PREFERENCES_KEY).then(this.setTheme);
+ UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
const [notification, deepLinking] = await Promise.all([initializePushNotifications(), Linking.getInitialURL()]);
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
store.dispatch(appInitLocalSettings());
diff --git a/app/lib/database/index.js b/app/lib/database/index.js
index 37a17a3cd..fbebde6c1 100644
--- a/app/lib/database/index.js
+++ b/app/lib/database/index.js
@@ -1,7 +1,6 @@
import { Database } from '@nozbe/watermelondb';
import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite';
import logger from '@nozbe/watermelondb/utils/common/logger';
-import RNFetchBlob from 'rn-fetch-blob';
import Subscription from './model/Subscription';
import Room from './model/Room';
@@ -17,6 +16,7 @@ import Permission from './model/Permission';
import SlashCommand from './model/SlashCommand';
import User from './model/User';
import Server from './model/Server';
+import ServersHistory from './model/ServersHistory';
import serversSchema from './schema/servers';
import appSchema from './schema/app';
@@ -26,8 +26,9 @@ import migrations from './model/migrations';
import serversMigrations from './model/serversMigrations';
import { isIOS } from '../../utils/deviceInfo';
+import appGroup from '../../utils/appGroup';
-const appGroupPath = isIOS ? `${ RNFetchBlob.fs.syncPathAppGroup('group.ios.chat.rocket') }/` : '';
+const appGroupPath = isIOS ? appGroup.path : '';
if (__DEV__ && isIOS) {
console.log(appGroupPath);
@@ -35,7 +36,7 @@ if (__DEV__ && isIOS) {
export const getDatabase = (database = '') => {
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
- const dbName = `${ appGroupPath }${ path }.db`;
+ const dbName = `${ appGroupPath }${ path }-experimental.db`;
const adapter = new SQLiteAdapter({
dbName,
@@ -67,11 +68,11 @@ class DB {
databases = {
serversDB: new Database({
adapter: new SQLiteAdapter({
- dbName: `${ appGroupPath }default.db`,
+ dbName: `${ appGroupPath }default-experimental.db`,
schema: serversSchema,
migrations: serversMigrations
}),
- modelClasses: [Server, User],
+ modelClasses: [Server, User, ServersHistory],
actionsEnabled: true
})
}
@@ -94,7 +95,7 @@ class DB {
setShareDB(database = '') {
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
- const dbName = `${ appGroupPath }${ path }.db`;
+ const dbName = `${ appGroupPath }${ path }-experimental.db`;
const adapter = new SQLiteAdapter({
dbName,
diff --git a/app/lib/database/model/Message.js b/app/lib/database/model/Message.js
index 22dfec731..c41bad26b 100644
--- a/app/lib/database/model/Message.js
+++ b/app/lib/database/model/Message.js
@@ -77,4 +77,6 @@ export default class Message extends Model {
@field('tmsg') tmsg;
@json('blocks', sanitizer) blocks;
+
+ @field('e2e') e2e;
}
diff --git a/app/lib/database/model/Room.js b/app/lib/database/model/Room.js
index 0a8beab12..32a3a5777 100644
--- a/app/lib/database/model/Room.js
+++ b/app/lib/database/model/Room.js
@@ -12,6 +12,8 @@ export default class Room extends Model {
@field('encrypted') encrypted;
+ @field('e2e_key_id') e2eKeyId;
+
@field('ro') ro;
@json('v', sanitizer) v;
diff --git a/app/lib/database/model/Server.js b/app/lib/database/model/Server.js
index d30b3a3f4..df770a32e 100644
--- a/app/lib/database/model/Server.js
+++ b/app/lib/database/model/Server.js
@@ -27,4 +27,8 @@ export default class Server extends Model {
@field('biometry') biometry;
@field('unique_id') uniqueID;
+
+ @field('enterprise_modules') enterpriseModules;
+
+ @field('e2e_enable') E2E_Enable;
}
diff --git a/app/lib/database/model/ServersHistory.js b/app/lib/database/model/ServersHistory.js
new file mode 100644
index 000000000..469775f13
--- /dev/null
+++ b/app/lib/database/model/ServersHistory.js
@@ -0,0 +1,12 @@
+import { Model } from '@nozbe/watermelondb';
+import { field, date, readonly } from '@nozbe/watermelondb/decorators';
+
+export default class ServersHistory extends Model {
+ static table = 'servers_history';
+
+ @field('url') url;
+
+ @field('username') username;
+
+ @readonly @date('updated_at') updatedAt
+}
diff --git a/app/lib/database/model/Subscription.js b/app/lib/database/model/Subscription.js
index a94e09423..afdd8391f 100644
--- a/app/lib/database/model/Subscription.js
+++ b/app/lib/database/model/Subscription.js
@@ -109,4 +109,10 @@ export default class Subscription extends Model {
@json('livechat_data', sanitizer) livechatData;
@json('tags', sanitizer) tags;
+
+ @field('e2e_key') E2EKey;
+
+ @field('encrypted') encrypted;
+
+ @field('e2e_key_id') e2eKeyId;
}
diff --git a/app/lib/database/model/Thread.js b/app/lib/database/model/Thread.js
index fca0122ff..e0179fc35 100644
--- a/app/lib/database/model/Thread.js
+++ b/app/lib/database/model/Thread.js
@@ -73,4 +73,6 @@ export default class Thread extends Model {
@field('auto_translate') autoTranslate;
@json('translations', sanitizer) translations;
+
+ @field('e2e') e2e;
}
diff --git a/app/lib/database/model/ThreadMessage.js b/app/lib/database/model/ThreadMessage.js
index 842f22285..b3b4216b5 100644
--- a/app/lib/database/model/ThreadMessage.js
+++ b/app/lib/database/model/ThreadMessage.js
@@ -75,4 +75,6 @@ export default class ThreadMessage extends Model {
@json('translations', sanitizer) translations;
@field('draft_message') draftMessage;
+
+ @field('e2e') e2e;
}
diff --git a/app/lib/database/model/User.js b/app/lib/database/model/User.js
index 5535ef440..3d7a7cf2f 100644
--- a/app/lib/database/model/User.js
+++ b/app/lib/database/model/User.js
@@ -18,5 +18,7 @@ export default class User extends Model {
@field('statusText') statusText;
+ @field('login_email_password') loginEmailPassword;
+
@json('roles', sanitizer) roles;
}
diff --git a/app/lib/database/model/migrations.js b/app/lib/database/model/migrations.js
index db3b92dc3..9dd851790 100644
--- a/app/lib/database/model/migrations.js
+++ b/app/lib/database/model/migrations.js
@@ -129,6 +129,43 @@ export default schemaMigrations({
]
})
]
+ },
+ {
+ toVersion: 10,
+ steps: [
+ addColumns({
+ table: 'subscriptions',
+ columns: [
+ { name: 'e2e_key', type: 'string', isOptional: true },
+ { name: 'encrypted', type: 'boolean', isOptional: true },
+ { name: 'e2e_key_id', type: 'string', isOptional: true }
+ ]
+ }),
+ addColumns({
+ table: 'messages',
+ columns: [
+ { name: 'e2e', type: 'string', isOptional: true }
+ ]
+ }),
+ addColumns({
+ table: 'thread_messages',
+ columns: [
+ { name: 'e2e', type: 'string', isOptional: true }
+ ]
+ }),
+ addColumns({
+ table: 'threads',
+ columns: [
+ { name: 'e2e', type: 'string', isOptional: true }
+ ]
+ }),
+ addColumns({
+ table: 'rooms',
+ columns: [
+ { name: 'e2e_key_id', type: 'string', isOptional: true }
+ ]
+ })
+ ]
}
]
});
diff --git a/app/lib/database/model/serversMigrations.js b/app/lib/database/model/serversMigrations.js
index 8d74b0434..418a61dd1 100644
--- a/app/lib/database/model/serversMigrations.js
+++ b/app/lib/database/model/serversMigrations.js
@@ -1,4 +1,4 @@
-import { schemaMigrations, addColumns } from '@nozbe/watermelondb/Schema/migrations';
+import { schemaMigrations, addColumns, createTable } from '@nozbe/watermelondb/Schema/migrations';
export default schemaMigrations({
migrations: [
@@ -37,6 +37,52 @@ export default schemaMigrations({
]
})
]
+ },
+ {
+ toVersion: 6,
+ steps: [
+ addColumns({
+ table: 'servers',
+ columns: [
+ { name: 'enterprise_modules', type: 'string', isOptional: true }
+ ]
+ })
+ ]
+ },
+ {
+ toVersion: 7,
+ steps: [
+ addColumns({
+ table: 'users',
+ columns: [
+ { name: 'login_email_password', type: 'boolean', isOptional: true }
+ ]
+ })
+ ]
+ },
+ {
+ toVersion: 8,
+ steps: [
+ addColumns({
+ table: 'servers',
+ columns: [
+ { name: 'e2e_enable', type: 'boolean', isOptional: true }
+ ]
+ })
+ ]
+ },
+ {
+ toVersion: 9,
+ steps: [
+ createTable({
+ name: 'servers_history',
+ columns: [
+ { name: 'url', type: 'string', isIndexed: true },
+ { name: 'username', type: 'string', isOptional: true },
+ { name: 'updated_at', type: 'number' }
+ ]
+ })
+ ]
}
]
});
diff --git a/app/lib/database/schema/app.js b/app/lib/database/schema/app.js
index 59336e3f0..3a1935cac 100644
--- a/app/lib/database/schema/app.js
+++ b/app/lib/database/schema/app.js
@@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
- version: 9,
+ version: 10,
tables: [
tableSchema({
name: 'subscriptions',
@@ -49,7 +49,10 @@ export default appSchema({
{ name: 'department_id', type: 'string', isOptional: true },
{ name: 'served_by', type: 'string', isOptional: true },
{ name: 'livechat_data', type: 'string', isOptional: true },
- { name: 'tags', type: 'string', isOptional: true }
+ { name: 'tags', type: 'string', isOptional: true },
+ { name: 'e2e_key', type: 'string', isOptional: true },
+ { name: 'encrypted', type: 'boolean', isOptional: true },
+ { name: 'e2e_key_id', type: 'string', isOptional: true }
]
}),
tableSchema({
@@ -63,7 +66,8 @@ export default appSchema({
{ name: 'department_id', type: 'string', isOptional: true },
{ name: 'served_by', type: 'string', isOptional: true },
{ name: 'livechat_data', type: 'string', isOptional: true },
- { name: 'tags', type: 'string', isOptional: true }
+ { name: 'tags', type: 'string', isOptional: true },
+ { name: 'e2e_key_id', type: 'string', isOptional: true }
]
}),
tableSchema({
@@ -101,7 +105,8 @@ export default appSchema({
{ name: 'auto_translate', type: 'boolean', isOptional: true },
{ name: 'translations', type: 'string', isOptional: true },
{ name: 'tmsg', type: 'string', isOptional: true },
- { name: 'blocks', type: 'string', isOptional: true }
+ { name: 'blocks', type: 'string', isOptional: true },
+ { name: 'e2e', type: 'string', isOptional: true }
]
}),
tableSchema({
@@ -137,7 +142,8 @@ export default appSchema({
{ name: 'channels', type: 'string', isOptional: true },
{ name: 'unread', type: 'boolean', isOptional: true },
{ name: 'auto_translate', type: 'boolean', isOptional: true },
- { name: 'translations', type: 'string', isOptional: true }
+ { name: 'translations', type: 'string', isOptional: true },
+ { name: 'e2e', type: 'string', isOptional: true }
]
}),
tableSchema({
@@ -173,7 +179,8 @@ export default appSchema({
{ name: 'channels', type: 'string', isOptional: true },
{ name: 'unread', type: 'boolean', isOptional: true },
{ name: 'auto_translate', type: 'boolean', isOptional: true },
- { name: 'translations', type: 'string', isOptional: true }
+ { name: 'translations', type: 'string', isOptional: true },
+ { name: 'e2e', type: 'string', isOptional: true }
]
}),
tableSchema({
diff --git a/app/lib/database/schema/servers.js b/app/lib/database/schema/servers.js
index b02859e10..bd4806643 100644
--- a/app/lib/database/schema/servers.js
+++ b/app/lib/database/schema/servers.js
@@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
- version: 5,
+ version: 9,
tables: [
tableSchema({
name: 'users',
@@ -12,7 +12,8 @@ export default appSchema({
{ name: 'language', type: 'string', isOptional: true },
{ name: 'status', type: 'string', isOptional: true },
{ name: 'statusText', type: 'string', isOptional: true },
- { name: 'roles', type: 'string', isOptional: true }
+ { name: 'roles', type: 'string', isOptional: true },
+ { name: 'login_email_password', type: 'boolean', isOptional: true }
]
}),
tableSchema({
@@ -29,7 +30,17 @@ export default appSchema({
{ name: 'auto_lock', type: 'boolean', isOptional: true },
{ name: 'auto_lock_time', type: 'number', isOptional: true },
{ name: 'biometry', type: 'boolean', isOptional: true },
- { name: 'unique_id', type: 'string', isOptional: true }
+ { name: 'unique_id', type: 'string', isOptional: true },
+ { name: 'enterprise_modules', type: 'string', isOptional: true },
+ { name: 'e2e_enable', type: 'boolean', isOptional: true }
+ ]
+ }),
+ tableSchema({
+ name: 'servers_history',
+ columns: [
+ { name: 'url', type: 'string', isIndexed: true },
+ { name: 'username', type: 'string', isOptional: true },
+ { name: 'updated_at', type: 'number' }
]
})
]
diff --git a/app/lib/database/utils.js b/app/lib/database/utils.js
index 8e5800304..472af733b 100644
--- a/app/lib/database/utils.js
+++ b/app/lib/database/utils.js
@@ -1 +1,7 @@
+import XRegExp from 'xregexp';
+
+// Matches letters from any alphabet and numbers
+const likeStringRegex = new XRegExp('[^\\p{L}\\p{Nd}]', 'g');
+export const sanitizeLikeString = str => str?.replace(likeStringRegex, '_');
+
export const sanitizer = r => r;
diff --git a/app/lib/database/utils.test.js b/app/lib/database/utils.test.js
new file mode 100644
index 000000000..cbeae7b8a
--- /dev/null
+++ b/app/lib/database/utils.test.js
@@ -0,0 +1,42 @@
+/* eslint-disable no-undef */
+import * as utils from './utils';
+
+describe('sanitizeLikeStringTester', () => {
+ // example chars that shouldn't return
+ const disallowedChars = ',./;[]!@#$%^&*()_-=+~';
+ const sanitizeLikeStringTester = str => expect(utils.sanitizeLikeString(`${ str }${ disallowedChars }`)).toBe(`${ str }${ '_'.repeat(disallowedChars.length) }`);
+
+ test('render empty', () => {
+ expect(utils.sanitizeLikeString(null)).toBe(undefined);
+ expect(utils.sanitizeLikeString('')).toBe('');
+ expect(utils.sanitizeLikeString(undefined)).toBe(undefined);
+ });
+
+ // Testing a couple of different alphabets
+ test('render test (latin)', () => {
+ sanitizeLikeStringTester('test123');
+ });
+
+ test('render test (arabic)', () => {
+ sanitizeLikeStringTester('اختبار123');
+ });
+
+ test('render test (russian)', () => {
+ sanitizeLikeStringTester('тест123');
+ });
+
+ test('render test (chinese trad)', () => {
+ sanitizeLikeStringTester('測試123');
+ });
+
+ test('render test (japanese)', () => {
+ sanitizeLikeStringTester('テスト123');
+ });
+});
+
+describe('sanitizer', () => {
+ test('render the same result', () => {
+ const content = { a: true };
+ expect(utils.sanitizer(content)).toBe(content);
+ });
+});
diff --git a/app/lib/encryption/README.md b/app/lib/encryption/README.md
new file mode 100644
index 000000000..8b44ddadb
--- /dev/null
+++ b/app/lib/encryption/README.md
@@ -0,0 +1,28 @@
+# Rocket.Chat Mobile
+
+## E2E Encryption
+
+> Note: This feature is currently in beta. Uploads will not be encrypted in this version.
+You can check [this documentation](https://docs.rocket.chat/guides/user-guides/end-to-end-encryption) for further information about the web client.
+
+### How it works
+
+- Each user has a public and private key (asymmetric cryptography).
+- The user private key is stored encrypted on the server and it can be decrypted on clients only using the user E2E encryption password.
+- A room key is generated using the public key of each room member (symmetric cryptography).
+- Users can decrypt the room key using their private key.
+- Each room has a unique identifier which make users able to request a room key.
+- The room unique identifier is called `e2eKeyId` and it's a property of the `room` collection.
+- The room key is called `E2EKey` and it's a property of the `subscription` collection.
+- After the room key is decrypted, the user is able to encrypt and decrypt messages of the room.
+
+### User keys
+
+* If the user doesn't have keys neither locally nor on the server, we create and encrypt them using a random password. These encrypted keys are sent to the server (so other clients can fetch) and saved locally.
+* If the user have keys stored on server, but doesn't have them stored locally, we fetch them from the server and request a password to decrypt the keys.
+
+### Room keys
+
+* If the room has a `E2EKey`, we decrypt it using the user key.
+* If the room doesn't have a `E2EKey`, but has a `e2eKeyId`, we *emit an event* on _stream-notify-room-users_ sending the `roomId` and the `e2eKeyId` requesting the `E2EKey` from any online room member.
+* If the room have none of them, we create new ones and send them back to the server.
diff --git a/app/lib/encryption/constants.js b/app/lib/encryption/constants.js
new file mode 100644
index 000000000..216746a7b
--- /dev/null
+++ b/app/lib/encryption/constants.js
@@ -0,0 +1,17 @@
+export const E2E_MESSAGE_TYPE = 'e2e';
+export const E2E_PUBLIC_KEY = 'RC_E2E_PUBLIC_KEY';
+export const E2E_PRIVATE_KEY = 'RC_E2E_PRIVATE_KEY';
+export const E2E_RANDOM_PASSWORD_KEY = 'RC_E2E_RANDOM_PASSWORD_KEY';
+export const E2E_REFRESH_MESSAGES_KEY = 'E2E_REFRESH_MESSAGES_KEY';
+export const E2E_STATUS = {
+ PENDING: 'pending',
+ DONE: 'done'
+};
+export const E2E_BANNER_TYPE = {
+ REQUEST_PASSWORD: 'REQUEST_PASSWORD',
+ SAVE_PASSWORD: 'SAVE_PASSWORD'
+};
+export const E2E_ROOM_TYPES = {
+ d: 'd',
+ p: 'p'
+};
diff --git a/app/lib/encryption/encryption.js b/app/lib/encryption/encryption.js
new file mode 100644
index 000000000..b79c74369
--- /dev/null
+++ b/app/lib/encryption/encryption.js
@@ -0,0 +1,454 @@
+import EJSON from 'ejson';
+import SimpleCrypto from 'react-native-simple-crypto';
+import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
+import { Q } from '@nozbe/watermelondb';
+
+import {
+ toString,
+ utf8ToBuffer,
+ splitVectorData,
+ joinVectorData,
+ randomPassword
+} from './utils';
+import {
+ E2E_PUBLIC_KEY,
+ E2E_PRIVATE_KEY,
+ E2E_RANDOM_PASSWORD_KEY,
+ E2E_STATUS,
+ E2E_MESSAGE_TYPE,
+ E2E_BANNER_TYPE
+} from './constants';
+import RocketChat from '../rocketchat';
+import { EncryptionRoom } from './index';
+import UserPreferences from '../userPreferences';
+import database from '../database';
+import protectedFunction from '../methods/helpers/protectedFunction';
+import Deferred from '../../utils/deferred';
+import log from '../../utils/log';
+import store from '../createStore';
+
+class Encryption {
+ constructor() {
+ this.ready = false;
+ this.privateKey = null;
+ this.roomInstances = {};
+ this.readyPromise = new Deferred();
+ this.readyPromise
+ .then(() => {
+ this.ready = true;
+ })
+ .catch(() => {
+ this.ready = false;
+ });
+ }
+
+ // Initialize Encryption client
+ initialize = (userId) => {
+ this.userId = userId;
+ this.roomInstances = {};
+
+ // Don't await these promises
+ // so they can run parallelized
+ this.decryptPendingSubscriptions();
+ this.decryptPendingMessages();
+
+ // Mark Encryption client as ready
+ this.readyPromise.resolve();
+ }
+
+ get establishing() {
+ const { banner } = store.getState().encryption;
+ // If the password was not inserted yet
+ if (!banner || banner === E2E_BANNER_TYPE.REQUEST_PASSWORD) {
+ // We can't decrypt/encrypt, so, reject this try
+ return Promise.reject();
+ }
+
+ // Wait the client ready state
+ return this.readyPromise;
+ }
+
+ // Stop Encryption client
+ stop = () => {
+ this.userId = null;
+ this.privateKey = null;
+ this.roomInstances = {};
+ // Cancel ongoing encryption/decryption requests
+ this.readyPromise.reject();
+ // Reset Deferred
+ this.ready = false;
+ this.readyPromise = new Deferred();
+ this.readyPromise
+ .then(() => {
+ this.ready = true;
+ })
+ .catch(() => {
+ this.ready = false;
+ });
+ }
+
+ // When a new participant join and request a new room encryption key
+ provideRoomKeyToUser = async(keyId, rid) => {
+ // If the client is not ready
+ if (!this.ready) {
+ try {
+ // Wait for ready status
+ await this.establishing;
+ } catch {
+ // If it can't be initialized (missing password)
+ // return and don't provide a key
+ return;
+ }
+ }
+
+ const roomE2E = await this.getRoomInstance(rid);
+ return roomE2E.provideKeyToUser(keyId);
+ }
+
+ // Persist keys on UserPreferences
+ persistKeys = async(server, publicKey, privateKey) => {
+ this.privateKey = await SimpleCrypto.RSA.importKey(EJSON.parse(privateKey));
+ await UserPreferences.setStringAsync(`${ server }-${ E2E_PUBLIC_KEY }`, EJSON.stringify(publicKey));
+ await UserPreferences.setStringAsync(`${ server }-${ E2E_PRIVATE_KEY }`, privateKey);
+ }
+
+ // Could not obtain public-private keypair from server.
+ createKeys = async(userId, server) => {
+ // Generate new keys
+ const key = await SimpleCrypto.RSA.generateKeys(2048);
+
+ // Cast these keys to the properly server format
+ const publicKey = await SimpleCrypto.RSA.exportKey(key.public);
+ const privateKey = await SimpleCrypto.RSA.exportKey(key.private);
+
+ // Persist these new keys
+ this.persistKeys(server, publicKey, EJSON.stringify(privateKey));
+
+ // Create a password to encode the private key
+ const password = await this.createRandomPassword(server);
+
+ // Encode the private key
+ const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, userId);
+
+ // Send the new keys to the server
+ await RocketChat.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey);
+
+ // Request e2e keys of all encrypted rooms
+ await RocketChat.e2eRequestSubscriptionKeys();
+ }
+
+ // Encode a private key before send it to the server
+ encodePrivateKey = async(privateKey, password, userId) => {
+ const masterKey = await this.generateMasterKey(password, userId);
+
+ const vector = await SimpleCrypto.utils.randomBytes(16);
+ const data = await SimpleCrypto.AES.encrypt(
+ utf8ToBuffer(privateKey),
+ masterKey,
+ vector
+ );
+
+ return EJSON.stringify(new Uint8Array(joinVectorData(vector, data)));
+ }
+
+ // Decode a private key fetched from server
+ decodePrivateKey = async(privateKey, password, userId) => {
+ const masterKey = await this.generateMasterKey(password, userId);
+ const [vector, cipherText] = splitVectorData(EJSON.parse(privateKey));
+
+ const privKey = await SimpleCrypto.AES.decrypt(
+ cipherText,
+ masterKey,
+ vector
+ );
+
+ return toString(privKey);
+ }
+
+ // Generate a user master key, this is based on userId and a password
+ generateMasterKey = async(password, userId) => {
+ const iterations = 1000;
+ const hash = 'SHA256';
+ const keyLen = 32;
+
+ const passwordBuffer = utf8ToBuffer(password);
+ const saltBuffer = utf8ToBuffer(userId);
+
+ const masterKey = await SimpleCrypto.PBKDF2.hash(
+ passwordBuffer,
+ saltBuffer,
+ iterations,
+ keyLen,
+ hash
+ );
+
+ return masterKey;
+ }
+
+ // Create a random password to local created keys
+ createRandomPassword = async(server) => {
+ const password = randomPassword();
+ await UserPreferences.setStringAsync(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`, password);
+ return password;
+ }
+
+ // get a encryption room instance
+ getRoomInstance = async(rid) => {
+ // Prevent handshake again
+ if (this.roomInstances[rid]?.ready) {
+ return this.roomInstances[rid];
+ }
+
+ // If doesn't have a instance of this room
+ if (!this.roomInstances[rid]) {
+ this.roomInstances[rid] = new EncryptionRoom(rid, this.userId);
+ }
+
+ const roomE2E = this.roomInstances[rid];
+
+ // Start Encryption Room instance handshake
+ await roomE2E.handshake();
+
+ return roomE2E;
+ }
+
+ // Logic to decrypt all pending messages/threads/threadMessages
+ // after initialize the encryption client
+ decryptPendingMessages = async(roomId) => {
+ const db = database.active;
+
+ const messagesCollection = db.collections.get('messages');
+ const threadsCollection = db.collections.get('threads');
+ const threadMessagesCollection = db.collections.get('thread_messages');
+
+ // e2e status is null or 'pending' and message type is 'e2e'
+ const whereClause = [
+ Q.where('t', E2E_MESSAGE_TYPE),
+ Q.or(
+ Q.where('e2e', null),
+ Q.where('e2e', E2E_STATUS.PENDING)
+ )
+ ];
+
+ // decrypt messages of a room
+ if (roomId) {
+ whereClause.push(Q.where('rid', roomId));
+ }
+
+ try {
+ // Find all messages/threads/threadsMessages that have pending e2e status
+ const messagesToDecrypt = await messagesCollection.query(...whereClause).fetch();
+ const threadsToDecrypt = await threadsCollection.query(...whereClause).fetch();
+ const threadMessagesToDecrypt = await threadMessagesCollection.query(...whereClause).fetch();
+
+ // Concat messages/threads/threadMessages
+ let toDecrypt = [...messagesToDecrypt, ...threadsToDecrypt, ...threadMessagesToDecrypt];
+ toDecrypt = await Promise.all(toDecrypt.map(async(message) => {
+ const { t, msg, tmsg } = message;
+ const { id: rid } = message.subscription;
+ // WM Object -> Plain Object
+ const newMessage = await this.decryptMessage({
+ t,
+ rid,
+ msg,
+ tmsg
+ });
+ if (message._hasPendingUpdate) {
+ console.log(message);
+ return;
+ }
+ return message.prepareUpdate(protectedFunction((m) => {
+ Object.assign(m, newMessage);
+ }));
+ }));
+
+ await db.action(async() => {
+ await db.batch(...toDecrypt);
+ });
+ } catch (e) {
+ log(e);
+ }
+ }
+
+ // Logic to decrypt all pending subscriptions
+ // after initialize the encryption client
+ decryptPendingSubscriptions = async() => {
+ const db = database.active;
+ const subCollection = db.collections.get('subscriptions');
+ try {
+ // Find all rooms that can have a lastMessage encrypted
+ // If we select only encrypted rooms we can miss some room that changed their encrypted status
+ const subsEncrypted = await subCollection.query(Q.where('e2e_key_id', Q.notEq(null))).fetch();
+ // We can't do this on database level since lastMessage is not a database object
+ const subsToDecrypt = subsEncrypted.filter(sub => (
+ // Encrypted message
+ sub?.lastMessage?.t === E2E_MESSAGE_TYPE
+ // Message pending decrypt
+ && sub?.lastMessage?.e2e === E2E_STATUS.PENDING
+ ));
+ await Promise.all(subsToDecrypt.map(async(sub) => {
+ const { rid, lastMessage } = sub;
+ const newSub = await this.decryptSubscription({ rid, lastMessage });
+ if (sub._hasPendingUpdate) {
+ console.log(sub);
+ return;
+ }
+ return sub.prepareUpdate(protectedFunction((m) => {
+ Object.assign(m, newSub);
+ }));
+ }));
+
+ await db.action(async() => {
+ await db.batch(...subsToDecrypt);
+ });
+ } catch (e) {
+ log(e);
+ }
+ }
+
+ // Decrypt a subscription lastMessage
+ decryptSubscription = async(subscription) => {
+ // If the subscription doesn't have a lastMessage just return
+ if (!subscription?.lastMessage) {
+ return subscription;
+ }
+
+ const { lastMessage } = subscription;
+ const { t, e2e } = lastMessage;
+
+ // If it's not a encrypted message or was decrypted before
+ if (t !== E2E_MESSAGE_TYPE || e2e === E2E_STATUS.DONE) {
+ return subscription;
+ }
+
+ // If the client is not ready
+ if (!this.ready) {
+ try {
+ // Wait for ready status
+ await this.establishing;
+ } catch {
+ // If it can't be initialized (missing password)
+ // return the encrypted message
+ return subscription;
+ }
+ }
+
+ const { rid } = subscription;
+ const db = database.active;
+ const subCollection = db.collections.get('subscriptions');
+
+ let subRecord;
+ try {
+ subRecord = await subCollection.find(rid);
+ } catch {
+ // Do nothing
+ }
+
+ try {
+ const batch = [];
+ // If the subscription doesn't exists yet
+ if (!subRecord) {
+ // Let's create the subscription with the data received
+ batch.push(subCollection.prepareCreate((s) => {
+ s._raw = sanitizedRaw({ id: rid }, subCollection.schema);
+ Object.assign(s, subscription);
+ }));
+ // If the subscription already exists but doesn't have the E2EKey yet
+ } else if (!subRecord.E2EKey && subscription.E2EKey) {
+ if (!subRecord._hasPendingUpdate) {
+ // Let's update the subscription with the received E2EKey
+ batch.push(subRecord.prepareUpdate((s) => {
+ s.E2EKey = subscription.E2EKey;
+ }));
+ }
+ }
+
+ // If batch has some operation
+ if (batch.length) {
+ await db.action(async() => {
+ await db.batch(...batch);
+ });
+ }
+ } catch {
+ // Abort the decryption process
+ // Return as received
+ return subscription;
+ }
+
+ // Get a instance using the subscription
+ const roomE2E = await this.getRoomInstance(rid);
+ const decryptedMessage = await roomE2E.decrypt(lastMessage);
+ return {
+ ...subscription,
+ lastMessage: decryptedMessage
+ };
+ }
+
+ // Encrypt a message
+ encryptMessage = async(message) => {
+ const { rid } = message;
+ const db = database.active;
+ const subCollection = db.collections.get('subscriptions');
+
+ try {
+ // Find the subscription
+ const subRecord = await subCollection.find(rid);
+
+ // Subscription is not encrypted at the moment
+ if (!subRecord.encrypted) {
+ // Send a non encrypted message
+ return message;
+ }
+
+ // If the client is not ready
+ if (!this.ready) {
+ // Wait for ready status
+ await this.establishing;
+ }
+
+ const roomE2E = await this.getRoomInstance(rid);
+ return roomE2E.encrypt(message);
+ } catch {
+ // Subscription not found
+ // or client can't be initialized (missing password)
+ }
+
+ // Send a non encrypted message
+ return message;
+ }
+
+ // Decrypt a message
+ decryptMessage = async(message) => {
+ const { t, e2e } = message;
+
+ // Prevent create a new instance if this room was encrypted sometime ago
+ if (t !== E2E_MESSAGE_TYPE || e2e === E2E_STATUS.DONE) {
+ return message;
+ }
+
+ // If the client is not ready
+ if (!this.ready) {
+ try {
+ // Wait for ready status
+ await this.establishing;
+ } catch {
+ // If it can't be initialized (missing password)
+ // return the encrypted message
+ return message;
+ }
+ }
+
+ const { rid } = message;
+ const roomE2E = await this.getRoomInstance(rid);
+ return roomE2E.decrypt(message);
+ }
+
+ // Decrypt multiple messages
+ decryptMessages = messages => Promise.all(messages.map(m => this.decryptMessage(m)))
+
+ // Decrypt multiple subscriptions
+ decryptSubscriptions = subscriptions => Promise.all(subscriptions.map(s => this.decryptSubscription(s)))
+}
+
+const encryption = new Encryption();
+export default encryption;
diff --git a/app/lib/encryption/index.js b/app/lib/encryption/index.js
new file mode 100644
index 000000000..9033aa251
--- /dev/null
+++ b/app/lib/encryption/index.js
@@ -0,0 +1,4 @@
+import Encryption from './encryption';
+import EncryptionRoom from './room';
+
+export { Encryption, EncryptionRoom };
diff --git a/app/lib/encryption/room.js b/app/lib/encryption/room.js
new file mode 100644
index 000000000..835bd9ab5
--- /dev/null
+++ b/app/lib/encryption/room.js
@@ -0,0 +1,256 @@
+import EJSON from 'ejson';
+import { Base64 } from 'js-base64';
+import SimpleCrypto from 'react-native-simple-crypto';
+
+import {
+ toString,
+ b64ToBuffer,
+ bufferToUtf8,
+ bufferToB64,
+ bufferToB64URI,
+ utf8ToBuffer,
+ splitVectorData,
+ joinVectorData
+} from './utils';
+import { E2E_MESSAGE_TYPE, E2E_STATUS } from './constants';
+import RocketChat from '../rocketchat';
+import Deferred from '../../utils/deferred';
+import debounce from '../../utils/debounce';
+import { Encryption } from './index';
+import database from '../database';
+import log from '../../utils/log';
+
+export default class EncryptionRoom {
+ constructor(roomId, userId) {
+ this.ready = false;
+ this.roomId = roomId;
+ this.userId = userId;
+ this.establishing = false;
+ this.readyPromise = new Deferred();
+ this.readyPromise.then(() => {
+ // Mark as ready
+ this.ready = true;
+ // Mark as established
+ this.establishing = false;
+ });
+ }
+
+ // Initialize the E2E room
+ handshake = async() => {
+ // If it's already ready we don't need to handshake again
+ if (this.ready) {
+ return;
+ }
+
+ // If it's already establishing
+ if (this.establishing) {
+ // Return the ready promise to wait this client ready
+ return this.readyPromise;
+ }
+
+ const db = database.active;
+ const subCollection = db.collections.get('subscriptions');
+ try {
+ // Find the subscription
+ const subscription = await subCollection.find(this.roomId);
+
+ const { E2EKey, e2eKeyId } = subscription;
+
+ // If this room has a E2EKey, we import it
+ if (E2EKey) {
+ // We're establishing a new room encryption client
+ this.establishing = true;
+ await this.importRoomKey(E2EKey, Encryption.privateKey);
+ this.readyPromise.resolve();
+ return;
+ }
+
+ // If it doesn't have a e2eKeyId, we need to create keys to the room
+ if (!e2eKeyId) {
+ // We're establishing a new room encryption client
+ this.establishing = true;
+ await this.createRoomKey();
+ this.readyPromise.resolve();
+ return;
+ }
+
+ // Request a E2EKey for this room to other users
+ await this.requestRoomKey(e2eKeyId);
+ } catch (e) {
+ log(e);
+ }
+ }
+
+ // Import roomKey as an AES Decrypt key
+ importRoomKey = async(E2EKey, privateKey) => {
+ const roomE2EKey = E2EKey.slice(12);
+
+ const decryptedKey = await SimpleCrypto.RSA.decrypt(roomE2EKey, privateKey);
+ this.sessionKeyExportedString = toString(decryptedKey);
+
+ this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);
+
+ // Extract K from Web Crypto Secret Key
+ // K is a base64URL encoded array of bytes
+ // Web Crypto API uses this as a private key to decrypt/encrypt things
+ // Reference: https://www.javadoc.io/doc/com.nimbusds/nimbus-jose-jwt/5.1/com/nimbusds/jose/jwk/OctetSequenceKey.html
+ const { k } = EJSON.parse(this.sessionKeyExportedString);
+ this.roomKey = b64ToBuffer(k);
+ }
+
+ // Create a key to a room
+ createRoomKey = async() => {
+ const key = await SimpleCrypto.utils.randomBytes(16);
+ this.roomKey = key;
+
+ // Web Crypto format of a Secret Key
+ const sessionKeyExported = {
+ // Type of Secret Key
+ kty: 'oct',
+ // Algorithm
+ alg: 'A128CBC',
+ // Base64URI encoded array of bytes
+ k: bufferToB64URI(this.roomKey),
+ // Specific Web Crypto properties
+ ext: true,
+ key_ops: ['encrypt', 'decrypt']
+ };
+
+ this.sessionKeyExportedString = EJSON.stringify(sessionKeyExported);
+ this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);
+
+ await RocketChat.e2eSetRoomKeyID(this.roomId, this.keyID);
+
+ await this.encryptRoomKey();
+ }
+
+ // Request a key to this room
+ // We're debouncing this function to avoid multiple calls
+ // when you join a room with a lot of messages and nobody
+ // can send the encryption key at the moment.
+ // Each time you see a encrypted message of a room that you don't have a key
+ // this will be called again and run once in 5 seconds
+ requestRoomKey = debounce(async(e2eKeyId) => {
+ await RocketChat.e2eRequestRoomKey(this.roomId, e2eKeyId);
+ }, 5000, true)
+
+ // Create an encrypted key for this room based on users
+ encryptRoomKey = async() => {
+ const result = await RocketChat.e2eGetUsersOfRoomWithoutKey(this.roomId);
+ if (result.success) {
+ const { users } = result;
+ await Promise.all(users.map(user => this.encryptRoomKeyForUser(user)));
+ }
+ }
+
+ // Encrypt the room key to each user in
+ encryptRoomKeyForUser = async(user) => {
+ if (user?.e2e?.public_key) {
+ const { public_key: publicKey } = user.e2e;
+ const userKey = await SimpleCrypto.RSA.importKey(EJSON.parse(publicKey));
+ const encryptedUserKey = await SimpleCrypto.RSA.encrypt(this.sessionKeyExportedString, userKey);
+ await RocketChat.e2eUpdateGroupKey(user?._id, this.roomId, this.keyID + encryptedUserKey);
+ }
+ }
+
+ // Provide this room key to a user
+ provideKeyToUser = async(keyId) => {
+ // Don't provide a key if the keyId received
+ // is different than the current one
+ if (this.keyID !== keyId) {
+ return;
+ }
+
+ await this.encryptRoomKey();
+ }
+
+ // Encrypt text
+ encryptText = async(text) => {
+ text = utf8ToBuffer(text);
+ const vector = await SimpleCrypto.utils.randomBytes(16);
+ const data = await SimpleCrypto.AES.encrypt(
+ text,
+ this.roomKey,
+ vector
+ );
+
+ return this.keyID + bufferToB64(joinVectorData(vector, data));
+ }
+
+ // Encrypt messages
+ encrypt = async(message) => {
+ if (!this.ready) {
+ return message;
+ }
+
+ try {
+ const msg = await this.encryptText(EJSON.stringify({
+ _id: message._id,
+ text: message.msg,
+ userId: this.userId,
+ ts: new Date()
+ }));
+
+ return {
+ ...message,
+ t: E2E_MESSAGE_TYPE,
+ e2e: E2E_STATUS.PENDING,
+ msg
+ };
+ } catch {
+ // Do nothing
+ }
+
+ return message;
+ }
+
+ // Decrypt text
+ decryptText = async(msg) => {
+ msg = b64ToBuffer(msg.slice(12));
+ const [vector, cipherText] = splitVectorData(msg);
+
+ const decrypted = await SimpleCrypto.AES.decrypt(
+ cipherText,
+ this.roomKey,
+ vector
+ );
+
+ const m = EJSON.parse(bufferToUtf8(decrypted));
+
+ return m.text;
+ }
+
+ // Decrypt messages
+ decrypt = async(message) => {
+ if (!this.ready) {
+ return message;
+ }
+
+ try {
+ const { t, e2e } = message;
+
+ // If message type is e2e and it's encrypted still
+ if (t === E2E_MESSAGE_TYPE && e2e !== E2E_STATUS.DONE) {
+ let { msg, tmsg } = message;
+ // Decrypt msg
+ msg = await this.decryptText(msg);
+
+ // Decrypt tmsg
+ if (tmsg) {
+ tmsg = await this.decryptText(tmsg);
+ }
+
+ return {
+ ...message,
+ tmsg,
+ msg,
+ e2e: E2E_STATUS.DONE
+ };
+ }
+ } catch {
+ // Do nothing
+ }
+
+ return message;
+ }
+}
diff --git a/app/lib/encryption/utils.js b/app/lib/encryption/utils.js
new file mode 100644
index 000000000..492ea0066
--- /dev/null
+++ b/app/lib/encryption/utils.js
@@ -0,0 +1,60 @@
+/* eslint-disable no-bitwise */
+import ByteBuffer from 'bytebuffer';
+import SimpleCrypto from 'react-native-simple-crypto';
+
+import random from '../../utils/random';
+import { fromByteArray, toByteArray } from '../../utils/base64-js';
+
+const BASE64URI = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
+
+export const b64ToBuffer = base64 => toByteArray(base64).buffer;
+export const utf8ToBuffer = SimpleCrypto.utils.convertUtf8ToArrayBuffer;
+export const bufferToB64 = arrayBuffer => fromByteArray(new Uint8Array(arrayBuffer));
+// ArrayBuffer -> Base64 URI Safe
+// https://github.com/herrjemand/Base64URL-ArrayBuffer/blob/master/lib/base64url-arraybuffer.js
+export const bufferToB64URI = (buffer) => {
+ const uintArray = new Uint8Array(buffer);
+ const len = uintArray.length;
+ let base64 = '';
+
+ for (let i = 0; i < len; i += 3) {
+ base64 += BASE64URI[uintArray[i] >> 2];
+ base64 += BASE64URI[((uintArray[i] & 3) << 4) | (uintArray[i + 1] >> 4)];
+ base64 += BASE64URI[((uintArray[i + 1] & 15) << 2) | (uintArray[i + 2] >> 6)];
+ base64 += BASE64URI[uintArray[i + 2] & 63];
+ }
+
+ if ((len % 3) === 2) {
+ base64 = base64.substring(0, base64.length - 1);
+ } else if (len % 3 === 1) {
+ base64 = base64.substring(0, base64.length - 2);
+ }
+
+ return base64;
+};
+// SimpleCrypto.utils.convertArrayBufferToUtf8 is not working with unicode emoji
+export const bufferToUtf8 = (buffer) => {
+ const uintArray = new Uint8Array(buffer);
+ const encodedString = String.fromCharCode.apply(null, uintArray);
+ const decodedString = decodeURIComponent(escape(encodedString));
+ return decodedString;
+};
+export const splitVectorData = (text) => {
+ const vector = text.slice(0, 16);
+ const data = text.slice(16);
+ return [vector, data];
+};
+export const joinVectorData = (vector, data) => {
+ const output = new Uint8Array(vector.byteLength + data.byteLength);
+ output.set(new Uint8Array(vector), 0);
+ output.set(new Uint8Array(data), vector.byteLength);
+ return output.buffer;
+};
+export const toString = (thing) => {
+ if (typeof thing === 'string') {
+ return thing;
+ }
+ // eslint-disable-next-line new-cap
+ return new ByteBuffer.wrap(thing).toString('binary');
+};
+export const randomPassword = () => `${ random(3) }-${ random(3) }-${ random(3) }`.toLowerCase();
diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js
new file mode 100644
index 000000000..4a0d1b38d
--- /dev/null
+++ b/app/lib/methods/enterpriseModules.js
@@ -0,0 +1,68 @@
+import semver from 'semver';
+
+import reduxStore from '../createStore';
+import database from '../database';
+import log from '../../utils/log';
+import { setEnterpriseModules as setEnterpriseModulesAction, clearEnterpriseModules } from '../../actions/enterpriseModules';
+
+export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise';
+export const LICENSE_LIVECHAT_ENTERPRISE = 'livechat-enterprise';
+
+export async function setEnterpriseModules() {
+ try {
+ const { server: serverId } = reduxStore.getState().server;
+ const serversDB = database.servers;
+ const serversCollection = serversDB.collections.get('servers');
+ let server;
+ try {
+ server = await serversCollection.find(serverId);
+ } catch {
+ // Server not found
+ }
+ if (server?.enterpriseModules) {
+ reduxStore.dispatch(setEnterpriseModulesAction(server.enterpriseModules.split(',')));
+ return;
+ }
+ reduxStore.dispatch(clearEnterpriseModules());
+ } catch (e) {
+ log(e);
+ }
+}
+
+export function getEnterpriseModules() {
+ return new Promise(async(resolve) => {
+ try {
+ const { version: serverVersion, server: serverId } = reduxStore.getState().server;
+ if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) {
+ // RC 3.1.0
+ const enterpriseModules = await this.methodCallWrapper('license:getModules');
+ if (enterpriseModules) {
+ const serversDB = database.servers;
+ const serversCollection = serversDB.collections.get('servers');
+ const server = await serversCollection.find(serverId);
+ await serversDB.action(async() => {
+ await server.update((s) => {
+ s.enterpriseModules = enterpriseModules.join(',');
+ });
+ });
+ reduxStore.dispatch(setEnterpriseModulesAction(enterpriseModules));
+ return resolve();
+ }
+ }
+ reduxStore.dispatch(clearEnterpriseModules());
+ } catch (e) {
+ log(e);
+ }
+ return resolve();
+ });
+}
+
+export function hasLicense(module) {
+ const { enterpriseModules } = reduxStore.getState();
+ return enterpriseModules.includes(module);
+}
+
+export function isOmnichannelModuleAvailable() {
+ const { enterpriseModules } = reduxStore.getState();
+ return [LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE, LICENSE_LIVECHAT_ENTERPRISE].some(module => enterpriseModules.includes(module));
+}
diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js
index 2ebad1ed2..f433f4a9a 100644
--- a/app/lib/methods/getSettings.js
+++ b/app/lib/methods/getSettings.js
@@ -11,7 +11,16 @@ import protectedFunction from './helpers/protectedFunction';
import fetch from '../../utils/fetch';
import { DEFAULT_AUTO_LOCK } from '../../constants/localAuthentication';
-const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After', 'uniqueID'];
+const serverInfoKeys = [
+ 'Site_Name',
+ 'UI_Use_Real_Name',
+ 'FileUpload_MediaTypeWhiteList',
+ 'FileUpload_MaxFileSize',
+ 'Force_Screen_Lock',
+ 'Force_Screen_Lock_After',
+ 'uniqueID',
+ 'E2E_Enable'
+];
// these settings are used only on onboarding process
const loginSettings = [
@@ -71,6 +80,9 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => {
if (setting._id === 'uniqueID') {
return { ...allSettings, uniqueID: setting.valueAsString };
}
+ if (setting._id === 'E2E_Enable') {
+ return { ...allSettings, E2E_Enable: setting.valueAsBoolean };
+ }
return allSettings;
}, {});
@@ -138,7 +150,11 @@ export default async function() {
// filter server info
const serverInfo = filteredSettings.filter(i1 => serverInfoKeys.includes(i1._id));
const iconSetting = data.find(item => item._id === 'Assets_favicon_512');
- await serverInfoUpdate(serverInfo, iconSetting);
+ try {
+ await serverInfoUpdate(serverInfo, iconSetting);
+ } catch {
+ // Server not found
+ }
await db.action(async() => {
const settingsCollection = db.collections.get('settings');
diff --git a/app/lib/methods/helpers/findSubscriptionsRooms.js b/app/lib/methods/helpers/findSubscriptionsRooms.js
index 457fc3b5f..fb3bc6d33 100644
--- a/app/lib/methods/helpers/findSubscriptionsRooms.js
+++ b/app/lib/methods/helpers/findSubscriptionsRooms.js
@@ -49,7 +49,10 @@ export default async(subscriptions = [], rooms = []) => {
departmentId: s.departmentId,
servedBy: s.servedBy,
livechatData: s.livechatData,
- tags: s.tags
+ tags: s.tags,
+ encrypted: s.encrypted,
+ e2eKeyId: s.e2eKeyId,
+ E2EKey: s.E2EKey
}));
subscriptions = subscriptions.concat(existingSubs);
@@ -75,7 +78,9 @@ export default async(subscriptions = [], rooms = []) => {
departmentId: r.departmentId,
servedBy: r.servedBy,
livechatData: r.livechatData,
- tags: r.tags
+ tags: r.tags,
+ encrypted: r.encrypted,
+ e2eKeyId: r.e2eKeyId
}));
rooms = rooms.concat(existingRooms);
} catch {
diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
index c4d3acd40..c529102f2 100644
--- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js
+++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
@@ -2,6 +2,7 @@ import EJSON from 'ejson';
import normalizeMessage from './normalizeMessage';
import findSubscriptionsRooms from './findSubscriptionsRooms';
+import { Encryption } from '../../encryption';
// TODO: delete and update
export const merge = (subscription, room) => {
@@ -27,6 +28,8 @@ export const merge = (subscription, room) => {
}
subscription.ro = room.ro;
subscription.broadcast = room.broadcast;
+ subscription.encrypted = room.encrypted;
+ subscription.e2eKeyId = room.e2eKeyId;
if (!subscription.roles || !subscription.roles.length) {
subscription.roles = [];
}
@@ -72,17 +75,23 @@ export default async(subscriptions = [], rooms = []) => {
rooms = rooms.update;
}
+ // Find missing rooms/subscriptions on local database
({ subscriptions, rooms } = await findSubscriptionsRooms(subscriptions, rooms));
+ // Merge each subscription into a room
+ subscriptions = subscriptions.map((s) => {
+ const index = rooms.findIndex(({ _id }) => _id === s.rid);
+ // Room not found
+ if (index < 0) {
+ return merge(s);
+ }
+ const [room] = rooms.splice(index, 1);
+ return merge(s, room);
+ });
+ // Decrypt all subscriptions missing decryption
+ subscriptions = await Encryption.decryptSubscriptions(subscriptions);
return {
- subscriptions: subscriptions.map((s) => {
- const index = rooms.findIndex(({ _id }) => _id === s.rid);
- if (index < 0) {
- return merge(s);
- }
- const [room] = rooms.splice(index, 1);
- return merge(s, room);
- }),
+ subscriptions,
rooms
};
};
diff --git a/app/lib/methods/loadThreadMessages.js b/app/lib/methods/loadThreadMessages.js
index 91369fa8b..4dc8a05fd 100644
--- a/app/lib/methods/loadThreadMessages.js
+++ b/app/lib/methods/loadThreadMessages.js
@@ -6,6 +6,7 @@ import buildMessage from './helpers/buildMessage';
import database from '../database';
import log from '../../utils/log';
import protectedFunction from './helpers/protectedFunction';
+import { Encryption } from '../encryption';
async function load({ tmid, offset }) {
try {
@@ -32,6 +33,7 @@ export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
InteractionManager.runAfterInteractions(async() => {
try {
data = data.map(m => buildMessage(m));
+ data = await Encryption.decryptMessages(data);
const db = database.active;
const threadMessagesCollection = db.collections.get('thread_messages');
const allThreadMessagesRecords = await threadMessagesCollection.query(Q.where('rid', tmid)).fetch();
diff --git a/app/lib/methods/logout.js b/app/lib/methods/logout.js
index 4064362de..0a1923de0 100644
--- a/app/lib/methods/logout.js
+++ b/app/lib/methods/logout.js
@@ -1,30 +1,34 @@
-import RNUserDefaults from 'rn-user-defaults';
import * as FileSystem from 'expo-file-system';
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
-import { SERVERS, SERVER_URL } from '../../constants/userDefaults';
import { getDeviceToken } from '../../notifications/push';
import { extractHostname } from '../../utils/server';
import { BASIC_AUTH_KEY } from '../../utils/fetch';
import database, { getDatabase } from '../database';
import RocketChat from '../rocketchat';
import { useSsl } from '../../utils/url';
+import {
+ E2E_PUBLIC_KEY,
+ E2E_PRIVATE_KEY,
+ E2E_RANDOM_PASSWORD_KEY
+} from '../encryption/constants';
+import UserPreferences from '../userPreferences';
async function removeServerKeys({ server, userId }) {
- await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ server }`);
- await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ userId }`);
- await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`);
+ await UserPreferences.removeItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ await UserPreferences.removeItem(`${ RocketChat.TOKEN_KEY }-${ userId }`);
+ await UserPreferences.removeItem(`${ BASIC_AUTH_KEY }-${ server }`);
+ await UserPreferences.removeItem(`${ server }-${ E2E_PUBLIC_KEY }`);
+ await UserPreferences.removeItem(`${ server }-${ E2E_PRIVATE_KEY }`);
+ await UserPreferences.removeItem(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`);
}
async function removeSharedCredentials({ server }) {
+ // clear certificate for server - SSL Pinning
try {
- const servers = await RNUserDefaults.objectForKey(SERVERS);
- await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server));
-
- // clear certificate for server - SSL Pinning
- const certificate = await RNUserDefaults.objectForKey(extractHostname(server));
+ const certificate = await UserPreferences.getMapAsync(extractHostname(server));
if (certificate && certificate.path) {
- await RNUserDefaults.clear(extractHostname(server));
+ await UserPreferences.removeItem(extractHostname(server));
await FileSystem.deleteAsync(certificate.path);
}
} catch (e) {
@@ -36,7 +40,7 @@ async function removeServerData({ server }) {
try {
const batch = [];
const serversDB = database.servers;
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const usersCollection = serversDB.collections.get('users');
if (userId) {
@@ -56,8 +60,8 @@ async function removeServerData({ server }) {
}
async function removeCurrentServer() {
- await RNUserDefaults.clear('currentServer');
- await RNUserDefaults.clear(RocketChat.TOKEN_KEY);
+ await UserPreferences.removeItem(RocketChat.CURRENT_SERVER);
+ await UserPreferences.removeItem(RocketChat.TOKEN_KEY);
}
async function removeServerDatabase({ server }) {
@@ -71,9 +75,9 @@ async function removeServerDatabase({ server }) {
export async function removeServer({ server }) {
try {
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (userId) {
- const resume = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ userId }`);
+ const resume = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ userId }`);
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
await sdk.login({ resume });
@@ -89,7 +93,7 @@ export async function removeServer({ server }) {
await removeServerData({ server });
await removeServerDatabase({ server });
} catch (e) {
- console.log('removePush', e);
+ console.log('removeServer', e);
}
}
diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js
index 2a91637a9..14def098b 100644
--- a/app/lib/methods/sendMessage.js
+++ b/app/lib/methods/sendMessage.js
@@ -4,6 +4,8 @@ import messagesStatus from '../../constants/messagesStatus';
import database from '../database';
import log from '../../utils/log';
import random from '../../utils/random';
+import { Encryption } from '../encryption';
+import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../encryption/constants';
const changeMessageStatus = async(id, tmid, status, message) => {
const db = database.active;
@@ -44,17 +46,11 @@ const changeMessageStatus = async(id, tmid, status, message) => {
};
export async function sendMessageCall(message) {
- const {
- id: _id, subscription: { id: rid }, msg, tmid
- } = message;
+ const { _id, tmid } = message;
try {
const sdk = this.shareSDK || this.sdk;
// RC 0.60.0
- const result = await sdk.post('chat.sendMessage', {
- message: {
- _id, rid, msg, tmid
- }
- });
+ const result = await sdk.post('chat.sendMessage', { message });
if (result.success) {
return changeMessageStatus(_id, tmid, messagesStatus.SENT, result.message);
}
@@ -64,6 +60,32 @@ export async function sendMessageCall(message) {
return changeMessageStatus(_id, tmid, messagesStatus.ERROR);
}
+export async function resendMessage(message, tmid) {
+ const db = database.active;
+ try {
+ await db.action(async() => {
+ await message.update((m) => {
+ m.status = messagesStatus.TEMP;
+ });
+ });
+ let m = {
+ _id: message.id,
+ rid: message.subscription.id,
+ msg: message.msg
+ };
+ if (tmid) {
+ m = {
+ ...m,
+ tmid
+ };
+ }
+ m = await Encryption.encryptMessage(m);
+ await sendMessageCall.call(this, m);
+ } catch (e) {
+ log(e);
+ }
+}
+
export default async function(rid, msg, tmid, user) {
try {
const db = database.active;
@@ -73,9 +95,12 @@ export default async function(rid, msg, tmid, user) {
const threadMessagesCollection = db.collections.get('thread_messages');
const messageId = random(17);
const batch = [];
- const message = {
- id: messageId, subscription: { id: rid }, msg, tmid
+
+ let message = {
+ _id: messageId, rid, msg, tmid
};
+ message = await Encryption.encryptMessage(message);
+
const messageDate = new Date();
let tMessageRecord;
@@ -106,6 +131,10 @@ export default async function(rid, msg, tmid, user) {
tm._updatedAt = messageDate;
tm.status = messagesStatus.SENT; // Original message was sent already
tm.u = tMessageRecord.u;
+ tm.t = message.t;
+ if (message.t === E2E_MESSAGE_TYPE) {
+ tm.e2e = E2E_STATUS.DONE;
+ }
})
);
}
@@ -124,6 +153,10 @@ export default async function(rid, msg, tmid, user) {
_id: user.id || '1',
username: user.username
};
+ tm.t = message.t;
+ if (message.t === E2E_MESSAGE_TYPE) {
+ tm.e2e = E2E_STATUS.DONE;
+ }
})
);
} catch (e) {
@@ -149,6 +182,10 @@ export default async function(rid, msg, tmid, user) {
m.tlm = messageDate;
m.tmsg = tMessageRecord.msg;
}
+ m.t = message.t;
+ if (message.t === E2E_MESSAGE_TYPE) {
+ m.e2e = E2E_STATUS.DONE;
+ }
})
);
diff --git a/app/lib/methods/subscriptions/room.js b/app/lib/methods/subscriptions/room.js
index 320aab52e..71ac3dfee 100644
--- a/app/lib/methods/subscriptions/room.js
+++ b/app/lib/methods/subscriptions/room.js
@@ -11,6 +11,7 @@ import { addUserTyping, removeUserTyping, clearUserTyping } from '../../../actio
import debounce from '../../../utils/debounce';
import RocketChat from '../../rocketchat';
import { subscribeRoom, unsubscribeRoom } from '../../../actions/room';
+import { Encryption } from '../../encryption';
const WINDOW_TIME = 1000;
@@ -46,6 +47,7 @@ export default class RoomSubscription {
unsubscribe = async() => {
console.log(`[RCRN] Unsubscribing from room ${ this.rid }`);
this.isAlive = false;
+ reduxStore.dispatch(unsubscribeRoom(this.rid));
if (this.promises) {
try {
const subscriptions = await this.promises || [];
@@ -62,8 +64,6 @@ export default class RoomSubscription {
if (this.timer) {
clearTimeout(this.timer);
}
-
- reduxStore.dispatch(unsubscribeRoom(this.rid));
}
removeListener = async(promise) => {
@@ -163,6 +163,9 @@ export default class RoomSubscription {
const threadsCollection = db.collections.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages');
+ // Decrypt the message if necessary
+ message = await Encryption.decryptMessage(message);
+
// Create or update message
try {
const messageRecord = await msgCollection.find(message._id);
diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js
index 52e474f14..eee596e86 100644
--- a/app/lib/methods/subscriptions/rooms.js
+++ b/app/lib/methods/subscriptions/rooms.js
@@ -16,6 +16,8 @@ import EventEmitter from '../../../utils/events';
import { removedRoom } from '../../../actions/room';
import { setUser } from '../../../actions/login';
import { INAPP_NOTIFICATION_EMITTER } from '../../../containers/InAppNotification';
+import { Encryption } from '../../encryption';
+import { E2E_MESSAGE_TYPE } from '../../encryption/constants';
const removeListener = listener => listener.stop();
@@ -23,10 +25,8 @@ let connectedListener;
let disconnectedListener;
let streamListener;
let subServer;
-let subQueue = {};
+let queue = {};
let subTimer = null;
-let roomQueue = {};
-let roomTimer = null;
const WINDOW_TIME = 500;
const createOrUpdateSubscription = async(subscription, room) => {
@@ -81,7 +81,10 @@ const createOrUpdateSubscription = async(subscription, room) => {
departmentId: s.departmentId,
servedBy: s.servedBy,
livechatData: s.livechatData,
- tags: s.tags
+ tags: s.tags,
+ encrypted: s.encrypted,
+ e2eKeyId: s.e2eKeyId,
+ E2EKey: s.E2EKey
};
} catch (error) {
try {
@@ -109,6 +112,7 @@ const createOrUpdateSubscription = async(subscription, room) => {
tags: r.tags,
servedBy: r.servedBy,
encrypted: r.encrypted,
+ e2eKeyId: r.e2eKeyId,
broadcast: r.broadcast,
customFields: r.customFields,
departmentId: r.departmentId,
@@ -119,73 +123,91 @@ const createOrUpdateSubscription = async(subscription, room) => {
}
}
- const tmp = merge(subscription, room);
- await db.action(async() => {
- let sub;
+ let tmp = merge(subscription, room);
+ tmp = await Encryption.decryptSubscription(tmp);
+ let sub;
+ try {
+ sub = await subCollection.find(tmp.rid);
+ } catch (error) {
+ // Do nothing
+ }
+
+ // If we're receiving a E2EKey of a room
+ if (sub && !sub.E2EKey && subscription?.E2EKey) {
+ // Assing info from database subscription to tmp
+ // It should be a plain object
+ tmp = Object.assign(tmp, {
+ rid: sub.rid,
+ encrypted: sub.encrypted,
+ lastMessage: sub.lastMessage,
+ E2EKey: subscription.E2EKey,
+ e2eKeyId: sub.e2eKeyId
+ });
+ // Decrypt lastMessage using the received E2EKey
+ tmp = await Encryption.decryptSubscription(tmp);
+ // Decrypt all pending messages of this room in parallel
+ Encryption.decryptPendingMessages(tmp.rid);
+ }
+
+ const batch = [];
+ if (sub) {
try {
- sub = await subCollection.find(tmp.rid);
+ const update = sub.prepareUpdate((s) => {
+ Object.assign(s, tmp);
+ if (subscription.announcement) {
+ if (subscription.announcement !== sub.announcement) {
+ s.bannerClosed = false;
+ }
+ }
+ });
+ batch.push(update);
+ } catch (e) {
+ console.log(e);
+ }
+ } else {
+ try {
+ const create = subCollection.prepareCreate((s) => {
+ s._raw = sanitizedRaw({ id: tmp.rid }, subCollection.schema);
+ Object.assign(s, tmp);
+ if (s.roomUpdatedAt) {
+ s.roomUpdatedAt = new Date();
+ }
+ });
+ batch.push(create);
+ } catch (e) {
+ console.log(e);
+ }
+ }
+
+ const { rooms } = store.getState().room;
+ if (tmp.lastMessage && !rooms.includes(tmp.rid)) {
+ const lastMessage = buildMessage(tmp.lastMessage);
+ const messagesCollection = db.collections.get('messages');
+ let messageRecord;
+ try {
+ messageRecord = await messagesCollection.find(lastMessage._id);
} catch (error) {
// Do nothing
}
- const batch = [];
- if (sub) {
- try {
- const update = sub.prepareUpdate((s) => {
- Object.assign(s, tmp);
- if (subscription.announcement) {
- if (subscription.announcement !== sub.announcement) {
- s.bannerClosed = false;
- }
- }
- });
- batch.push(update);
- } catch (e) {
- console.log(e);
- }
+ if (messageRecord) {
+ batch.push(
+ messageRecord.prepareUpdate(() => {
+ Object.assign(messageRecord, lastMessage);
+ })
+ );
} else {
- try {
- const create = subCollection.prepareCreate((s) => {
- s._raw = sanitizedRaw({ id: tmp.rid }, subCollection.schema);
- Object.assign(s, tmp);
- if (s.roomUpdatedAt) {
- s.roomUpdatedAt = new Date();
- }
- });
- batch.push(create);
- } catch (e) {
- console.log(e);
- }
- }
-
- const { rooms } = store.getState().room;
- if (tmp.lastMessage && !rooms.includes(tmp.rid)) {
- const lastMessage = buildMessage(tmp.lastMessage);
- const messagesCollection = db.collections.get('messages');
- let messageRecord;
- try {
- messageRecord = await messagesCollection.find(lastMessage._id);
- } catch (error) {
- // Do nothing
- }
-
- if (messageRecord) {
- batch.push(
- messageRecord.prepareUpdate(() => {
- Object.assign(messageRecord, lastMessage);
- })
- );
- } else {
- batch.push(
- messagesCollection.prepareCreate((m) => {
- m._raw = sanitizedRaw({ id: lastMessage._id }, messagesCollection.schema);
- m.subscription.id = lastMessage.rid;
- return Object.assign(m, lastMessage);
- })
- );
- }
+ batch.push(
+ messagesCollection.prepareCreate((m) => {
+ m._raw = sanitizedRaw({ id: lastMessage._id }, messagesCollection.schema);
+ m.subscription.id = lastMessage.rid;
+ return Object.assign(m, lastMessage);
+ })
+ );
}
+ }
+ await db.action(async() => {
await db.batch(...batch);
});
} catch (e) {
@@ -193,36 +215,38 @@ const createOrUpdateSubscription = async(subscription, room) => {
}
};
-const debouncedUpdateSub = (subscription) => {
+const getSubQueueId = rid => `SUB-${ rid }`;
+
+const getRoomQueueId = rid => `ROOM-${ rid }`;
+
+const debouncedUpdate = (subscription) => {
if (!subTimer) {
subTimer = setTimeout(() => {
- const subBatch = subQueue;
- subQueue = {};
+ const batch = queue;
+ queue = {};
subTimer = null;
- Object.keys(subBatch).forEach((key) => {
+ Object.keys(batch).forEach((key) => {
InteractionManager.runAfterInteractions(() => {
- createOrUpdateSubscription(subBatch[key]);
+ if (batch[key]) {
+ if (/SUB/.test(key)) {
+ const sub = batch[key];
+ const roomQueueId = getRoomQueueId(sub.rid);
+ const room = batch[roomQueueId];
+ delete batch[roomQueueId];
+ createOrUpdateSubscription(sub, room);
+ } else {
+ const room = batch[key];
+ const subQueueId = getSubQueueId(room._id);
+ const sub = batch[subQueueId];
+ delete batch[subQueueId];
+ createOrUpdateSubscription(sub, room);
+ }
+ }
});
});
}, WINDOW_TIME);
}
- subQueue[subscription.rid] = subscription;
-};
-
-const debouncedUpdateRoom = (room) => {
- if (!roomTimer) {
- roomTimer = setTimeout(() => {
- const roomBatch = roomQueue;
- roomQueue = {};
- roomTimer = null;
- Object.keys(roomBatch).forEach((key) => {
- InteractionManager.runAfterInteractions(() => {
- createOrUpdateSubscription(null, roomBatch[key]);
- });
- });
- }, WINDOW_TIME);
- }
- roomQueue[room._id] = room;
+ queue[subscription.rid ? getSubQueueId(subscription.rid) : getRoomQueueId(subscription._id)] = subscription;
};
export default function subscribeRooms() {
@@ -244,7 +268,9 @@ export default function subscribeRooms() {
const [, ev] = ddpMessage.fields.eventName.split('/');
if (/userData/.test(ev)) {
const [{ diff }] = ddpMessage.fields.args;
- store.dispatch(setUser({ statusLivechat: diff?.statusLivechat }));
+ if (diff?.statusLivechat) {
+ store.dispatch(setUser({ statusLivechat: diff.statusLivechat }));
+ }
}
if (/subscriptions/.test(ev)) {
if (type === 'removed') {
@@ -278,12 +304,12 @@ export default function subscribeRooms() {
log(e);
}
} else {
- debouncedUpdateSub(data);
+ debouncedUpdate(data);
}
}
if (/rooms/.test(ev)) {
if (type === 'updated' || type === 'inserted') {
- debouncedUpdateRoom(data);
+ debouncedUpdate(data);
}
}
if (/message/.test(ev)) {
@@ -318,12 +344,25 @@ export default function subscribeRooms() {
if (/notification/.test(ev)) {
const [notification] = ddpMessage.fields.args;
try {
- const { payload: { rid } } = notification;
+ const { payload: { rid, message, sender } } = notification;
const room = await RocketChat.getRoom(rid);
notification.title = RocketChat.getRoomTitle(room);
notification.avatar = RocketChat.getRoomAvatar(room);
+
+ // If it's from a encrypted room
+ if (message.t === E2E_MESSAGE_TYPE) {
+ // Decrypt this message content
+ const { msg } = await Encryption.decryptMessage({ ...message, rid });
+ // If it's a direct the content is the message decrypted
+ if (room.t === 'd') {
+ notification.text = msg;
+ // If it's a private group we should add the sender name
+ } else {
+ notification.text = `${ RocketChat.getSenderName(sender) }: ${ msg }`;
+ }
+ }
} catch (e) {
- // do nothing
+ log(e);
}
EventEmitter.emit(INAPP_NOTIFICATION_EMITTER, notification);
}
@@ -331,6 +370,14 @@ export default function subscribeRooms() {
const { type: eventType, ...args } = type;
handlePayloadUserInteraction(eventType, args);
}
+ if (/e2ekeyRequest/.test(ev)) {
+ const [roomId, keyId] = ddpMessage.fields.args;
+ try {
+ await Encryption.provideRoomKeyToUser(keyId, roomId);
+ } catch (e) {
+ log(e);
+ }
+ }
});
const stop = () => {
@@ -346,16 +393,11 @@ export default function subscribeRooms() {
streamListener.then(removeListener);
streamListener = false;
}
- subQueue = {};
- roomQueue = {};
+ queue = {};
if (subTimer) {
clearTimeout(subTimer);
subTimer = false;
}
- if (roomTimer) {
- clearTimeout(roomTimer);
- roomTimer = false;
- }
};
connectedListener = this.sdk.onStreamData('connected', handleConnection);
diff --git a/app/lib/methods/updateMessages.js b/app/lib/methods/updateMessages.js
index 350ece501..0dc63e9ba 100644
--- a/app/lib/methods/updateMessages.js
+++ b/app/lib/methods/updateMessages.js
@@ -5,6 +5,7 @@ import buildMessage from './helpers/buildMessage';
import log from '../../utils/log';
import database from '../database';
import protectedFunction from './helpers/protectedFunction';
+import { Encryption } from '../encryption';
export default function updateMessages({ rid, update = [], remove = [] }) {
try {
@@ -13,6 +14,8 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
}
const db = database.active;
return db.action(async() => {
+ // Decrypt these messages
+ update = await Encryption.decryptMessages(update);
const subCollection = db.collections.get('subscriptions');
let sub;
try {
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 52c09f93f..c9341e0e9 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -1,18 +1,21 @@
import { InteractionManager } from 'react-native';
import semver from 'semver';
-import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
-import RNUserDefaults from 'rn-user-defaults';
+import {
+ Rocketchat as RocketchatClient,
+ settings as RocketChatSettings
+} from '@rocket.chat/sdk';
import { Q } from '@nozbe/watermelondb';
import AsyncStorage from '@react-native-community/async-storage';
+import RNFetchBlob from 'rn-fetch-blob';
import reduxStore from './createStore';
import defaultSettings from '../constants/settings';
-import messagesStatus from '../constants/messagesStatus';
import database from './database';
import log from '../utils/log';
import { isIOS, getBundleId } from '../utils/deviceInfo';
import fetch from '../utils/fetch';
+import { encryptionInit } from '../actions/encryption';
import { setUser, setLoginServices, loginRequest } from '../actions/login';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import {
@@ -20,7 +23,6 @@ import {
} from '../actions/share';
import subscribeRooms from './methods/subscriptions/rooms';
-import subscribeInquiry from './methods/subscriptions/inquiry';
import getUsersPresence, { getUserPresence, subscribeUsersPresence } from './methods/getUsersPresence';
import protectedFunction from './methods/helpers/protectedFunction';
@@ -30,6 +32,9 @@ import getSettings, { getLoginSettings, setSettings } from './methods/getSetting
import getRooms from './methods/getRooms';
import getPermissions from './methods/getPermissions';
import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis';
+import {
+ getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable
+} from './methods/enterpriseModules';
import getSlashCommands from './methods/getSlashCommands';
import getRoles from './methods/getRoles';
import canOpenRoom from './methods/canOpenRoom';
@@ -39,7 +44,7 @@ import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import loadThreadMessages from './methods/loadThreadMessages';
-import sendMessage, { sendMessageCall } from './methods/sendMessage';
+import sendMessage, { resendMessage } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import callJitsi from './methods/callJitsi';
@@ -51,11 +56,17 @@ import I18n from '../i18n';
import { twoFactor } from '../utils/twoFactor';
import { selectServerFailure } from '../actions/server';
import { useSsl } from '../utils/url';
+import UserPreferences from './userPreferences';
+import { Encryption } from './encryption';
+import EventEmitter from '../utils/events';
+import { sanitizeLikeString } from './database/utils';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
+const CURRENT_SERVER = 'currentServer';
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
+export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
const returnAnArray = obj => obj || [];
const MIN_ROCKETCHAT_VERSION = '0.70.0';
@@ -63,6 +74,7 @@ const STATUSES = ['offline', 'online', 'away', 'busy'];
const RocketChat = {
TOKEN_KEY,
+ CURRENT_SERVER,
callJitsi,
async subscribeRooms() {
if (!this.roomsSub) {
@@ -73,28 +85,12 @@ const RocketChat = {
}
}
},
- async subscribeInquiry() {
- if (!this.inquirySub) {
- try {
- this.inquirySub = await subscribeInquiry.call(this);
- } catch (e) {
- log(e);
- }
- }
- },
canOpenRoom,
createChannel({
- name, users, type, readOnly, broadcast
+ name, users, type, readOnly, broadcast, encrypted
}) {
// RC 0.51.0
- return this.methodCallWrapper(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast });
- },
- async getUserToken() {
- try {
- return await RNUserDefaults.get(TOKEN_KEY);
- } catch (error) {
- console.warn(`RNUserDefaults error: ${ error.message }`);
- }
+ return this.methodCallWrapper(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast, encrypted });
},
async getWebsocketInfo({ server }) {
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
@@ -105,10 +101,7 @@ const RocketChat = {
if (err.message && err.message.includes('400')) {
return {
success: false,
- message: 'Websocket_disabled',
- messageOptions: {
- contact: I18n.t('Contact_your_server_admin')
- }
+ message: I18n.t('Websocket_disabled', { contact: I18n.t('Contact_your_server_admin') })
};
}
}
@@ -120,52 +113,46 @@ const RocketChat = {
};
},
async getServerInfo(server) {
- const notRCServer = {
- success: false,
- message: 'Not_RC_Server',
- messageOptions: {
- contact: I18n.t('Contact_your_server_admin')
- }
- };
try {
- const result = await fetch(`${ server }/api/info`).then(async(response) => {
- let res = notRCServer;
- try {
- res = await response.json();
- if (!(res && res.success)) {
- return notRCServer;
- }
- } catch (e) {
- // do nothing
- }
- return res;
- });
- if (result.success) {
- if (semver.lt(result.version, MIN_ROCKETCHAT_VERSION)) {
+ const response = await RNFetchBlob.fetch('GET', `${ server }/api/info`, { ...RocketChatSettings.customHeaders });
+ try {
+ // Try to resolve as json
+ const jsonRes = response.json();
+ if (!(jsonRes?.success)) {
return {
success: false,
- message: 'Invalid_server_version',
- messageOptions: {
- currentVersion: result.version,
- minVersion: MIN_ROCKETCHAT_VERSION
- }
+ message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') })
};
}
+ if (semver.lt(jsonRes.version, MIN_ROCKETCHAT_VERSION)) {
+ return {
+ success: false,
+ message: I18n.t('Invalid_server_version', {
+ currentVersion: jsonRes.version,
+ minVersion: MIN_ROCKETCHAT_VERSION
+ })
+ };
+ }
+ return jsonRes;
+ } catch (error) {
+ // Request is successful, but response isn't a json
}
- return result;
} catch (e) {
- if (e.message === 'Aborted') {
- reduxStore.dispatch(selectServerFailure());
- throw e;
+ if (e?.message) {
+ if (e.message === 'Aborted') {
+ reduxStore.dispatch(selectServerFailure());
+ throw e;
+ }
+ return {
+ success: false,
+ message: e.message
+ };
}
- log(e);
}
+
return {
success: false,
- message: 'The_URL_is_invalid',
- messageOptions: {
- contact: I18n.t('Contact_your_server_admin')
- }
+ message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') })
};
},
stopListener(listener) {
@@ -213,10 +200,7 @@ const RocketChat = {
this.roomsSub = null;
}
- if (this.inquirySub) {
- this.inquirySub.stop();
- this.inquirySub = null;
- }
+ EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
if (this.sdk) {
this.sdk.disconnect();
@@ -307,7 +291,7 @@ const RocketChat = {
// set User info
try {
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users');
let user = null;
if (userId) {
@@ -321,6 +305,7 @@ const RocketChat = {
}
reduxStore.dispatch(shareSetUser(user));
await RocketChat.login({ resume: user.token });
+ reduxStore.dispatch(encryptionInit());
} catch (e) {
log(e);
}
@@ -335,6 +320,45 @@ const RocketChat = {
reduxStore.dispatch(shareSetUser({}));
},
+ async e2eFetchMyKeys() {
+ // RC 0.70.0
+ const sdk = this.shareSDK || this.sdk;
+ const result = await sdk.get('e2e.fetchMyKeys');
+ // snake_case -> camelCase
+ if (result.success) {
+ return {
+ success: result.success,
+ publicKey: result.public_key,
+ privateKey: result.private_key
+ };
+ }
+ return result;
+ },
+ e2eSetUserPublicAndPrivateKeys(public_key, private_key) {
+ // RC 2.2.0
+ return this.post('e2e.setUserPublicAndPrivateKeys', { public_key, private_key });
+ },
+ e2eRequestSubscriptionKeys() {
+ // RC 0.72.0
+ return this.methodCallWrapper('e2e.requestSubscriptionKeys');
+ },
+ e2eGetUsersOfRoomWithoutKey(rid) {
+ // RC 0.70.0
+ return this.sdk.get('e2e.getUsersOfRoomWithoutKey', { rid });
+ },
+ e2eSetRoomKeyID(rid, keyID) {
+ // RC 0.70.0
+ return this.post('e2e.setRoomKeyID', { rid, keyID });
+ },
+ e2eUpdateGroupKey(uid, rid, key) {
+ // RC 0.70.0
+ return this.post('e2e.updateGroupKey', { uid, rid, key });
+ },
+ e2eRequestRoomKey(rid, e2eKeyId) {
+ // RC 0.70.0
+ return this.methodCallWrapper('stream-notify-room-users', `${ rid }/e2ekeyRequest`, rid, e2eKeyId);
+ },
+
updateJitsiTimeout(roomId) {
// RC 0.74.0
return this.post('video-conference/jitsi.update-timeout', { roomId });
@@ -350,10 +374,10 @@ const RocketChat = {
return this.post('users.forgotPassword', { email }, false);
},
- loginTOTP(params) {
+ loginTOTP(params, loginEmailPassword) {
return new Promise(async(resolve, reject) => {
try {
- const result = await this.login(params);
+ const result = await this.login(params, loginEmailPassword);
return resolve(result);
} catch (e) {
if (e.data?.error && (e.data.error === 'totp-required' || e.data.error === 'totp-invalid')) {
@@ -361,7 +385,7 @@ const RocketChat = {
try {
reduxStore.dispatch(setUser({ username: params.user || params.username }));
const code = await twoFactor({ method: details?.method || 'totp', invalid: e.data.error === 'totp-invalid' });
- return resolve(this.loginTOTP({ ...params, code: code?.twoFactorCode }));
+ return resolve(this.loginTOTP({ ...params, code: code?.twoFactorCode }, loginEmailPassword));
} catch {
// twoFactor was canceled
return reject();
@@ -392,7 +416,7 @@ const RocketChat = {
};
}
- return this.loginTOTP(params);
+ return this.loginTOTP(params, true);
},
async loginOAuthOrSso(params) {
@@ -400,7 +424,7 @@ const RocketChat = {
reduxStore.dispatch(loginRequest({ resume: result.token }));
},
- async login(params) {
+ async login(params, loginEmailPassword) {
const sdk = this.shareSDK || this.sdk;
// RC 0.64.0
await sdk.login(params);
@@ -416,11 +440,16 @@ const RocketChat = {
customFields: result.me.customFields,
statusLivechat: result.me.statusLivechat,
emails: result.me.emails,
- roles: result.me.roles
+ roles: result.me.roles,
+ loginEmailPassword
};
return user;
},
logout,
+ logoutOtherLocations() {
+ const { id: userId } = reduxStore.getState().login.user;
+ return this.sdk.post('users.removeOtherTokens', { userId });
+ },
removeServer,
async clearCache({ server }) {
try {
@@ -477,30 +506,7 @@ const RocketChat = {
sendMessage,
getRooms,
readMessages,
- async resendMessage(message, tmid) {
- const db = database.active;
- try {
- await db.action(async() => {
- await message.update((m) => {
- m.status = messagesStatus.TEMP;
- });
- });
- let m = {
- id: message.id,
- msg: message.msg,
- subscription: { id: message.subscription.id }
- };
- if (tmid) {
- m = {
- ...m,
- tmid
- };
- }
- await sendMessageCall.call(this, m);
- } catch (e) {
- log(e);
- }
- },
+ resendMessage,
async search({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();
@@ -515,8 +521,12 @@ const RocketChat = {
}
const db = database.active;
+ const likeString = sanitizeLikeString(searchText);
let data = await db.collections.get('subscriptions').query(
- Q.where('name', Q.like(`%${ Q.sanitizeLikeString(searchText) }%`))
+ Q.or(
+ Q.where('name', Q.like(`%${ likeString }%`)),
+ Q.where('fname', Q.like(`%${ likeString }%`))
+ )
).fetch();
if (filterUsers && !filterRooms) {
@@ -524,6 +534,7 @@ const RocketChat = {
} else if (!filterUsers && filterRooms) {
data = data.filter(item => item.t !== 'd' || RocketChat.isGroupChat(item));
}
+
data = data.slice(0, 7);
data = data.map((sub) => {
@@ -625,6 +636,10 @@ const RocketChat = {
getPermissions,
getCustomEmojis,
setCustomEmojis,
+ getEnterpriseModules,
+ setEnterpriseModules,
+ hasLicense,
+ isOmnichannelModuleAvailable,
getSlashCommands,
getRoles,
parseSettings: settings => settings.reduce((ret, item) => {
@@ -645,10 +660,10 @@ const RocketChat = {
// RC 0.48.0
return this.post('chat.delete', { msgId: messageId, roomId: rid });
},
- editMessage(message) {
- const { id, msg, rid } = message;
+ async editMessage(message) {
+ const { rid, msg } = await Encryption.encryptMessage(message);
// RC 0.49.0
- return this.post('chat.update', { roomId: rid, msgId: id, text: msg });
+ return this.post('chat.update', { roomId: rid, msgId: message.id, text: msg });
},
markAsUnread({ messageId }) {
return this.post('subscriptions.unread', { firstUnreadMessage: { _id: messageId } });
@@ -731,6 +746,10 @@ const RocketChat = {
setUserPresenceOnline() {
return this.methodCall('UserPresence:online');
},
+ setUserPreferences(userId, data) {
+ // RC 0.62.0
+ return this.sdk.post('users.setPreferences', { userId, data });
+ },
setUserStatus(status, message) {
// RC 1.2.0
return this.post('users.setStatus', { status, message });
@@ -754,12 +773,17 @@ const RocketChat = {
return this.methodCallWrapper('getUsersOfRoom', rid, allUsers, { skip, limit });
},
- async methodCallWrapper(method, ...params) {
+ methodCallWrapper(method, ...params) {
const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings;
if (API_Use_REST_For_DDP_Calls) {
- const data = await this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) });
- const { result } = JSON.parse(data.message);
- return result;
+ return new Promise(async(resolve, reject) => {
+ const data = await this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) });
+ const response = JSON.parse(data.message);
+ if (response?.error) {
+ return reject(response.error);
+ }
+ return resolve(response.result);
+ });
}
return this.methodCall(method, ...params);
},
@@ -780,6 +804,10 @@ const RocketChat = {
// RC 0.48.0
return this.sdk.get('users.info', { userId });
},
+ getUserPreferences(userId) {
+ // RC 0.62.0
+ return this.sdk.get('users.getPreferences', { userId });
+ },
getRoomInfo(roomId) {
// RC 0.72.0
return this.sdk.get('rooms.info', { roomId });
@@ -837,20 +865,6 @@ const RocketChat = {
// RC 2.2.0
return this.sdk.get('livechat/custom-fields');
},
- changeLivechatStatus() {
- // RC 0.26.0
- return this.methodCallWrapper('livechat:changeLivechatStatus');
- },
- getInquiriesQueued() {
- // RC 2.4.0
- return this.sdk.get('livechat/inquiries.queued');
- },
- takeInquiry(inquiryId) {
- // this inquiry is added to the db by the subscriptions stream
- // and will be removed by the queue stream
- // RC 2.4.0
- return this.methodCallWrapper('livechat:takeInquiry', inquiryId);
- },
getUidDirectMessage(room) {
const { id: userId } = reduxStore.getState().login.user;
@@ -1056,18 +1070,21 @@ const RocketChat = {
}
return JSON.parse(allowCrashReport);
},
+ async getAllowAnalyticsEvents() {
+ const allowAnalyticsEvents = await AsyncStorage.getItem(ANALYTICS_EVENTS_KEY);
+ if (allowAnalyticsEvents === null) {
+ return true;
+ }
+ return JSON.parse(allowAnalyticsEvents);
+ },
async getSortPreferences() {
- const prefs = await RNUserDefaults.objectForKey(SORT_PREFS_KEY);
+ const prefs = await UserPreferences.getMapAsync(SORT_PREFS_KEY);
return prefs;
},
async saveSortPreference(param) {
- try {
- let prefs = await RocketChat.getSortPreferences();
- prefs = { ...prefs, ...param };
- return await RNUserDefaults.setObjectForKey(SORT_PREFS_KEY, prefs);
- } catch (error) {
- console.warn(error);
- }
+ let prefs = await RocketChat.getSortPreferences();
+ prefs = { ...prefs, ...param };
+ return UserPreferences.setMapAsync(SORT_PREFS_KEY, prefs);
},
async getLoginServices(server) {
try {
@@ -1279,6 +1296,10 @@ const RocketChat = {
translateMessage(message, targetLanguage) {
return this.methodCallWrapper('autoTranslate.translateMessage', message, targetLanguage);
},
+ getSenderName(sender) {
+ const { UI_Use_Real_Name: useRealName } = reduxStore.getState().settings;
+ return useRealName ? sender.name : sender.username;
+ },
getRoomTitle(room) {
const { UI_Use_Real_Name: useRealName, UI_Allow_room_names_with_special_chars: allowSpecialChars } = reduxStore.getState().settings;
const { username } = reduxStore.getState().login.user;
diff --git a/app/lib/selection.json b/app/lib/selection.json
index 7a375d65a..ad1bf20b7 100644
--- a/app/lib/selection.json
+++ b/app/lib/selection.json
@@ -1 +1 @@
-{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M794.598 131.828c-24.994-24.994-65.515-24.994-90.509 0l-529.769 529.769c-9.564 9.562-15.852 21.909-17.962 35.273l-19.585 124.019c-1.384 8.764-0.941 17.28 1.005 25.229 7.587 30.997 38.032 53.367 72.195 47.974l124.022-19.584c13.36-2.112 25.708-8.401 35.272-17.963l529.771-529.769c12.497-12.497 18.743-28.877 18.743-45.257 0-10.365-2.505-20.73-7.509-30.111-2.901-5.443-6.647-10.556-11.238-15.144l-104.435-104.437zM629.845 296.585l119.497-119.5 104.439 104.436-119.501 119.5-104.435-104.436zM199.991 830.874l19.585-124.019 365.013-365.015 104.435 104.436-365.012 365.013-124.021 19.584z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":544,"id":172,"name":"edit","prevSize":32,"code":59835},"setIdx":0,"setId":3,"iconIdx":0},{"icon":{"paths":["M410.708 527.33c26.575 0 48.121-21.585 48.121-48.213s-21.547-48.213-48.121-48.213c-26.576 0-48.12 21.585-48.12 48.213s21.544 48.213 48.12 48.213z","M603.187 479.117c0 26.628-21.542 48.213-48.119 48.213s-48.119-21.585-48.119-48.213c0-26.628 21.542-48.213 48.119-48.213s48.119 21.585 48.119 48.213z","M747.55 479.117c0 26.628-21.542 48.213-48.119 48.213s-48.119-21.585-48.119-48.213c0-26.628 21.542-48.213 48.119-48.213s48.119 21.585 48.119 48.213z","M101.734 813.683l119.625 31.279c85.312 22.31 173.449-16.252 238.694-79.71 29.5 4.796 59.874 7.245 90.675 7.245 218.295 0 404.339-122.863 404.339-294.519 0-171.663-186.039-294.519-404.339-294.519-218.313 0-404.35 122.852-404.339 294.524 0 65.071 27.642 125.013 75.655 173.585-2.847 24.836-14.596 44.732-36.049 68.553l-84.261 93.564zM550.729 258.994c183.13 0 331.601 98.043 331.601 218.984 0 120.93-148.471 218.982-331.601 218.982-40.781 0-79.834-4.877-115.913-13.76-36.667 45.598-117.332 109.005-195.694 88.516 25.489-28.305 63.251-76.13 55.168-154.91-46.968-37.781-75.162-86.135-75.162-138.829-0.008-120.949 148.46-218.984 331.599-218.984z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["baloon-ellipsis"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":372,"id":171,"name":"baloon-ellipsis","prevSize":32,"code":59648},"setIdx":0,"setId":3,"iconIdx":1},{"icon":{"paths":["M912.401 572.574h-269.013l-250.606-423.241h269.063l250.556 423.241zM375.23 190.906l134.483 238.078-230.78 408.354-134.532-237.922 230.829-408.51zM455.343 615.979h455.706l-134.532 258.688h-465.642l144.468-258.688z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-google-drive"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":373,"id":170,"name":"file-google-drive","prevSize":32,"code":59649},"setIdx":0,"setId":3,"iconIdx":2},{"icon":{"paths":["M892.437 567.625c-41.856-41.139-161.237-29.828-220.928-22.285-59.004-35.998-98.458-85.705-126.242-158.727 13.376-55.195 34.645-139.187 18.526-191.982-14.409-89.82-129.677-80.907-146.143-20.227-15.094 55.194-1.372 131.987 24.014 230.035-34.305 81.935-85.42 191.981-121.44 255.059-68.611 35.311-161.235 89.822-174.957 158.387-11.321 54.165 89.194 189.239 261.063-106.961 76.845-25.37 160.548-56.567 234.647-68.907 64.836 34.965 140.651 58.278 191.424 58.278 87.479 0 96.055-96.674 60.036-132.672zM212.848 834.342c17.496-46.967 84.048-101.133 104.288-119.991-65.18 103.876-104.288 122.389-104.288 119.991zM492.779 180.918c25.387 0 22.985 110.047 6.174 139.872-15.091-47.653-14.75-139.872-6.174-139.872zM409.074 649.216c33.277-57.937 61.748-126.844 84.733-187.524 28.476 51.767 64.836 93.248 103.262 121.702-71.356 14.741-133.449 44.911-187.995 65.822zM860.531 632.077c0 0-17.152 20.57-127.957-26.743 120.41-8.913 140.309 18.513 127.957 26.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-pdf"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":374,"id":169,"name":"file-pdf","prevSize":32,"code":59650},"setIdx":0,"setId":3,"iconIdx":3},{"icon":{"paths":["M862.455 324.045c-35.2-60.31-82.944-108.058-143.249-143.253-60.314-35.197-126.161-52.792-197.581-52.792-71.415 0-137.28 17.6-197.58 52.792-60.31 35.194-108.054 82.943-143.253 143.253-35.194 60.308-52.792 126.165-52.792 197.568 0 85.773 25.024 162.901 75.086 231.407 50.056 68.51 114.721 115.917 193.99 142.225 9.227 1.711 16.058 0.508 20.499-3.584 4.443-4.096 6.662-9.225 6.662-15.369 0-1.024-0.088-10.249-0.259-27.678-0.176-17.429-0.259-32.631-0.259-45.606l-11.789 2.039c-7.516 1.378-16.998 1.963-28.446 1.796-11.442-0.162-23.321-1.361-35.619-3.588-12.304-2.21-23.748-7.334-34.342-15.364-10.588-8.030-18.104-18.543-22.547-31.518l-5.125-11.793c-3.416-7.851-8.794-16.576-16.142-26.138-7.347-9.57-14.778-16.055-22.294-19.473l-3.589-2.569c-2.391-1.707-4.61-3.767-6.662-6.157-2.050-2.389-3.585-4.779-4.61-7.172-1.027-2.398-0.176-4.365 2.562-5.905 2.737-1.545 7.685-2.291 14.864-2.291l10.247 1.532c6.834 1.37 15.287 5.461 25.371 12.297 10.078 6.835 18.362 15.718 24.856 26.645 7.863 14.012 17.335 24.691 28.446 32.038 11.101 7.347 22.294 11.017 33.568 11.017s21.010-0.858 29.214-2.556c8.195-1.711 15.884-4.279 23.062-7.693 3.075-22.903 11.446-40.495 25.112-52.796-19.473-2.048-36.983-5.129-52.535-9.229-15.542-4.1-31.604-10.761-48.173-19.998-16.578-9.22-30.331-20.672-41.262-34.334-10.932-13.666-19.904-31.612-26.904-53.815-7.003-22.212-10.505-47.838-10.505-76.881 0-41.348 13.5-76.538 40.493-105.584-12.645-31.088-11.451-65.94 3.585-104.55 9.909-3.079 24.604-0.768 44.078 6.917 19.477 7.689 33.738 14.275 42.796 19.736 9.058 5.459 16.316 10.085 21.784 13.837 31.782-8.881 64.58-13.322 98.406-13.322s66.633 4.441 98.415 13.322l19.477-12.295c13.316-8.204 29.043-15.722 47.142-22.556 18.112-6.831 31.957-8.712 41.532-5.633 15.369 38.612 16.738 73.461 4.092 104.55 26.991 29.045 40.495 64.243 40.495 105.587 0 29.039-3.516 54.746-10.509 77.129-6.997 22.387-16.047 40.316-27.149 53.82-11.115 13.5-24.956 24.862-41.523 34.082-16.576 9.225-32.64 15.885-48.183 19.989-15.548 4.105-33.058 7.189-52.531 9.237 17.762 15.373 26.641 39.633 26.641 72.772v108.139c0 6.14 2.138 11.268 6.413 15.369 4.271 4.092 11.017 5.295 20.245 3.58 79.279-26.304 143.945-73.711 193.997-142.221 50.048-68.506 75.081-145.634 75.081-231.407-0.017-71.394-17.621-137.247-52.8-197.555z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Github"]},"attrs":[{}],"properties":{"order":375,"id":168,"name":"Github","prevSize":32,"code":59651},"setIdx":0,"setId":3,"iconIdx":4},{"icon":{"paths":["M510.144 910.66l156.407-481.412h-312.813l156.406 481.412z","M510.144 910.66l-156.405-481.412h-219.199l375.604 481.412z","M134.541 429.248l-47.612 146.287c-4.37 13.342 0.46 28.062 11.73 36.113l411.484 299.012-375.603-481.412z","M134.54 429.252h219.199l-94.304-290.039c-4.83-14.95-25.991-14.95-30.821 0l-94.074 290.039z","M510.144 910.66l156.407-481.412h219.196l-375.603 481.412z","M885.747 429.248l47.612 146.287c4.373 13.342-0.457 28.062-11.729 36.113l-411.486 299.012 375.603-481.412z","M885.747 429.252h-219.196l94.302-290.039c4.83-14.95 25.993-14.95 30.822 0l94.071 290.039z"],"attrs":[{"fill":"rgb(226, 67, 41)"},{"fill":"rgb(252, 109, 38)"},{"fill":"rgb(252, 163, 38)"},{"fill":"rgb(226, 67, 41)"},{"fill":"rgb(252, 109, 38)"},{"fill":"rgb(252, 163, 38)"},{"fill":"rgb(226, 67, 41)"}],"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["Gitlab"]},"attrs":[{"fill":"rgb(226, 67, 41)"},{"fill":"rgb(252, 109, 38)"},{"fill":"rgb(252, 163, 38)"},{"fill":"rgb(226, 67, 41)"},{"fill":"rgb(252, 109, 38)"},{"fill":"rgb(252, 163, 38)"},{"fill":"rgb(226, 67, 41)"}],"properties":{"order":376,"id":167,"name":"Gitlab","prevSize":32,"code":59652,"codes":[59652,59653,59654,59655,59656,59657,59658]},"setIdx":0,"setId":3,"iconIdx":5},{"icon":{"paths":["M881.941 442.176h-361.655v148.54h206.66c-8.9 48-35.955 88.67-76.625 115.9v96.346h124.1c72.614-66.85 114.505-165.295 114.505-282.24 0-27.226-2.445-53.41-6.985-78.545z","M520.286 896c103.68 0 190.605-34.385 254.135-93.035l-124.1-96.35c-34.385 23.040-78.37 36.655-130.035 36.655-100.015 0-184.67-67.55-214.866-158.31h-128.291v99.49c63.185 125.495 193.047 211.55 343.157 211.55z","M305.42 584.964c-7.68-23.040-12.044-47.65-12.044-72.96s4.364-49.92 12.044-72.96v-99.492h-128.291c-26.007 51.84-40.844 110.487-40.844 172.452 0 61.961 14.836 120.61 40.844 172.45l128.291-99.49z","M520.286 280.727c56.375 0 106.995 19.375 146.79 57.425l110.14-110.138c-66.5-61.964-153.425-100.015-256.93-100.015-150.11 0-279.971 86.051-343.157 211.549l128.291 99.491c30.196-90.764 114.851-158.313 214.866-158.313z"],"attrs":[{"fill":"rgb(66, 133, 244)"},{"fill":"rgb(52, 168, 83)"},{"fill":"rgb(251, 188, 5)"},{"fill":"rgb(234, 67, 53)"}],"width":1067,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["Google"]},"attrs":[{"fill":"rgb(66, 133, 244)"},{"fill":"rgb(52, 168, 83)"},{"fill":"rgb(251, 188, 5)"},{"fill":"rgb(234, 67, 53)"}],"properties":{"order":377,"id":166,"name":"Google","prevSize":32,"code":59659,"codes":[59659,59660,59661,59662]},"setIdx":0,"setId":3,"iconIdx":6},{"icon":{"paths":["M839.189 128h-654.548c-31.254 0-56.642 24.79-56.642 55.383v657.214c0 30.592 25.388 55.424 56.642 55.424h654.548c31.317 0 56.811-24.832 56.811-55.424v-657.214c0-30.593-25.493-55.383-56.811-55.383z","M298.842 233.729c36.438 0 66.007 29.59 66.007 66.050 0 36.481-29.569 66.071-66.007 66.071-36.588 0-66.092-29.59-66.092-66.071 0-36.46 29.505-66.050 66.092-66.050zM241.795 782.438h114.030v-366.515h-114.030v366.515z","M427.273 415.921h109.188v50.114h1.579c15.189-28.821 52.352-59.202 107.755-59.202 115.354 0 136.666 75.884 136.666 174.598v201.007h-113.903v-178.244c0-42.496-0.725-97.178-59.179-97.178-59.285 0-68.331 46.319-68.331 94.148v181.274h-113.775v-366.517z"],"attrs":[{"fill":"rgb(0, 113, 161)"},{"fill":"rgb(255, 255, 254)"},{"fill":"rgb(255, 255, 254)"}],"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["Linkedin"]},"attrs":[{"fill":"rgb(0, 113, 161)"},{"fill":"rgb(255, 255, 254)"},{"fill":"rgb(255, 255, 254)"}],"properties":{"order":378,"id":165,"name":"Linkedin","prevSize":32,"code":59663,"codes":[59663,59664,59665]},"setIdx":0,"setId":3,"iconIdx":7},{"icon":{"paths":["M85.333 85.333l730.795 774.089c0 0 24.896 17.557 43.934-2.927 19.038-20.489 4.395-40.973 4.395-40.973l-779.123-730.189zM316.727 158.5l556.515 599.956c0 0 24.896 17.557 43.938-2.927 19.038-20.489 4.39-40.973 4.39-40.973l-604.844-556.056zM710.682 915.025l-556.516-599.952 604.845 556.057c0 0 14.643 20.484-4.395 40.969-19.038 20.489-43.934 2.927-43.934 2.927zM512.337 221.417l388.804 419.151c0 0 17.395 12.271 30.694-2.044 13.303-14.31 3.072-28.625 3.072-28.625l-422.571-388.482zM596.523 915.674l-388.803-419.153 422.569 388.484c0 0 10.231 14.31-3.068 28.625-13.303 14.31-30.699 2.044-30.699 2.044zM712.145 312.141l176.222 190.549c0 0 8.602 5.751 15.181-0.956s1.519-13.414 1.519-13.414l-192.922-176.179zM481.229 880.226l-176.218-190.549 192.922 176.179c0 0 5.060 6.707-1.519 13.414s-15.185 0.956-15.185 0.956z"],"attrs":[{"fill":"rgb(222, 79, 79)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Meteor"]},"attrs":[{"fill":"rgb(222, 79, 79)"}],"properties":{"order":379,"id":164,"name":"Meteor","prevSize":32,"code":59666},"setIdx":0,"setId":3,"iconIdx":8},{"icon":{"paths":["M385.058 837.38c290.193 0 448.922-240.111 448.922-448.329 0-6.82 0-13.609-0.465-20.367 30.878-22.306 57.536-49.924 78.716-81.562-28.791 12.742-59.341 21.099-90.62 24.791 32.939-19.692 57.587-50.665 69.367-87.153-30.972 18.354-64.853 31.289-100.19 38.246-48.905-51.936-126.618-64.647-189.559-31.006s-95.458 105.266-79.317 174.714c-126.86-6.351-245.054-66.192-325.169-164.628-41.876 71.997-20.487 164.102 48.848 210.338-25.108-0.742-49.67-7.505-71.61-19.721 0 0.644 0 1.323 0 1.997 0.020 75.004 52.962 139.61 126.58 154.462-23.228 6.323-47.599 7.249-71.241 2.701 20.669 64.188 79.903 108.16 147.404 109.427-55.869 43.849-124.886 67.652-195.945 67.584-12.553-0.026-25.094-0.785-37.559-2.274 72.153 46.242 156.107 70.771 241.839 70.656z"],"attrs":[{"fill":"rgb(29, 161, 242)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Twitter"]},"attrs":[{"fill":"rgb(29, 161, 242)"}],"properties":{"order":380,"id":163,"name":"Twitter","prevSize":32,"code":59667},"setIdx":0,"setId":3,"iconIdx":9},{"icon":{"paths":["M317.896 231.569c-62.882 49.504-135.086 136.548-141.94 266.125-5.762 108.924 37.404 187.187 89.916 241.51 53.259 55.1 115.701 84.924 144.248 94.443 41.861 13.952 114.271 29.534 234.646-15.607 34.859-13.069 73.161-42.095 108.301-78.426 14.272-14.754 27.614-30.293 39.599-45.773-28.774 9.293-61.696 17.758-96.589 23.595-56.188 9.404-119.287 12.322-179.409-0.235-2.492-0.521-4.958-1.033-7.411-1.536-22.191-4.578-42.731-8.815-62.583-17.651-23.095-10.274-43.808-25.89-69.3-51.383-42.097-42.095-89.143-107.221-89.372-213.841-1.569-34.084 4.621-81.88 13.763-129.219 4.624-23.946 10.142-48.465 16.132-72.002zM344.209 138.22c32.8-17.15 63.709 15.843 53.877 45.907-12.235 37.414-24.586 85.511-33.483 131.58-9.024 46.731-13.946 88.633-12.643 114.698l0.040 0.798v0.802c0 84.915 36.172 134.916 70.627 169.374 22.505 22.507 36.833 32.277 50.064 38.165 13.227 5.884 26.829 8.717 51.085 13.764 1.924 0.397 3.913 0.815 5.978 1.246 49.911 10.423 104.533 8.337 155.759-0.239 65.434-10.953 122.385-31.979 152.375-47.386 17.562-9.020 34.991-2.466 44.476 5.965 9.664 8.589 19.375 27.153 8.887 46.955-20.638 38.95-53.645 84.42-92.181 124.262-38.217 39.509-84.318 76.036-131.836 93.854-135.625 50.863-223.215 34.441-277.353 16.397-36.516-12.173-108.392-46.912-170.026-110.677-62.381-64.533-114.683-159.428-107.809-289.37 10.79-203.98 157.583-317.1 232.164-356.095z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["moon"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":381,"id":162,"name":"moon","prevSize":32,"code":59668},"setIdx":0,"setId":3,"iconIdx":10},{"icon":{"paths":["M480 117.333c0-17.673 14.327-32 32-32s32 14.327 32 32v106.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-106.667zM770.039 214.629c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-75.413 75.412c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.758 0-45.255l75.413-75.412zM335.377 694.63c-12.497-12.497-32.758-12.497-45.255 0l-75.495 75.494c-12.497 12.497-12.497 32.755 0 45.252s32.758 12.497 45.255 0l75.495-75.494c12.497-12.497 12.497-32.755 0-45.252zM480 800c0-17.673 14.327-32 32-32s32 14.327 32 32v106.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-106.667zM212 211.999c-12.497 12.497-12.497 32.758 0 45.255l75.349 75.349c12.497 12.497 32.758 12.497 45.255 0s12.497-32.758 0-45.255l-75.349-75.349c-12.497-12.497-32.758-12.497-45.255 0zM694.626 739.883c-12.497-12.497-12.497-32.759 0-45.257s32.759-12.497 45.257 0l75.328 75.328c12.497 12.497 12.497 32.759 0 45.257s-32.759 12.497-45.257 0l-75.328-75.328zM86.4 512c0 17.673 14.327 32 32 32h106.667c17.673 0 32-14.327 32-32s-14.327-32-32-32h-106.667c-17.673 0-32 14.327-32 32zM800 544c-17.673 0-32-14.327-32-32s14.327-32 32-32h106.667c17.673 0 32 14.327 32 32s-14.327 32-32 32h-106.667zM666.816 512c0-85.505-69.316-154.82-154.82-154.82-85.503 0-154.817 69.315-154.817 154.82 0 85.504 69.315 154.816 154.817 154.816 85.504 0 154.82-69.312 154.82-154.816zM725.329 512c0 117.82-95.509 213.333-213.333 213.333-117.819 0-213.332-95.514-213.332-213.333 0-117.822 95.512-213.335 213.332-213.335 117.824 0 213.333 95.513 213.333 213.335z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sun"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":382,"id":161,"name":"sun","prevSize":32,"code":59669},"setIdx":0,"setId":3,"iconIdx":11},{"icon":{"paths":["M195.378 191.997c0-15.807 12.815-28.622 28.622-28.622h128c15.807 0 28.622 12.815 28.622 28.622s-12.815 28.622-28.622 28.622h-128c-15.807 0-28.622-12.814-28.622-28.622z","M736 559.996c0 97.203-78.797 176-176 176s-176-78.797-176-176c0-97.203 78.797-175.999 176-175.999s176 78.796 176 175.999zM672 559.996c0-61.854-50.146-112-112-112s-112 50.146-112 112c0 61.858 50.146 112 112 112s112-50.142 112-112z","M192 255.997c-35.346 0-64 28.654-64 64v447.999c0 35.345 28.654 64 64 64h640c35.345 0 64-28.655 64-64v-447.999c0-35.346-28.655-64-64-64h-640zM832 319.997v447.999h-640v-447.999h640z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-photo"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":383,"id":160,"name":"camera-photo","prevSize":32,"code":59670},"setIdx":0,"setId":3,"iconIdx":12},{"icon":{"paths":["M700.147 361.892c4.681-24.975 34.701-34.093 49.126-13.174 31.991 46.404 50.726 102.656 50.726 163.281 0 60.629-18.735 116.881-50.726 163.285-14.426 20.919-44.446 11.802-49.126-13.175l-1.707-9.101c-1.583-8.431 0.384-17.084 4.855-24.401 20.749-33.967 32.704-73.89 32.704-116.608 0-42.714-11.955-82.637-32.704-116.604-4.471-7.319-6.438-15.972-4.855-24.402l1.707-9.102z","M320.705 395.396c-20.749 33.967-32.705 73.891-32.705 116.604 0 42.718 11.956 82.641 32.705 116.608 4.471 7.317 6.436 15.97 4.856 24.401l-1.707 9.101c-4.683 24.977-34.703 34.095-49.127 13.175-31.994-46.404-50.728-102.656-50.728-163.285 0-60.625 18.734-116.878 50.728-163.281 14.423-20.919 44.444-11.801 49.127 13.174l1.707 9.102c1.58 8.43-0.385 17.083-4.856 24.402z","M728.764 209.256l-0.512 2.747c-2.236 11.911 2.534 23.968 11.763 31.822 75.861 64.565 123.985 160.751 123.985 268.175 0 107.426-48.124 203.614-123.985 268.177-9.229 7.855-13.999 19.913-11.763 31.821l0.512 2.748c4.194 22.362 29.692 33.195 47.262 18.743 92.796-76.292 151.974-191.979 151.974-321.489 0-129.507-59.179-245.193-151.974-321.489-17.57-14.448-43.068-3.615-47.262 18.745z","M283.986 243.825c9.229-7.854 13.998-19.911 11.764-31.822l-0.515-2.747c-4.192-22.359-29.69-33.193-47.262-18.745-92.793 76.296-151.973 191.982-151.973 321.489 0 129.51 59.18 245.197 151.973 321.489 17.572 14.451 43.070 3.618 47.262-18.743l0.515-2.748c2.233-11.908-2.536-23.966-11.764-31.821-75.863-64.563-123.986-160.751-123.986-268.177 0-107.424 48.122-203.61 123.986-268.175z","M608 512c0 53.022-42.982 96-96 96s-96-42.978-96-96c0-53.018 42.982-95.999 96-95.999s96 42.981 96 95.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["live"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":384,"id":159,"name":"live","prevSize":32,"code":59671},"setIdx":0,"setId":3,"iconIdx":13},{"icon":{"paths":["M527.782 128c75.546 0 138.859 52.355 155.644 122.761l-59.644 59.644v-22.406c0-53.019-42.982-96-96-96-53.022 0-96 42.981-96 96v128c0 24.068 8.858 46.067 23.488 62.916l-45.322 45.321c-26.182-28.484-42.167-66.496-42.167-108.237v-128c0-88.366 71.634-160 160.001-160zM577.131 568.243l102.895-102.895c-15.765 48.674-54.221 87.13-102.895 102.895zM303.781 416c0 72.154 23.182 123.102 55.327 159.078l-45.303 45.303c-43.427-47.351-74.024-113.993-74.024-204.382 0-17.673 14.327-32 32-32s32 14.327 32 32zM494.438 650.935l-51.806 51.81c18.56 6.374 36.578 10.807 53.15 13.636v115.618h-160.001c-17.673 0-32 14.327-32 32s14.327 32 32 32h384.001c17.673 0 32-14.327 32-32s-14.327-32-32-32h-160v-115.618c44.638-7.62 99.819-26.897 147.721-64.38 60.702-47.501 108.279-123.29 108.279-236.002 0-17.673-14.327-32-32-32s-32 14.327-32 32c0 92.087-37.76 149.632-83.721 185.6-46.468 36.361-102.737 51.58-140.279 54.327-9.971-0.73-21.265-2.338-33.344-4.992zM841.152 153.373c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-672 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l671.998-672.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["microphone-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":385,"id":158,"name":"microphone-disabled","prevSize":32,"code":59672},"setIdx":0,"setId":3,"iconIdx":14},{"icon":{"paths":["M621.53 288c0-53.019-42.982-96-96-96-53.022 0-96 42.981-96 96v128c0 53.018 42.978 96 96 96 53.018 0 96-42.982 96-96v-128zM365.529 288c0-88.366 71.634-160 160.001-160 88.363 0 160 71.634 160 160v128c0 88.367-71.637 160-160 160-88.367 0-160.001-71.633-160.001-160v-128zM269.529 384c17.673 0 32 14.327 32 32 0 92.087 37.758 149.632 83.72 185.6 46.465 36.361 102.734 51.58 140.281 54.327 37.542-2.748 93.811-17.967 140.279-54.327 45.961-35.968 83.721-93.513 83.721-185.6 0-17.673 14.327-32 32-32s32 14.327 32 32c0 112.713-47.578 188.501-108.279 236.002-47.902 37.483-103.083 56.759-147.721 64.38v115.618h160c17.673 0 32 14.327 32 32s-14.327 32-32 32h-384.001c-17.673 0-32-14.327-32-32s14.327-32 32-32h160.001v-115.618c-44.642-7.62-99.819-26.897-147.721-64.38-60.705-47.501-108.28-123.29-108.28-236.002 0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["microphone"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":386,"id":157,"name":"microphone","prevSize":32,"code":59673},"setIdx":0,"setId":3,"iconIdx":15},{"icon":{"paths":["M384 149.138c0-11.766 9.551-21.305 21.333-21.305h85.333c11.78 0 21.333 9.539 21.333 21.305v106.526h-106.667c-11.782 0-21.333-9.539-21.333-21.305v-85.221z","M512 255.665h106.667c11.78 0 21.333 9.539 21.333 21.305v85.221c0 11.767-9.553 21.305-21.333 21.305h-106.667v-127.831z","M384 404.801c0-11.767 9.551-21.305 21.333-21.305h106.667v127.83h-106.667c-11.782 0-21.333-9.536-21.333-21.303v-85.222z","M512 511.326h106.667c11.78 0 21.333 9.54 21.333 21.308v106.526h-128v-127.834z","M426.667 639.159c-23.564 0-42.667 19.076-42.667 42.607v170.445c0 23.531 19.103 42.611 42.667 42.611h170.667c23.565 0 42.667-19.081 42.667-42.611v-213.052h-213.333zM490.667 724.378c-11.78 0-21.333 9.54-21.333 21.308v42.607c0 11.767 9.553 21.308 21.333 21.308h42.667c11.78 0 21.333-9.54 21.333-21.308v-42.607c0-11.767-9.553-21.308-21.333-21.308h-42.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["zip"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":387,"id":156,"name":"zip","prevSize":32,"code":59674},"setIdx":0,"setId":3,"iconIdx":16},{"icon":{"paths":["M288 224c-17.673 0-32 14.327-32 32s14.327 32 32 32h256c17.673 0 32-14.327 32-32s-14.327-32-32-32h-256z","M170.667 341.328c-23.564 0-42.667 19.102-42.667 42.667v384.001c0 23.565 19.103 42.667 42.667 42.667h512c23.565 0 42.667-19.102 42.667-42.667v-128l97.83 97.83c26.88 26.876 72.836 7.842 72.836-30.17v-263.322c0-38.012-45.956-57.049-72.836-30.17l-97.83 97.831v-128.001c0-23.564-19.102-42.667-42.667-42.667h-512z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-filled"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":388,"id":155,"name":"camera-filled","prevSize":32,"code":59675},"setIdx":0,"setId":3,"iconIdx":17},{"icon":{"paths":["M809.003 166.758c7.782 6.063 12.331 15.377 12.331 25.242v448h-0.111c0.073 1.732 0.111 3.477 0.111 5.231 0 69.052-57.306 125.026-128 125.026s-128-55.974-128-125.026c0-69.052 57.306-125.030 128-125.030 23.313 0 45.171 6.089 64 16.725v-303.858l-384 96.797v409.139c0 69.052-57.308 125.035-128 125.035s-128-55.979-128-125.030c0-69.052 57.308-125.026 128-125.026 23.314 0 45.173 6.089 64 16.725v-325.777c0-14.66 9.963-27.446 24.178-31.029l448.001-112.929c9.566-2.412 19.708-0.276 27.49 5.787z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["musical-note"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":389,"id":154,"name":"musical-note","prevSize":32,"code":59676},"setIdx":0,"setId":3,"iconIdx":18},{"icon":{"paths":["M533.333 128c200.294 0 362.667 162.371 362.667 362.667h-362.667v-362.667z","M128 533.333c0-200.295 162.371-362.667 362.667-362.667v362.667h362.667c0 200.294-162.372 362.667-362.667 362.667s-362.667-162.372-362.667-362.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pizza-graph"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":390,"id":153,"name":"pizza-graph","prevSize":32,"code":59677},"setIdx":0,"setId":3,"iconIdx":19},{"icon":{"paths":["M405.333 405.333c0 47.13-38.205 85.333-85.333 85.333s-85.333-38.204-85.333-85.333c0-47.128 38.205-85.333 85.333-85.333s85.333 38.205 85.333 85.333z","M96 192c0-17.673 14.327-32 32-32h768c17.673 0 32 14.327 32 32v640c0 17.673-14.327 32-32 32h-768c-17.673 0-32-14.327-32-32v-640zM160 764.164l151.704-176.99c9.269-10.816 24.571-14.199 37.538-8.307l153.124 69.602 160.474-204.241c6.007-7.646 15.172-12.147 24.9-12.228 9.728-0.077 18.961 4.271 25.097 11.823l151.164 186.048v-405.871h-704v540.164zM213.575 800h650.425v-68.638l-175.582-216.102-166.78 212.271-176.99-80.448-131.073 152.917z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["image"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":391,"id":152,"name":"image","prevSize":32,"code":59678},"setIdx":0,"setId":3,"iconIdx":20},{"icon":{"paths":["M671.057 183.163c16.683 16.663 16.683 43.677 0 60.34l-268.852 268.497 268.852 268.497c16.683 16.661 16.683 43.678 0 60.339-16.687 16.661-43.738 16.661-60.42 0l-299.061-298.667c-8.012-8-12.513-18.854-12.513-30.17s4.501-22.17 12.513-30.17l299.061-298.667c16.683-16.662 43.733-16.662 60.42 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-left-big"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":392,"id":151,"name":"chevron-left-big","prevSize":32,"code":59679},"setIdx":0,"setId":3,"iconIdx":21},{"icon":{"paths":["M160.253 256c0-17.673 14.327-32 32-32h640.844c17.677 0 32 14.327 32 32s-14.323 32-32 32h-640.844c-17.673 0-32-14.327-32-32zM160.253 512c0-17.673 14.327-32 32-32h640.844c17.677 0 32 14.327 32 32s-14.323 32-32 32h-640.844c-17.673 0-32-14.327-32-32zM160.253 768c0-17.673 14.327-32 32-32h640.844c17.677 0 32 14.327 32 32s-14.323 32-32 32h-640.844c-17.673 0-32-14.327-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hamburguer"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":393,"id":150,"name":"hamburguer","prevSize":32,"code":59680},"setIdx":0,"setId":3,"iconIdx":22},{"icon":{"paths":["M512 96c80.094 0 154.901 22.636 218.377 61.859l-46.746 46.746c-24.738-13.843-51.324-24.785-79.287-32.368 12.169 22.394 23.078 49.096 32.405 79.249l-51.998 51.999c-5.658-22.993-12.169-44.004-19.349-62.666-12.821-33.338-26.522-55.927-38.473-69.070-8.397-9.235-13.431-11.29-14.929-11.698-1.498 0.407-6.532 2.463-14.929 11.698-11.951 13.143-25.651 35.731-38.473 69.070-16.299 42.377-29.175 96.867-36.34 159.182h65.976l-64 63.999h-7.114c-0.116 2.47-0.224 4.949-0.322 7.437l-64.536 64.538c-0.174-7.936-0.262-15.927-0.262-23.974 0-16.23 0.358-32.247 1.054-48h-189.809c-2.14 15.697-3.245 31.718-3.245 48 0 33.28 4.619 65.485 13.251 96h106.984l-64 64h-17.852c2.004 3.921 4.079 7.799 6.223 11.631l-46.746 46.746c-39.223-63.475-61.859-138.283-61.859-218.377 0-229.751 186.249-416 416-416zM866.142 293.623l-46.746 46.746c10.56 18.873 19.43 38.819 26.411 59.632h-86.046l-64 63.999h164.992c2.142 15.697 3.247 31.718 3.247 48 0 33.28-4.617 65.485-13.252 96h-183.027c2.799-30.814 4.279-62.959 4.279-96 0-8.047-0.085-16.038-0.26-23.974l-64.538 64.538c-0.751 19.008-2.022 37.517-3.763 55.437h-51.678l-64 64h107.093c-7.369 42.458-17.489 80.081-29.453 111.185-12.821 33.335-26.522 55.923-38.473 69.069-8.397 9.233-13.431 11.29-14.929 11.695-1.498-0.405-6.532-2.462-14.929-11.695-11.951-13.146-25.651-35.733-38.473-69.069-7.177-18.662-13.692-39.676-19.349-62.673l-51.998 52.002c9.327 30.153 20.238 56.853 32.404 79.249-27.964-7.582-54.546-18.526-79.285-32.367l-46.746 46.746c63.347 39.142 137.984 61.769 217.899 61.858h0.956c229.53-0.26 415.522-186.411 415.522-416 0-80.094-22.635-154.903-61.858-218.377zM419.657 172.237c-113.551 30.788-204.311 116.98-241.465 227.764h179.674c10-93.227 32.176-173.254 61.791-227.764zM604.348 851.763c24.393-44.902 43.738-107.119 55.39-179.763h165.879c-44.651 87.347-124.723 153.583-221.269 179.763zM825.374 153.373c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-671.999 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l672.001-672.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["directory-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":394,"id":149,"name":"directory-disabled","prevSize":32,"code":59681},"setIdx":0,"setId":3,"iconIdx":23},{"icon":{"paths":["M864 511.326c0 33.237-4.621 65.399-13.252 95.876h-183.027c2.799-30.78 4.279-62.878 4.279-95.876 0-16.209-0.358-32.205-1.054-47.936h189.807c2.142 15.676 3.247 31.676 3.247 47.936zM606.882 463.39c0.734 15.65 1.118 31.646 1.118 47.936 0 33.353-1.604 65.468-4.561 95.876h-182.876c-2.958-30.409-4.562-62.524-4.562-95.876 0-16.29 0.383-32.286 1.12-47.936h189.762zM666.133 399.476c-9.997-93.104-32.175-173.025-61.79-227.464 113.553 30.747 204.309 116.826 241.463 227.464h-179.674zM419.657 172.012c-29.615 54.438-51.791 134.359-61.791 227.464h-179.674c37.155-110.638 127.914-196.717 241.465-227.464zM422.259 399.476c7.164-62.232 20.041-116.651 36.34-158.972 12.821-33.295 26.522-55.853 38.473-68.979 8.397-9.223 13.431-11.276 14.929-11.683 1.498 0.407 6.532 2.46 14.929 11.683 11.951 13.126 25.651 35.684 38.473 68.979 16.299 42.321 29.175 96.739 36.339 158.972h-179.482zM353.054 463.39c-0.696 15.731-1.054 31.727-1.054 47.936 0 32.998 1.482 65.097 4.282 95.876h-183.031c-8.632-30.477-13.251-62.639-13.251-95.876 0-16.26 1.105-32.26 3.245-47.936h189.809zM364.263 671.117c11.652 72.55 30.999 134.682 55.392 179.524-96.549-26.146-176.621-92.292-221.273-179.524h165.881zM512.478 926.775c229.53-0.256 415.522-186.163 415.522-415.45 0-229.447-186.249-415.451-416-415.451s-416 186.004-416 415.451c0 229.291 185.992 415.194 415.522 415.45 0.158 0.004 0.32 0.004 0.478 0.004s0.32 0 0.478-0.004zM604.348 850.641c24.393-44.843 43.738-106.978 55.39-179.524h165.879c-44.651 87.232-124.723 153.378-221.269 179.524zM594.854 671.117c-7.369 42.398-17.489 79.974-29.453 111.036-12.821 33.293-26.522 55.851-38.473 68.979-8.397 9.22-13.431 11.273-14.929 11.682-1.498-0.41-6.532-2.462-14.929-11.682-11.951-13.129-25.651-35.686-38.473-68.979-11.964-31.061-22.080-68.638-29.453-111.036h165.709z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["directory"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":395,"id":148,"name":"directory","prevSize":32,"code":59682},"setIdx":0,"setId":3,"iconIdx":24},{"icon":{"paths":["M864 512c0 194.402-157.598 352-352 352-194.404 0-352-157.598-352-352 0-194.404 157.596-352 352-352 194.402 0 352 157.596 352 352zM928 512c0-229.751-186.249-416-416-416s-416 186.249-416 416c0 229.751 186.249 416 416 416s416-186.249 416-416zM544 288c0-17.673-14.327-32-32-32s-32 14.327-32 32v224c0 8.486 3.371 16.627 9.374 22.626l96 96c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-86.626-86.63v-210.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clock"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":396,"id":147,"name":"clock","prevSize":32,"code":59683},"setIdx":0,"setId":3,"iconIdx":25},{"icon":{"paths":["M577.353 256c0 35.346-28.655 64-64 64s-64-28.654-64-64c0-35.346 28.655-64 64-64s64 28.654 64 64z","M577.353 512c0 35.345-28.655 64-64 64s-64-28.655-64-64c0-35.345 28.655-64 64-64s64 28.655 64 64z","M577.353 768c0 35.345-28.655 64-64 64s-64-28.655-64-64c0-35.345 28.655-64 64-64s64 28.655 64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["kebab"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":397,"id":146,"name":"kebab","prevSize":32,"code":59684},"setIdx":0,"setId":3,"iconIdx":26},{"icon":{"paths":["M512 873.024c-199.389 0-361.026-161.634-361.026-361.024s161.637-361.026 361.026-361.026c199.39 0 361.024 161.637 361.024 361.026s-161.634 361.024-361.024 361.024zM512 938.667c235.639 0 426.667-191.027 426.667-426.667 0-235.642-191.027-426.667-426.667-426.667-235.642 0-426.667 191.025-426.667 426.667 0 235.639 191.025 426.667 426.667 426.667zM544.819 347.898c0 18.126-14.694 32.82-32.819 32.82s-32.819-14.694-32.819-32.82c0-18.127 14.694-32.82 32.819-32.82s32.819 14.694 32.819 32.82zM512 413.539c-18.125 0-32.819 14.694-32.819 32.819v229.747c0 18.125 14.694 32.819 32.819 32.819s32.819-14.694 32.819-32.819v-229.747c0-18.125-14.694-32.819-32.819-32.819z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["info"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":398,"id":145,"name":"info","prevSize":32,"code":59685},"setIdx":0,"setId":3,"iconIdx":27},{"icon":{"paths":["M864 511.326c0-194.147-157.598-351.535-352-351.535-194.404 0-352 157.388-352 351.535 0 194.15 157.596 351.535 352 351.535 194.402 0 352-157.385 352-351.535zM928 511.326c0 229.449-186.249 415.454-416 415.454s-416-186.005-416-415.454c0-229.447 186.249-415.451 416-415.451s416 186.004 416 415.451zM694.259 570.615l-159.97 155.511c-12.425 12.079-32.218 12.079-44.642 0l-159.968-155.511c-12.664-12.309-12.937-32.542-0.61-45.188 12.328-12.651 32.587-12.924 45.251-0.61l105.65 102.707v-275.987c0-17.65 14.327-31.958 32-31.958s32 14.308 32 31.958v275.987l105.647-102.707c12.668-12.314 32.926-12.041 45.252 0.61 12.326 12.646 12.053 32.879-0.61 45.188z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down-circle"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":399,"id":144,"name":"arrow-down-circle","prevSize":32,"code":59686},"setIdx":0,"setId":3,"iconIdx":28},{"icon":{"paths":["M512 864c194.402 0 352-157.598 352-352 0-46.519-9.024-90.932-25.417-131.582l48.516-48.518c26.214 54.496 40.9 115.584 40.9 180.1 0 229.751-186.249 416-416 416s-416-186.249-416-416c0-229.751 186.249-416 416-416 95.377 0 183.253 32.096 253.423 86.076l-45.709 45.712c-58.223-42.623-130.031-67.788-207.714-67.788-194.404 0-352 157.596-352 352 0 194.402 157.596 352 352 352zM902.63 230.624c12.497-12.499 12.493-32.76-0.009-45.255-12.497-12.495-32.759-12.491-45.252 0.008l-345.387 345.504-105.339-105.491c-12.488-12.506-32.749-12.52-45.255-0.032-12.506 12.489-12.52 32.747-0.032 45.253l127.971 128.158c6.003 6.007 14.144 9.387 22.635 9.387 8.495 0.004 16.636-3.371 22.643-9.374l368.026-368.156z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-check"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":400,"id":143,"name":"circle-check","prevSize":32,"code":59687},"setIdx":0,"setId":3,"iconIdx":29},{"icon":{"paths":["M512 352.004c17.673 0 32 14.327 32 32v224c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32z","M512 672.004c17.673 0 32 14.327 32 32s-14.327 32-32 32c-17.673 0-32-14.327-32-32s14.327-32 32-32z","M567.1 158.348c-24.772-41.922-85.427-41.922-110.199 0l-359.921 609.098c-25.21 42.662 5.544 96.559 55.099 96.559h719.845c49.553 0 80.307-53.897 55.095-96.559l-359.919-609.098zM512 190.906l359.923 609.098h-719.845l359.922-609.098z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["warning"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":401,"id":142,"name":"warning","prevSize":32,"code":59688},"setIdx":0,"setId":3,"iconIdx":30},{"icon":{"paths":["M512.606 128.018c-72.375-1.375-144.904 25.78-199.305 80.524-54.654 54.999-89.301 136.056-89.301 239.466 0 80.23 44.5 174.869 97.546 252.804 53.065 77.965 120.83 148.16 176.139 175.821l15.194 7.603 14.81-8.333c217.156-122.15 272.311-333.879 272.311-427.895 0-101.732-27.925-181.775-80.179-236.931-52.343-55.247-125.295-81.503-207.215-83.060zM288 448.009c0-88.591 29.353-152.747 70.699-194.353 41.599-41.862 97.071-62.705 152.691-61.648 68.992 1.311 124.041 23.055 161.971 63.089 38.016 40.126 62.639 102.647 62.639 192.913 0 74.551-44.689 253.679-224.175 363.034-39.684-25.613-92.187-79.855-137.371-146.24-50.954-74.863-86.454-156.22-86.454-216.794zM544 416c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 17.673 14.327 32 32 32s32-14.327 32-32zM608 416c0 53.018-42.982 96-96 96s-96-42.982-96-96c0-53.019 42.982-96 96-96s96 42.981 96 96z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin-map"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":402,"id":141,"name":"pin-map","prevSize":32,"code":59689},"setIdx":0,"setId":3,"iconIdx":31},{"icon":{"paths":["M192 736h640v-448h-640v448zM128 256c0-17.673 14.327-32 32-32h704c17.673 0 32 14.327 32 32v512c0 17.673-14.327 32-32 32h-704c-17.673 0-32-14.327-32-32v-512zM305.304 389.082c-14.866-9.557-34.665-5.253-44.222 9.613s-5.253 34.665 9.613 44.223l241.304 155.123 241.306-155.123c14.865-9.557 19.17-29.356 9.613-44.223s-29.355-19.17-44.224-9.613l-206.694 132.876-206.696-132.876z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mail"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":403,"id":140,"name":"mail","prevSize":32,"code":59690},"setIdx":0,"setId":3,"iconIdx":32},{"icon":{"paths":["M160 224c-35.346 0-64 28.654-64 64v448c0 35.345 28.654 64 64 64h704c35.345 0 64-28.655 64-64v-448c0-35.346-28.655-64-64-64h-704zM160 288h704v448h-704v-448zM256 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM256 512c0-17.673 14.327-32 32-32h64c17.673 0 32 14.327 32 32s-14.327 32-32 32h-64c-17.673 0-32-14.327-32-32zM480 480c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM640 512c0-17.673 14.327-32 32-32h64c17.673 0 32 14.327 32 32s-14.327 32-32 32h-64c-17.673 0-32-14.327-32-32zM480 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM320 640c0-17.673 14.327-32 32-32h320c17.673 0 32 14.327 32 32s-14.327 32-32 32h-320c-17.673 0-32-14.327-32-32zM704 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["keyboard"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":404,"id":139,"name":"keyboard","prevSize":32,"code":59691},"setIdx":0,"setId":3,"iconIdx":33},{"icon":{"paths":["M266.667 170.667c0-17.673 14.327-32 32-32h500.621c12.122 0 23.202 6.848 28.625 17.689 5.419 10.841 4.25 23.814-3.025 33.511l-122.133 162.845 122.133 162.843c7.275 9.698 8.444 22.673 3.025 33.51-5.423 10.842-16.503 17.69-28.625 17.69h-468.621v286.579c0 17.673-14.327 32-32 32s-32-14.327-32-32v-682.667zM330.667 502.754h404.621l-98.133-130.843c-8.533-11.378-8.533-27.023 0-38.4l98.133-130.845h-404.621v300.087z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["flag"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":405,"id":138,"name":"flag","prevSize":32,"code":59692},"setIdx":0,"setId":3,"iconIdx":34},{"icon":{"paths":["M354.254 512c-88.366 0-160-71.633-160-160 0-88.366 71.634-160 160-160s159.999 71.634 159.999 160c0 26.934-6.656 52.313-18.411 74.583l11.917 11.915-0.132 0.128 299.136 299.136c3.533 9.924 6.409 20.975 7.74 31.607 1.651 13.218 0.563 22.967-1.877 28.937-1.766 4.335-4.22 7.232-10.769 9.015-8.316 2.261-24.521 2.795-52.821-5.504-8.525-3.806-27.721-16.282-45.133-35.379-18.18-19.938-29.649-41.856-29.649-62.438 0-15.642-11.307-28.992-26.739-31.565l-91.75-15.292c-3.763-2.658-18.765-15.693-10.129-58.867 2.095-10.492-1.186-21.338-8.751-28.902l-88.201-88.196c-26.467 19.379-59.111 30.822-94.429 30.822zM570.423 410.916c5.107-18.773 7.829-38.527 7.829-58.916 0-123.712-100.288-224-223.999-224-123.712 0-224 100.288-224 224s100.288 224 224 224c29.287 0 57.261-5.619 82.904-15.842l43.008 43.008c-7.155 65.493 23.991 104.533 55.97 115.191l2.381 0.794 74.854 12.476c7.083 31.407 25.173 58.125 43.234 77.935 23.125 25.357 50.961 44.629 69.764 52.151l1.323 0.529 1.365 0.41c34.714 10.415 64.73 13.188 89.596 6.426 26.782-7.283 44.331-24.785 53.227-46.583 8.23-20.164 8.474-42.283 6.127-61.065-2.402-19.217-7.898-37.956-14.042-53.312-1.609-4.023-4.019-7.68-7.083-10.743l-286.46-286.458zM386.254 336c0 26.51-21.49 48-48 48s-48-21.49-48-48c0-26.51 21.49-48 48-48s48 21.49 48 48z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["encrypted"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":406,"id":137,"name":"encrypted","prevSize":32,"code":59693},"setIdx":0,"setId":3,"iconIdx":35},{"icon":{"paths":["M694.626 300.76c12.497-12.854 12.497-33.694 0-46.548l-160-164.571c-12.497-12.854-32.755-12.854-45.252 0l-160.001 164.571c-12.497 12.854-12.497 33.694 0 46.548s32.758 12.854 45.255 0l105.373-108.383v430.711c0 18.176 14.327 32.913 32 32.913s32-14.737 32-32.913v-430.711l105.374 108.384c12.497 12.854 32.755 12.854 45.252 0z","M192 384h160v64h-128v416h576v-416h-128v-64h160c17.673 0 32 14.327 32 32v480c0 17.673-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32v-480c0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["share"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":407,"id":136,"name":"share","prevSize":32,"code":59694},"setIdx":0,"setId":3,"iconIdx":36},{"icon":{"paths":["M273.128 356.679c-6.619 27.765-2.384 63.755 25.498 105.58l21.374 32.060v17.69h-48c-44.011 0-70.483 14.874-86.278 33.621-16.496 19.575-24.573 47.262-23.734 77.491 0.841 30.259 10.601 59.968 25.818 81.31 15.127 21.218 33.403 31.578 52.194 31.578h80v64h-80c-45.209 0-80.933-25.638-104.305-58.423-23.283-32.657-36.523-74.948-37.682-116.689-1.161-41.771 9.763-86.084 38.767-120.508 20.726-24.597 49.155-42.317 84.825-50.782-16.1-39.010-19.009-77.050-10.731-111.77 11.253-47.2 42.304-84.492 80.791-107.342 38.399-22.798 85.676-32.138 131.301-21.965 35.587 7.935 68.909 27.48 95.252 59.554 53.751-35.147 127.582-30.892 182.485-2.495 34.436 17.812 64.794 46.382 81.438 85.009 12.292 28.531 16.435 61.011 10.603 96.206 46.114 6.682 81.51 25.156 105.617 53.453 30.349 35.627 38.106 81.353 33.446 123.285-4.659 41.92-21.948 83.486-46.545 115.115-24.060 30.933-59.354 57.353-101.261 57.353h-80v-64h80c14.093 0 32.802-9.579 50.739-32.644 17.404-22.374 30.114-52.804 33.455-82.889 3.341-30.067-2.901-56.341-18.556-74.718-15.219-17.869-44.032-33.749-97.638-33.749h-45.687l15.616-42.935c13.542-37.252 11.089-66.743 1.434-89.149-9.856-22.872-28.501-41.302-52.062-53.489-49.587-25.649-107.477-18.624-134.716 14.063l-30.135 36.162-22.541-41.324c-19.759-36.22-47.232-54.176-74.872-60.34-28.375-6.327-59.098-0.669-84.699 14.531-25.513 15.148-44.462 38.854-51.209 67.153zM630.98 787.221c12.297-12.689 11.981-32.947-0.708-45.248-12.693-12.301-32.951-11.985-45.252 0.708l-41.020 42.321v-273.003c0-17.673-14.327-32-32-32s-32 14.327-32 32v273.003l-41.020-42.321c-12.301-12.693-32.561-13.009-45.251-0.708s-13.007 32.559-0.707 45.248l95.998 99.051c6.029 6.217 14.319 9.728 22.98 9.728s16.951-3.511 22.98-9.728l96-99.051z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["download"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":408,"id":135,"name":"download","prevSize":32,"code":59695},"setIdx":0,"setId":3,"iconIdx":37},{"icon":{"paths":["M273.128 356.679c-6.619 27.765-2.384 63.755 25.498 105.58l33.166 49.749h-59.792c-44.011 0-70.483 14.874-86.278 33.621-16.496 19.575-24.573 47.262-23.734 77.491 0.841 30.259 10.601 59.968 25.818 81.31 15.127 21.218 33.403 31.578 52.194 31.578h144v64h-144c-45.209 0-80.933-25.638-104.305-58.423-23.283-32.657-36.523-74.948-37.682-116.689-1.161-41.771 9.763-86.084 38.767-120.508 20.726-24.597 49.155-42.317 84.825-50.782-16.1-39.010-19.009-77.050-10.731-111.77 11.253-47.2 42.304-84.492 80.791-107.342 38.399-22.798 85.676-32.138 131.301-21.965 35.587 7.935 68.909 27.48 95.252 59.554 53.751-35.147 127.582-30.892 182.485-2.495 34.436 17.812 64.794 46.382 81.438 85.009 12.292 28.531 16.435 61.011 10.603 96.206 46.114 6.682 81.51 25.156 105.617 53.453 30.349 35.627 38.106 81.353 33.446 123.285-4.659 41.92-21.948 83.486-46.545 115.115-24.060 30.933-59.354 57.353-101.261 57.353h-144v-64h144c14.093 0 32.802-9.579 50.739-32.644 17.404-22.374 30.114-52.804 33.455-82.889 3.341-30.067-2.901-56.341-18.556-74.718-15.219-17.869-44.032-33.749-97.638-33.749h-45.687l15.616-42.935c13.547-37.252 11.089-66.743 1.434-89.149-9.856-22.872-28.501-41.302-52.062-53.489-49.587-25.649-107.477-18.624-134.716 14.063l-30.135 36.162-22.541-41.324c-19.759-36.22-47.232-54.176-74.872-60.34-28.375-6.327-59.098-0.669-84.699 14.531-25.513 15.148-44.462 38.854-51.209 67.153zM630.98 588.779l-96-99.051c-6.029-6.217-14.319-9.728-22.98-9.728s-16.951 3.511-22.98 9.728l-95.998 99.051c-12.3 12.689-11.983 32.947 0.707 45.248s32.951 11.985 45.251-0.708l41.020-42.321v273.003c0 17.673 14.327 32 32 32s32-14.327 32-32v-273.003l41.020 42.321c12.301 12.693 32.559 13.009 45.252 0.708 12.689-12.301 13.005-32.559 0.708-45.248z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["upload"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":409,"id":134,"name":"upload","prevSize":32,"code":59696},"setIdx":0,"setId":3,"iconIdx":38},{"icon":{"paths":["M444.156 742.626c-12.497-12.497-12.497-32.755 0-45.252l185.374-185.374-185.374-185.373c-12.497-12.497-12.497-32.758 0-45.255s32.759-12.497 45.257 0l208 208.001c12.497 12.497 12.497 32.755 0 45.252l-208 208c-12.497 12.497-32.759 12.497-45.257 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-right"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":410,"id":133,"name":"chevron-right","prevSize":32,"code":59697},"setIdx":0,"setId":3,"iconIdx":39},{"icon":{"paths":["M593.822 281.373c12.497 12.497 12.497 32.758 0 45.255l-185.372 185.373 185.372 185.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-208.002-208c-12.497-12.497-12.497-32.755 0-45.252l208.002-208.001c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-left"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":411,"id":132,"name":"chevron-left","prevSize":32,"code":59698},"setIdx":0,"setId":3,"iconIdx":40},{"icon":{"paths":["M746.236 587.959c-12.497 12.497-32.759 12.497-45.257 0l-185.374-185.371-185.371 185.371c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l208-208.001c12.497-12.497 32.759-12.497 45.257 0l208 208.001c12.497 12.497 12.497 32.755 0 45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-up"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":412,"id":131,"name":"chevron-up","prevSize":32,"code":59699},"setIdx":0,"setId":3,"iconIdx":41},{"icon":{"paths":["M282.726 436.041c12.497-12.498 32.758-12.498 45.255 0l185.372 185.37 185.374-185.37c12.497-12.498 32.755-12.498 45.252 0s12.497 32.755 0 45.252l-208 208c-12.497 12.497-32.755 12.497-45.252 0l-208.001-208c-12.497-12.497-12.497-32.755 0-45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-down"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":413,"id":130,"name":"chevron-down","prevSize":32,"code":59700},"setIdx":0,"setId":3,"iconIdx":42},{"icon":{"paths":["M697.698 626.56c12.676-12.314 32.934-12.023 45.252 0.649 12.314 12.676 12.023 32.934-0.649 45.252l-208 202.121c-12.42 12.066-32.183 12.066-44.604 0l-207.999-202.121c-12.675-12.318-12.965-32.576-0.649-45.252 12.317-12.672 32.576-12.962 45.25-0.649l185.699 180.45 185.698-180.45zM697.698 416.798c12.676 12.317 32.934 12.027 45.252-0.649 12.314-12.675 12.023-32.934-0.649-45.25l-208-202.119c-12.42-12.067-32.183-12.067-44.604 0l-207.999 202.119c-12.675 12.316-12.965 32.576-0.649 45.25 12.316 12.676 32.576 12.966 45.25 0.649l185.699-180.449 185.698 180.449z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["order"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":414,"id":129,"name":"order","prevSize":32,"code":59701},"setIdx":0,"setId":3,"iconIdx":43},{"icon":{"paths":["M930.253 512c0 229.751-186.249 416-416 416-229.749 0-415.999-186.249-415.999-416s186.249-416 415.999-416c229.751 0 416 186.249 416 416zM570.039 859.605l-41.83-156.105c-4.608 0.333-9.263 0.499-13.956 0.499-4.57 0-9.101-0.158-13.594-0.474l-41.835 156.134c18.057 2.854 36.57 4.339 55.428 4.339 18.982 0 37.615-1.502 55.787-4.395zM438.443 688.452c-47.551-20.459-85.181-59.571-103.675-108.126l-155.028 41.54c33.967 103.484 114.617 185.805 217.044 222.054l41.659-155.469zM162.254 512c0 16.090 1.079 31.927 3.17 47.445l156.904-42.044c-0.049-1.796-0.074-3.597-0.074-5.402 0-4.911 0.184-9.779 0.546-14.596l-156.051-41.813c-2.958 18.368-4.495 37.21-4.495 56.41zM395.858 180.407c-99.159 35.408-177.785 114.033-213.195 213.191l155.535 41.675c19.356-44.352 54.982-79.977 99.336-99.331l-41.676-155.535zM514.253 160c-19.196 0-38.037 1.537-56.401 4.495l41.813 156.051c4.817-0.361 9.681-0.546 14.588-0.546 5.030 0 10.018 0.194 14.95 0.573l41.805-156.022c-18.475-2.995-37.431-4.552-56.755-4.552zM632.064 843.802c102.656-36.45 183.39-119.194 217.092-223.121l-154.974-41.523c-18.291 48.981-56.009 88.491-103.787 109.15l41.668 155.494zM863.249 558.199c1.984-15.121 3.004-30.541 3.004-46.199 0-18.769-1.468-37.197-4.297-55.172l-156.156 41.843c0.299 4.403 0.452 8.849 0.452 13.329 0 1.378-0.013 2.752-0.043 4.122l157.039 42.078zM846.263 394.775c-35.166-99.601-113.886-178.641-213.278-214.248l-41.681 155.559c44.587 19.556 80.311 55.564 99.507 100.343l155.452-41.653zM642.253 512c0-70.694-57.306-128-128-128-70.69 0-127.999 57.306-127.999 128s57.309 128 127.999 128c70.694 0 128-57.306 128-128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["support"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":415,"id":128,"name":"support","prevSize":32,"code":59702},"setIdx":0,"setId":3,"iconIdx":44},{"icon":{"paths":["M336 128c17.673 0 32 14.327 32 32v144h288v-144c0-17.673 14.327-32 32-32s32 14.327 32 32v144h144c17.673 0 32 14.327 32 32s-14.327 32-32 32h-144v288h144c17.673 0 32 14.327 32 32s-14.327 32-32 32h-144v144c0 17.673-14.327 32-32 32s-32-14.327-32-32v-144h-288v144c0 17.673-14.327 32-32 32s-32-14.327-32-32v-144h-144c-17.673 0-32-14.327-32-32s14.327-32 32-32h144v-288h-144c-17.673 0-32-14.327-32-32s14.327-32 32-32h144v-144c0-17.673 14.327-32 32-32zM368 368v288h288v-288h-288z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["channel-public"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":416,"id":127,"name":"channel-public","prevSize":32,"code":59703},"setIdx":0,"setId":3,"iconIdx":45},{"icon":{"paths":["M138.667 213.333c0-17.673 14.327-32 32-32h234.057c7.183 0 14.156 2.416 19.798 6.86l88.814 69.94h339.998c17.673 0 32 14.327 32 32v520.533c0 17.673-14.327 32-32 32h-682.667c-17.673 0-32-14.327-32-32v-597.333zM202.667 245.333v533.333h618.667v-456.533h-319.087c-7.181 0-14.153-2.416-19.797-6.86l-88.813-69.94h-190.97z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["folder"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":417,"id":126,"name":"folder","prevSize":32,"code":59704},"setIdx":0,"setId":3,"iconIdx":46},{"icon":{"paths":["M160 800c-11.706 0-22.478-6.391-28.087-16.666s-5.161-22.793 1.169-32.64l112.304-174.694h-69.387c-17.673 0-32-14.327-32-32v-288c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v288c0 6.135-1.762 12.143-5.082 17.306l-144.001 224c-5.888 9.156-16.029 14.694-26.918 14.694h-128zM330.918 561.306l-112.304 174.694h51.916l129.47-201.399v-246.601h-192v224h96c11.706 0 22.478 6.391 28.087 16.666s5.161 22.793-1.169 32.64zM576 800c-11.708 0-22.477-6.391-28.087-16.666s-5.158-22.793 1.169-32.64l112.307-174.694h-69.389c-17.673 0-32-14.327-32-32v-288c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v288c0 6.135-1.762 12.143-5.082 17.306l-144 224c-5.888 9.156-16.030 14.694-26.918 14.694h-128zM746.918 561.306l-112.307 174.694h51.917l129.472-201.399v-246.601h-192v224h96c11.708 0 22.477 6.391 28.087 16.666s5.158 22.793-1.169 32.64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["quote"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":418,"id":125,"name":"quote","prevSize":32,"code":59705},"setIdx":0,"setId":3,"iconIdx":47},{"icon":{"paths":["M321.626 430.882l224.49 224.491-126.12 126.118-224.49-224.491 126.12-126.118zM366.881 385.626l192.116-192.116 224.491 224.49-192.115 192.116-224.492-224.49zM581.623 125.628c-12.497-12.497-32.759-12.497-45.257 0l-408.743 408.747c-12.497 12.497-12.497 32.755 0 45.252l269.745 269.747c5.23 5.231 11.82 8.269 18.631 9.122v0.068h0.571c2.276 0.243 4.573 0.243 6.85 0h440.579c17.673 0 32-14.327 32-32s-14.327-32-32-32h-366.566l353.937-353.937c12.497-12.497 12.497-32.757 0-45.254l-269.747-269.745z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["prune"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":419,"id":124,"name":"prune","prevSize":32,"code":59706},"setIdx":0,"setId":3,"iconIdx":48},{"icon":{"paths":["M536.845 548.215h-255.502c-20.173 0-36.527-16.354-36.527-36.527s16.354-36.527 36.527-36.527h102.99c-21.546-22.75-32.32-50.118-32.32-82.102 0-39.587 15.766-72.27 47.297-98.048 31.764-26.008 72.959-39.012 123.596-39.012 34.526 0 65.25 6.675 92.177 20.024 27.162 13.349 48.107 31.762 62.835 55.238 8.849 13.888 15.083 28.54 18.697 43.958 4.198 17.914-11.17 33.030-29.572 33.030-18.398 0-32.499-15.279-37.828-32.89-4.617-15.263-12.779-28.121-24.486-38.574-19.332-17.492-46.609-26.238-81.822-26.238-32.683 0-58.231 7.25-76.642 21.75-18.185 14.27-27.275 34.179-27.275 59.726 0 20.484 8.629 37.86 25.892 52.132 13.764 11.046 35.012 21.385 63.744 31.006h247.556c20.173 0 36.527 16.354 36.527 36.527s-16.354 36.527-36.527 36.527h-90.027c7.138 7.215 13.133 14.878 17.975 22.989 11.511 18.871 17.263 41.079 17.263 66.628 0 40.738-15.881 73.421-47.642 98.048-31.761 24.397-74.227 36.595-127.394 36.595-34.522 0-66.743-6.558-96.666-19.678-29.921-13.35-53.052-31.531-69.393-54.549-9.777-13.965-16.588-29.077-20.432-45.338-4.233-17.903 11.182-33.028 29.581-33.028s32.466 15.364 38.446 32.764c5.318 15.475 14.771 28.604 28.356 39.39 22.558 17.493 52.591 26.236 90.108 26.236 34.987 0 61.798-7.134 80.439-21.402 18.645-14.272 27.968-33.719 27.968-58.347s-8.631-43.614-25.894-56.964c-14.332-11.273-38.34-22.387-72.026-33.344z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["strike"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":420,"id":123,"name":"strike","prevSize":32,"code":59707},"setIdx":0,"setId":3,"iconIdx":49},{"icon":{"paths":["M388.508 320c0-17.673-14.327-32-32-32s-32 14.327-32 32v256c0 106.039 85.961 192 192.002 192 106.035 0 192-85.961 192-192v-256c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32v256c0 70.694-57.31 128-128 128-70.694 0-128.002-57.306-128.002-128v-256zM356.508 848c-17.673 0-32 14.327-32 32s14.327 32 32 32h320.002c17.673 0 32-14.327 32-32s-14.327-32-32-32h-320.002z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["underline"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":421,"id":122,"name":"underline","prevSize":32,"code":59708},"setIdx":0,"setId":3,"iconIdx":50},{"icon":{"paths":["M546.253 352c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-32 14.327-32 32s14.327 32 32 32zM561.865 436.996c2.756-17.457-9.161-33.844-26.62-36.6-17.455-2.756-33.843 9.161-36.599 26.616l-48 304c-2.756 17.459 9.161 33.843 26.62 36.599 17.455 2.756 33.843-9.161 36.599-26.615l48-304z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["italic"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":422,"id":121,"name":"italic","prevSize":32,"code":59709},"setIdx":0,"setId":3,"iconIdx":51},{"icon":{"paths":["M384 775.415c-17.673 0-32-14.327-32-32v-453.166c0-17.673 14.327-32 32-32h137.075c56.119 0 98.27 11.603 126.447 34.81 28.416 23.206 42.624 57.542 42.624 103.007 0 24.153-6.865 45.585-20.599 64.29-13.734 18.47-32.444 32.798-56.124 42.978 27.942 7.817 49.967 22.733 66.069 44.757 16.337 21.786 24.508 47.834 24.508 78.144 0 46.409-15.036 82.876-45.111 109.397-30.071 26.522-72.576 39.782-127.514 39.782h-147.375zM420.198 533.53v186.121h112.598c31.731 0 56.713-8.171 74.944-24.508 18.47-16.576 27.708-39.309 27.708-68.198 0-62.276-33.865-93.414-101.585-93.414h-113.664zM420.198 478.827h103.006c29.837 0 53.636-7.458 71.394-22.379 17.997-14.916 26.995-35.163 26.995-60.737 0-28.416-8.286-49.017-24.862-61.804-16.576-13.024-41.796-19.536-75.657-19.536h-100.877v164.456z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bold"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":423,"id":120,"name":"bold","prevSize":32,"code":59710},"setIdx":0,"setId":3,"iconIdx":52},{"icon":{"paths":["M639.172 204.798c16.495 6.344 24.725 24.859 18.381 41.354l-213.333 554.667c-6.345 16.495-24.861 24.725-41.356 18.381s-24.724-24.862-18.38-41.357l213.335-554.666c6.34-16.495 24.858-24.724 41.353-18.38zM330.312 361.371c12.497 12.497 12.497 32.758 0 45.255l-105.373 105.374 105.373 105.37c12.497 12.497 12.497 32.759 0 45.257s-32.758 12.497-45.255 0l-128-128c-12.497-12.497-12.497-32.759 0-45.257l128-127.999c12.497-12.497 32.758-12.497 45.255 0zM711.723 361.371c12.497-12.497 32.759-12.497 45.257 0l128 127.999c12.497 12.497 12.497 32.759 0 45.257l-128 128c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.759 0-45.257l105.374-105.37-105.374-105.374c-12.497-12.497-12.497-32.758 0-45.255z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["code"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":424,"id":119,"name":"code","prevSize":32,"code":59711},"setIdx":0,"setId":3,"iconIdx":53},{"icon":{"paths":["M769.293 649.374c12.497 12.497 12.497 32.755 0 45.252l-192 192c-12.497 12.497-32.755 12.497-45.252 0l-192.001-192c-12.497-12.497-12.497-32.755 0-45.252s32.758-12.497 45.255 0l137.373 137.37v-578.743h-192v192c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v610.743l137.374-137.37c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump-to-message"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":425,"id":118,"name":"jump-to-message","prevSize":32,"code":59712},"setIdx":0,"setId":3,"iconIdx":54},{"icon":{"paths":["M385 769.293c-12.497 12.497-32.758 12.497-45.255 0l-192-192c-12.497-12.497-12.497-32.755 0-45.252l192-192.001c12.497-12.497 32.758-12.497 45.255 0s12.497 32.758 0 45.255l-137.373 137.373h578.745v-192h-192c-17.673 0-32-14.327-32-32s14.327-32 32-32h224c17.673 0 32 14.327 32 32v256c0 17.673-14.327 32-32 32h-610.745l137.373 137.374c12.497 12.497 12.497 32.755 0 45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-return"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":426,"id":117,"name":"arrow-return","prevSize":32,"code":59713},"setIdx":0,"setId":3,"iconIdx":55},{"icon":{"paths":["M385 297.373c-12.497-12.497-32.758-12.497-45.255 0l-192 192.001c-12.497 12.497-12.497 32.755 0 45.252l192 192c12.497 12.497 32.758 12.497 45.255 0s12.497-32.755 0-45.252l-137.373-137.374h578.745v128c0 17.673 14.327 32 32 32s32-14.327 32-32v-160c0-17.673-14.327-32-32-32h-610.745l137.373-137.373c12.497-12.497 12.497-32.758 0-45.255z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-back"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":427,"id":116,"name":"arrow-back","prevSize":32,"code":59714},"setIdx":0,"setId":3,"iconIdx":56},{"icon":{"paths":["M307.745 105.235c12.497-12.48 32.758-12.48 45.255 0s12.497 32.715 0 45.195l-105.373 105.233h610.745c17.673 0 32 14.308 32 31.958v111.852c0 17.65-14.327 31.959-32 31.959s-32-14.309-32-31.959v-79.894h-578.745l105.373 105.233c12.497 12.482 12.497 32.715 0 45.195s-32.758 12.48-45.255 0l-160-159.788c-12.497-12.48-12.497-32.715 0-45.195l160-159.789zM720.998 917.414c-12.497 12.48-32.755 12.48-45.252 0s-12.497-32.713 0-45.193l105.37-105.233h-610.743c-17.673 0-32-14.31-32-31.957v-111.855c0-17.647 14.327-31.957 32-31.957s32 14.31 32 31.957v79.898h578.743l-105.37-105.237c-12.497-12.48-12.497-32.713 0-45.193s32.755-12.48 45.252 0l160 159.787c12.497 12.48 12.497 32.717 0 45.197l-160 159.787z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-looping"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":428,"id":115,"name":"arrow-looping","prevSize":32,"code":59715},"setIdx":0,"setId":3,"iconIdx":57},{"icon":{"paths":["M276.48 157.44c0-16.259 13.181-29.44 29.44-29.44s29.44 13.181 29.44 29.44v58.877h353.28v-58.877c0-16.259 13.18-29.44 29.44-29.44s29.44 13.181 29.44 29.44v58.877h85.76c17.673 0 32 14.327 32 32v583.679c0 17.673-14.327 32-32 32h-642.56c-17.673 0-32-14.327-32-32v-583.679c0-1.105 0.056-2.196 0.165-3.272 1.639-16.136 15.267-28.728 31.835-28.728h85.76v-58.877zM801.28 392.957h-578.56v407.039h578.56v-407.039zM423.68 644.476c0-8.836 7.163-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM321.92 510.72c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.837 0 16-7.164 16-16v-26.88c0-8.836-7.163-16-16-16h-26.88zM541.44 526.72c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM321.92 628.476c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.837 0 16-7.164 16-16v-26.88c0-8.836-7.163-16-16-16h-26.88zM541.44 644.476c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM439.68 510.72c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.836 0 16-7.164 16-16v-26.88c0-8.836-7.164-16-16-16h-26.88zM659.2 526.72c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM675.2 628.476c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.836 0 16-7.164 16-16v-26.88c0-8.836-7.164-16-16-16h-26.88z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["calendar"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":429,"id":114,"name":"calendar","prevSize":32,"code":59716},"setIdx":0,"setId":3,"iconIdx":58},{"icon":{"paths":["M774.426 576c-35.345 0-64-28.655-64-64s28.655-64 64-64c35.345 0 64 28.655 64 64s-28.655 64-64 64z","M518.426 576c-35.345 0-64-28.655-64-64s28.655-64 64-64c35.345 0 64 28.655 64 64s-28.655 64-64 64z","M262.426 576c-35.346 0-64-28.655-64-64s28.654-64 64-64c35.346 0 64 28.655 64 64s-28.654 64-64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["meatballs"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":430,"id":113,"name":"meatballs","prevSize":32,"code":59717},"setIdx":0,"setId":3,"iconIdx":59},{"icon":{"paths":["M521.357 195.096l224.171 259.701v365.751c0 5.85-4.749 10.59-10.607 10.59h-136.235v-149.623c0-23.394-18.987-42.355-42.411-42.355h-85.841c-23.424 0-42.415 18.961-42.415 42.355v149.623h-136.229c-5.856 0-10.603-4.74-10.603-10.59v-365.824l224.105-259.628c4.233-4.9 11.836-4.9 16.064 0zM559.834 894.673h175.087c40.994 0 74.223-33.186 74.223-74.125v-243.883h56.154c12.437 0 23.735-7.241 28.919-18.534 5.18-11.294 3.294-24.567-4.826-33.975l-319.846-370.544c-29.611-34.302-82.829-34.302-112.435 0l-319.848 370.544c-8.122 9.408-10.007 22.682-4.825 33.975s16.48 18.534 28.918 18.534h56.214v243.883c0 40.939 33.231 74.125 74.223 74.125h175.085c1.173 0.098 2.359 0.149 3.558 0.149h85.841c1.199 0 2.385-0.051 3.558-0.149z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["home"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":431,"id":112,"name":"home","prevSize":32,"code":59718},"setIdx":0,"setId":3,"iconIdx":60},{"icon":{"paths":["M832 512c0 176.73-143.27 320-320 320-176.731 0-320-143.27-320-320h-64c0 212.079 171.923 384 384 384 212.079 0 384-171.921 384-384 0-212.077-171.921-384-384-384-123.718 0-233.772 58.508-304 149.364v-101.364c0-17.673-14.327-32-32-32s-32 14.327-32 32v192c0 17.673 14.327 32 32 32h176c17.673 0 32-14.327 32-32s-14.327-32-32-32h-107.295c57.239-86.755 155.582-144 267.295-144 176.73 0 320 143.269 320 320z","M544 320c0-17.673-14.327-32-32-32s-32 14.327-32 32v224c0 8.486 3.371 16.627 9.374 22.626l96 96c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-86.626-86.63v-210.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["history"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":432,"id":111,"name":"history","prevSize":32,"code":59719},"setIdx":0,"setId":3,"iconIdx":61},{"icon":{"paths":["M854.507 233.251c12.561 12.43 12.672 32.691 0.243 45.254l-474.877 480.001c-6.013 6.076-14.208 9.498-22.757 9.493s-16.743-3.426-22.752-9.506l-165.124-167.091c-12.423-12.57-12.303-32.828 0.268-45.252s32.832-12.305 45.254 0.269l142.376 144.068 452.113-456.993c12.429-12.564 32.691-12.672 45.257-0.243z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["check"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":433,"id":110,"name":"check","prevSize":32,"code":59720},"setIdx":0,"setId":3,"iconIdx":62},{"icon":{"paths":["M711.113 512v54.818c-0.196 76.634-43.682 143.091-107.298 176.209-5.525-25.634-28.326-44.847-55.612-44.847h-72.405c-31.42 0-56.888 25.472-56.888 56.892v41.374c0 31.415 25.469 56.887 56.888 56.887h72.405c28.471 0 52.062-20.919 56.235-48.226 69.167-26.795 123.797-82.773 148.77-152.828 4.715 1.267 9.677 1.941 14.793 1.941h28.446c31.415 0 56.887-25.468 56.887-56.887v-85.333c0-31.42-25.472-56.887-56.887-56.887h-28.446v-28.446c0-141.385-114.615-256-256-256s-256 114.615-256 256v23.275h-28.445c-31.419 0-56.889 25.468-56.889 56.887v103.433c0 31.42 25.47 56.892 56.889 56.892h28.445c31.419 0 56.889-25.472 56.889-56.892v-36.203h0.149c-0.099-2.573-0.148-5.158-0.148-7.757v-139.636c0-109.966 89.145-199.111 199.11-199.111s199.113 89.145 199.113 199.111v85.333z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["omnichannel"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":434,"id":109,"name":"omnichannel","prevSize":32,"code":59721},"setIdx":0,"setId":3,"iconIdx":63},{"icon":{"paths":["M512 554.018c66.466 0 120.346-52.493 120.346-117.239 0-64.753-53.879-117.243-120.346-117.243s-120.347 52.491-120.347 117.243c0 64.747 53.881 117.239 120.347 117.239zM512 490.018c-32.708 0-56.346-25.404-56.346-53.239 0-27.84 23.637-53.243 56.346-53.243 32.704 0 56.346 25.403 56.346 53.243 0 27.836-23.642 53.239-56.346 53.239z","M364.125 894.144h-172.125c-17.673 0-32-14.327-32-32v-702.144c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32v702.144c0 17.673-14.327 32-32 32h-172.126c-6.242 1.276-12.702 1.946-19.319 1.946h-257.109c-6.618 0-13.079-0.67-19.32-1.946zM224 830.144h68.244c-3.114-9.455-4.799-19.558-4.799-30.054v-93.449c0-56.107 37.881-105.143 92.172-119.309 19.171-5.005 39.265-5.312 58.583-0.9l42.773 9.766c20.049 4.578 40.909 4.254 60.813-0.939l28.070-7.326c20.693-5.397 42.381-5.73 63.232-0.973 60.531 13.824 103.467 67.665 103.467 129.758v83.371c0 10.496-1.685 20.599-4.8 30.054h68.245v-638.144h-576v638.144zM651.567 830.144c12.245-4.489 20.988-16.252 20.988-30.054v-83.371c0-32.235-22.289-60.186-53.713-67.362-10.825-2.47-22.084-2.3-32.828 0.503l-28.070 7.326c-29.85 7.791-61.141 8.273-91.217 1.404l-42.775-9.766c-9.29-2.121-18.955-1.971-28.175 0.435-26.112 6.814-44.331 30.396-44.331 57.382v93.449c0 13.803 8.74 25.566 20.989 30.054h279.133z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["avatar"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":435,"id":108,"name":"avatar","prevSize":32,"code":59722},"setIdx":0,"setId":3,"iconIdx":64},{"icon":{"paths":["M651.358 116.071c-57.242-19.989-105.788-20.069-138.355-20.071l-0.004 64-0.998-64c-92.005 0.342-297.345 47.824-381.242 242.996-17.897 38.968-34.758 103.342-34.758 173.004 0 69.961 17.020 146.283 52.216 207.876 23.229 40.653 92.798 131.375 191.178 173.538 94.863 40.653 206.368 52.343 296.488 16.299 16.41-6.566 24.393-25.19 17.83-41.596-6.566-16.41-25.19-24.393-41.596-17.83-69.879 27.955-163.174 20.446-247.512-15.701-80.82-34.637-141.385-112.448-160.822-146.462-28.804-50.406-43.784-115.418-43.784-176.124 0-60.766 15.020-116.183 29.055-146.59l0.184-0.4 0.174-0.404c69.507-162.182 243.825-204.605 323.586-204.605 31.599 0.002 70.882 0.296 117.261 16.493 46.144 16.113 101.389 48.779 161.822 116.767 43.657 49.114 63.535 114.976 69.389 177.712 5.892 63.121-2.85 118.187-11.541 142.093-6.404 17.6-20.429 45.44-59.392 45.7-18.261-0.806-72.823-14.673-83.123-69.568v-235.063c0-17.673-3.413-34.133-29.013-34.133-19.337 0-26.453 16.46-26.453 34.133v34.133c-35.183-39.86-95.403-68.267-152.747-68.267-106.039 0-192 85.961-192 192s85.961 192 192 192c62.178 0 117.658-29.555 152.747-75.388 25.711 71.078 102.571 93.030 137.011 94.135l0.516 0.017h0.512c82.645 0 111.714-64.806 120.085-87.829 12.642-34.761 21.675-99.695 15.125-169.907-6.592-70.597-29.38-151.402-85.278-214.288-66.906-75.265-131.076-114.598-188.561-134.67zM627.2 512c0 70.694-57.306 128-128 128s-128-57.306-128-128c0-70.694 57.306-128 128-128s128 57.306 128 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mention"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":436,"id":107,"name":"mention","prevSize":32,"code":59723},"setIdx":0,"setId":3,"iconIdx":65},{"icon":{"paths":["M370.254 544c0-17.673-14.327-32-32-32s-32 14.327-32 32v32h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v32c0 17.673 14.327 32 32 32s32-14.327 32-32v-32h32c17.673 0 31.999-14.327 31.999-32s-14.326-32-31.999-32h-32v-32z","M746.253 624c30.929 0 56-25.071 56-56s-25.071-56-56-56c-30.925 0-56 25.071-56 56s25.075 56 56 56z","M674.253 664c0 30.929-25.071 56-56 56-30.925 0-56-25.071-56-56s25.075-56 56-56c30.929 0 56 25.071 56 56z","M706.253 128c0-17.673-14.327-32-32-32s-32 14.327-32 32v96h-128c-17.673 0-32 14.327-32 32v96h-191.999c-106.039 0-192 85.961-192 192v128c0 106.039 85.961 192 192 192h447.999c106.039 0 192-85.961 192-192v-128c0-106.039-85.961-192-192-192h-192v-64h128c17.673 0 32-14.327 32-32v-128zM866.253 544v128c0 70.694-57.306 128-128 128h-447.999c-70.693 0-128-57.306-128-128v-128c0-70.694 57.307-128 128-128h447.999c70.694 0 128 57.306 128 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["game"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":437,"id":106,"name":"game","prevSize":32,"code":59724},"setIdx":0,"setId":3,"iconIdx":66},{"icon":{"paths":["M550.037 242.504c0.192-17.672 14.669-31.845 32.341-31.657s31.846 14.668 31.659 32.34l-1.318 123.488 193.438-193.438c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-193.438 193.438 123.49-1.319c17.673-0.189 32.149 13.984 32.337 31.658 0.192 17.673-13.982 32.149-31.654 32.337l-201.92 2.159c-8.606 0.090-16.887-3.285-22.972-9.374-6.084-6.084-9.459-14.362-9.37-22.967l2.155-201.921zM474.628 782.148c-0.192 17.673-14.669 31.846-32.341 31.659-17.672-0.192-31.845-14.669-31.656-32.341l1.318-123.49-193.438 193.438c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l193.438-193.438-123.488 1.318c-17.672 0.188-32.151-13.986-32.34-31.659s13.984-32.149 31.657-32.337l201.919-2.159c8.606-0.090 16.887 3.285 22.972 9.37 6.084 6.089 9.463 14.366 9.37 22.972l-2.155 201.92z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-collapse"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":438,"id":105,"name":"arrow-collapse","prevSize":32,"code":59725},"setIdx":0,"setId":3,"iconIdx":67},{"icon":{"paths":["M858.624 398.148c-0.188 17.672-14.669 31.847-32.337 31.655-17.673-0.188-31.846-14.666-31.659-32.338l1.318-123.488-193.438 193.437c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.755 0-45.254l193.442-193.439-123.49 1.319c-17.673 0.189-32.154-13.984-32.341-31.657s13.986-32.151 31.659-32.34l201.92-2.156c8.606-0.092 16.883 3.286 22.967 9.37s9.463 14.365 9.374 22.969l-2.159 201.921zM166.041 626.517c0.189-17.673 14.668-31.842 32.34-31.654s31.845 14.669 31.657 32.341l-1.319 123.486 193.438-193.438c12.497-12.497 32.759-12.497 45.257 0s12.497 32.759 0 45.257l-193.44 193.438 123.488-1.318c17.672-0.192 32.153 13.982 32.341 31.654s-13.985 32.154-31.657 32.341l-201.921 2.155c-8.605 0.094-16.884-3.285-22.969-9.37s-9.463-14.366-9.37-22.967l2.156-201.924z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-expand"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":439,"id":104,"name":"arrow-expand","prevSize":32,"code":59726},"setIdx":0,"setId":3,"iconIdx":68},{"icon":{"paths":["M586.372 672c-17.673 0-32 14.327-32 32s14.327 32 32 32h224c17.673 0 32-14.327 32-32v-208c0-17.673-14.327-32-32-32s-32 14.327-32 32v130.743l-233.374-233.371c-12.497-12.497-32.755-12.497-45.252 0l-73.374 73.371-169.373-169.371c-12.497-12.497-32.758-12.497-45.255 0s-12.497 32.758 0 45.255l192 191.999c12.497 12.497 32.756 12.497 45.254 0l73.374-73.37 210.743 210.743h-146.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-decrease"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":440,"id":103,"name":"arrow-decrease","prevSize":32,"code":59727},"setIdx":0,"setId":3,"iconIdx":69},{"icon":{"paths":["M584.115 352c-17.673 0-32-14.327-32-32s14.327-32 32-32h224c17.677 0 32 14.327 32 32v208c0 17.673-14.323 32-32 32-17.673 0-32-14.327-32-32v-130.745l-233.37 233.371c-12.497 12.497-32.759 12.497-45.257 0l-73.372-73.37-169.373 169.37c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l192-192c12.497-12.497 32.759-12.497 45.256 0l73.37 73.37 210.748-210.743h-146.748z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-increase"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":441,"id":102,"name":"arrow-increase","prevSize":32,"code":59728},"setIdx":0,"setId":3,"iconIdx":70},{"icon":{"paths":["M498.697 141.563l-298.668 136.533c-11.39 5.207-18.696 16.58-18.696 29.103v386.846c0 11.819 6.513 22.673 16.941 28.233l298.668 159.292c9.408 5.018 20.706 5.018 30.114 0l298.667-159.292c10.428-5.559 16.943-16.414 16.943-28.233v-386.846c0-12.524-7.305-23.896-18.697-29.103l-298.667-136.533c-8.448-3.862-18.159-3.862-26.607 0zM245.333 357.014l234.667 107.276v335.71l-234.667-125.154v-317.832zM544 800l234.667-125.154v-317.832l-234.667 107.276v335.71zM512 408.548l-221.699-101.348 221.699-101.348 221.7 101.348-221.7 101.348z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["apps"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":442,"id":101,"name":"apps","prevSize":32,"code":59729},"setIdx":0,"setId":3,"iconIdx":71},{"icon":{"paths":["M129.353 351.541c0-17.65 14.327-31.958 32-31.958h704c17.673 0 32 14.308 32 31.958s-14.327 31.958-32 31.958h-704c-17.673 0-32-14.308-32-31.958zM214.686 521.984c0-17.651 14.327-31.957 32-31.957h533.333c17.673 0 32 14.306 32 31.957s-14.327 31.957-32 31.957h-533.333c-17.673 0-32-14.306-32-31.957zM332.019 660.467c-17.673 0-32 14.306-32 31.957 0 17.647 14.327 31.957 32 31.957h362.667c17.673 0 32-14.31 32-31.957 0-17.651-14.327-31.957-32-31.957h-362.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["donner"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":443,"id":100,"name":"donner","prevSize":32,"code":59730},"setIdx":0,"setId":3,"iconIdx":72},{"icon":{"paths":["M192 181.333c-53.019 0-96 42.981-96 96v469.333c0 53.018 42.981 96 96 96h640c53.018 0 96-42.982 96-96v-469.333c0-53.019-42.982-96-96-96h-640zM160 277.333c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32v202.671h-704v-202.671zM160 544.004h704v202.662c0 17.673-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32v-202.662zM810.667 661.333c0-35.345-28.655-64-64-64s-64 28.655-64 64c0 35.345 28.655 64 64 64s64-28.655 64-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["card"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":444,"id":99,"name":"card","prevSize":32,"code":59731},"setIdx":0,"setId":3,"iconIdx":73},{"icon":{"paths":["M192 405.333h213.333v-213.333h-213.333v213.333zM128 170.667c0-23.564 19.103-42.667 42.667-42.667h256c23.565 0 42.667 19.103 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.564 0-42.667-19.102-42.667-42.667v-256zM618.667 405.333h213.333v-213.333h-213.333v213.333zM554.667 170.667c0-23.564 19.102-42.667 42.667-42.667h256c23.565 0 42.667 19.103 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.565 0-42.667-19.102-42.667-42.667v-256zM618.667 618.667h213.333v213.333h-213.333v-213.333zM597.333 554.667c-23.565 0-42.667 19.102-42.667 42.667v256c0 23.565 19.102 42.667 42.667 42.667h256c23.565 0 42.667-19.102 42.667-42.667v-256c0-23.565-19.102-42.667-42.667-42.667h-256zM192 832h213.333v-213.333h-213.333v213.333zM128 597.333c0-23.565 19.103-42.667 42.667-42.667h256c23.565 0 42.667 19.102 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.564 0-42.667-19.102-42.667-42.667v-256z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["squares"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":445,"id":98,"name":"squares","prevSize":32,"code":59732},"setIdx":0,"setId":3,"iconIdx":74},{"icon":{"paths":["M694.626 473.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-105.374-105.371v418.745c0 17.673-14.327 32-32 32s-32-14.327-32-32v-418.745l-105.373 105.371c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l160.001-160.001c12.497-12.497 32.755-12.497 45.252 0l160 160.001zM912 160c0-17.673-14.327-32-32-32h-768c-17.673 0-32 14.327-32 32v512c0 17.673 14.327 32 32 32h96c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64v-448h704v448h-64c-17.673 0-32 14.327-32 32s14.327 32 32 32h96c17.673 0 32-14.327 32-32v-512z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-up-box"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":446,"id":97,"name":"arrow-up-box","prevSize":32,"code":59733},"setIdx":0,"setId":3,"iconIdx":75},{"icon":{"paths":["M329.373 550.626c-12.497-12.497-12.497-32.755 0-45.252s32.758-12.497 45.255 0l105.373 105.37v-418.743c0-17.673 14.327-32 32-32s32 14.327 32 32v418.743l105.374-105.37c12.497-12.497 32.755-12.497 45.252 0s12.497 32.755 0 45.252l-160 160c-12.497 12.497-32.755 12.497-45.252 0l-160.001-160zM112 864c0 17.673 14.327 32 32 32h768c17.673 0 32-14.327 32-32v-512c0-17.673-14.327-32-32-32h-96c-17.673 0-32 14.327-32 32s14.327 32 32 32h64v448h-704v-448h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-96c-17.673 0-32 14.327-32 32v512z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down-box"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":447,"id":96,"name":"arrow-down-box","prevSize":32,"code":59734},"setIdx":0,"setId":3,"iconIdx":76},{"icon":{"paths":["M128 192c-17.673 0-32 14.327-32 32v576.021c0 17.673 14.327 32 32 32h768c17.673 0 32-14.327 32-32v-576.021c0-17.673-14.327-32-32-32h-768zM160 378.302v-122.302h149.333v122.302h-149.333zM160 442.304h149.333v141.722h-149.333v-141.722zM160 648.026h149.333v119.996h-149.333v-119.996zM373.333 768.021v-119.996h490.667v119.996h-490.667zM864 584.026h-490.667v-141.722h490.667v141.722zM864 378.302h-490.667v-122.302h490.667v122.302z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-sheet"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":448,"id":95,"name":"file-sheet","prevSize":32,"code":59735},"setIdx":0,"setId":3,"iconIdx":77},{"icon":{"paths":["M905.088 512c0-194.404-157.598-352-352-352-194.405 0-352.001 157.596-352.001 352 0 194.402 157.596 352 352.001 352 194.402 0 352-157.598 352-352zM969.088 512c0 229.751-186.249 416-416 416s-416.001-186.249-416.001-416c0-229.751 186.25-416 416.001-416s416 186.249 416 416zM441.088 383.995v256.001c0 17.673 14.323 32 32 32 17.673 0 32-14.327 32-32v-256.001c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32zM601.084 383.995v256.001c0 17.673 14.327 32 32 32s32-14.327 32-32v-256.001c0-17.673-14.327-32-32-32s-32 14.327-32 32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":449,"id":94,"name":"pause","prevSize":32,"code":59736},"setIdx":0,"setId":3,"iconIdx":78},{"icon":{"paths":["M512.674 928c229.751 0 416-186.249 416-416s-186.249-416-416-416c-229.749 0-415.998 186.249-415.998 416s186.249 416 415.998 416zM400.675 383.995c0-17.673 14.327-32 32-32s32 14.327 32 32v256.001c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256.001zM560.674 383.995c0-17.673 14.327-32 32-32s32 14.327 32 32v256.001c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause-filled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":450,"id":93,"name":"pause-filled","prevSize":32,"code":59737},"setIdx":0,"setId":3,"iconIdx":79},{"icon":{"paths":["M512.678 928.004c229.747 0 416-186.249 416-416 0-229.749-186.253-415.999-416-415.999-229.752 0-416.001 186.25-416.001 415.999 0 229.751 186.249 416 416.001 416zM452.523 357.193l195.255 136.768c14.208 9.95 18.496 30.874 9.583 46.729-2.432 4.322-5.709 7.979-9.583 10.692l-195.255 136.768c-14.204 9.95-32.95 5.163-41.866-10.692-3.036-5.397-4.646-11.644-4.646-18.018v-273.536c0-18.72 13.597-33.897 30.372-33.897 5.709 0 11.307 1.798 16.141 5.186z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play-filled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":451,"id":92,"name":"play-filled","prevSize":32,"code":59738},"setIdx":0,"setId":3,"iconIdx":80},{"icon":{"paths":["M230.487 636.693c-14.92-9.472-34.694-5.056-44.167 9.865-9.473 14.916-5.058 34.692 9.862 44.164l298.665 189.632c10.47 6.647 23.838 6.647 34.304 0l298.667-189.632c14.921-9.472 19.337-29.248 9.865-44.164-9.472-14.921-29.248-19.337-44.169-9.865l-281.515 178.739-281.513-178.739zM186.32 494.852c9.473-14.921 29.247-19.337 44.167-9.86l281.513 178.739 281.515-178.739c14.921-9.476 34.697-5.060 44.169 9.86s5.056 34.697-9.865 44.169l-298.667 189.628c-10.466 6.647-23.834 6.647-34.304 0l-298.665-189.628c-14.92-9.472-19.336-29.248-9.862-44.169zM529.152 143.663l298.667 189.63c9.25 5.871 14.848 16.062 14.848 27.015s-5.598 21.144-14.848 27.015l-298.667 189.629c-10.466 6.647-23.834 6.647-34.304 0l-298.665-189.629c-9.246-5.871-14.848-16.062-14.848-27.015s5.602-21.144 14.848-27.015l298.665-189.63c10.47-6.647 23.838-6.647 34.304 0zM273.035 360.307l238.965 151.723 238.967-151.723-238.967-151.725-238.965 151.725z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["queue"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":452,"id":91,"name":"queue","prevSize":32,"code":59739},"setIdx":0,"setId":3,"iconIdx":81},{"icon":{"paths":["M512 928c-14.217 0-28.275-0.713-42.142-2.112-16.695-1.681-30.558-12.531-36.689-27.392l-40.54-98.308-98.161 40.841c-14.837 6.174-32.3 4.045-45.292-6.554-21.839-17.818-41.831-37.811-59.65-59.648-10.601-12.992-12.726-30.455-6.553-45.295l40.839-98.159-98.307-40.542c-14.864-6.127-25.714-19.994-27.395-36.689-1.396-13.867-2.111-27.925-2.111-42.142s0.714-28.275 2.11-42.142c1.681-16.695 12.531-30.558 27.395-36.685l98.307-40.543-40.84-98.163c-6.173-14.836-4.047-32.3 6.553-45.292 17.818-21.838 37.81-41.83 59.648-59.648 12.992-10.601 30.455-12.726 45.292-6.554l98.163 40.84 40.54-98.309c6.131-14.865 19.994-25.714 36.689-27.395 13.867-1.396 27.925-2.111 42.142-2.111s28.275 0.714 42.142 2.111c16.695 1.681 30.558 12.53 36.689 27.395l40.542 98.309 98.163-40.84c14.835-6.173 32.299-4.047 45.291 6.554 21.837 17.818 41.83 37.81 59.648 59.648 10.598 12.992 12.723 30.455 6.554 45.292l-40.841 98.163 98.308 40.543c14.861 6.127 25.711 19.994 27.392 36.685 1.399 13.867 2.112 27.925 2.112 42.142s-0.713 28.275-2.112 42.142c-1.681 16.695-12.531 30.562-27.392 36.689l-98.308 40.542 40.836 98.159c6.174 14.839 4.049 32.303-6.549 45.295-17.822 21.837-37.811 41.83-59.652 59.648-12.992 10.598-30.455 12.727-45.291 6.554l-98.159-40.841-40.542 98.308c-6.131 14.861-19.994 25.711-36.689 27.392-13.867 1.399-27.925 2.112-42.142 2.112zM444.454 757.982l43.383 105.203c7.979 0.542 16.034 0.815 24.162 0.815s16.183-0.273 24.162-0.815l43.383-105.203c9.455-22.921 35.733-33.805 58.628-24.282l105.054 43.708c12.156-10.598 23.578-22.020 34.18-34.176l-43.708-105.058c-9.527-22.895 1.357-49.173 24.282-58.624l105.203-43.383c0.542-7.983 0.815-16.038 0.815-24.166 0-8.124-0.273-16.183-0.815-24.162l-105.203-43.383c-22.925-9.455-33.809-35.731-24.282-58.625l43.708-105.058c-10.603-12.156-22.020-23.577-34.176-34.177l-105.058 43.709c-22.895 9.525-49.173-1.359-58.628-24.283l-43.383-105.204c-7.979-0.54-16.038-0.815-24.162-0.815s-16.183 0.275-24.162 0.815l-43.383 105.204c-9.455 22.924-35.732 33.809-58.627 24.283l-105.058-43.709c-12.156 10.6-23.577 22.022-34.177 34.177l43.709 105.058c9.525 22.895-1.359 49.17-24.283 58.625l-105.203 43.383c-0.54 7.979-0.815 16.038-0.815 24.162 0 8.128 0.275 16.183 0.815 24.166l105.202 43.383c22.924 9.451 33.809 35.729 24.283 58.624l-43.708 105.058c10.601 12.156 22.022 23.578 34.179 34.176l105.056-43.708c22.895-9.523 49.172 1.361 58.627 24.282zM416 512c0-53.018 42.982-96 96-96s96 42.982 96 96c0 53.018-42.982 96-96 96s-96-42.982-96-96zM512 352c-88.366 0-160 71.634-160 160 0 88.367 71.634 160 160 160 88.367 0 160-71.633 160-160 0-88.366-71.633-160-160-160z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["administration"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":453,"id":90,"name":"administration","prevSize":32,"code":59740},"setIdx":0,"setId":3,"iconIdx":82},{"icon":{"paths":["M561.668 127.99c-121.698 0-200.973 50.509-248.815 115.801-46.216 63.073-61.638 137.752-61.933 188.615-8.705 98.748 28.601 172.898 89.423 223.471 59.685 49.626 140.202 75.26 217.847 83.738 77.751 8.486 161.715 1.195 227.857-12.548 33.067-6.869 62.775-15.578 85.641-25.331 11.366-4.847 22.029-10.368 30.797-16.67 7.876-5.662 18.543-14.976 23.245-28.834 7.283-21.474-3.136-38.784-11.968-48.102-8.41-8.879-19.426-15.403-28.237-20.015-9.306-4.868-19.379-9.207-27.533-12.71-8.977-3.853-14.861-6.396-18.56-8.431-4.847-2.662-8.845-5.086-12.028-7.202 20.254-47.142 31.548-77.043 37.696-99.191 7.292-26.27 7.313-41.723 7.313-58.907 0-21.523-9.6-88.542-52.809-151.109-44.702-64.73-124.070-122.573-257.937-122.573zM314.916 433.894c0-40.414 12.606-101.841 49.562-152.276 35.783-48.835 95.882-89.628 197.19-89.628 112.085 0 172.087 46.886 205.274 94.94 34.679 50.219 41.472 104.040 41.472 114.741v0.2c0.004 14.918 0.004 23.638-4.979 41.594-5.491 19.776-16.964 50.189-41.54 106.534-5.798 13.295-2.761 25.771 1.156 33.754 3.665 7.479 9.003 13.244 13.329 17.263 8.836 8.213 20.655 15.684 32.226 22.046 6.618 3.635 15.236 7.334 22.562 10.475l1.562 0.67c5.905 2.539 11.52 4.971 16.687 7.42-0.909 0.41-1.856 0.823-2.833 1.237-17.741 7.569-43.081 15.206-73.557 21.542-60.945 12.663-138.065 19.209-207.885 11.584-69.926-7.633-136.986-30.336-183.881-69.325-45.46-37.798-73.674-92.066-66.481-169.822l0.136-1.472v-1.476zM819.162 553.348l-0.073-0.081c0 0 0.009 0.009 0.026 0.034 0.013 0.013 0.026 0.030 0.047 0.047z","M178.552 502.468c7.496-11.255 16.26-22.259 26.436-32.589 0.876 31.744 6.169 61.227 15.216 88.358-15.094 30.878-18.374 59.315-18.374 65.357v0.188c-0 11.379-0 17.532 3.523 30.699 3.993 14.916 12.441 38.212 30.867 82.022 5.256 12.497 2.48 24.098-0.985 31.428-3.249 6.874-7.925 12.058-11.511 15.514-7.319 7.057-16.852 13.257-25.751 18.33-5.008 2.854-11.333 5.705-16.546 8.026 11.182 3.942 24.979 7.817 40.803 11.226 44.976 9.694 101.833 14.673 153.062 8.87 51.281-5.807 99.779-23.014 133.353-51.964l0.606-0.525c14.793 2.782 29.53 4.937 44.053 6.519 10.88 1.19 21.858 2.091 32.875 2.722-10.121 14.797-22.165 28.045-35.742 39.753-46.362 39.974-108.544 60.365-167.945 67.089-59.451 6.729-123.406 0.947-173.745-9.899-25.169-5.427-48.056-12.352-65.902-20.245-8.86-3.917-17.457-8.503-24.686-13.892-6.431-4.791-15.831-13.171-20.001-25.92-6.422-19.631 2.792-35.443 10.458-43.831 7.193-7.872 16.405-13.461 23.247-17.173 7.309-3.968 15.158-7.467 21.235-10.176 6.906-3.076 10.854-4.855 13.183-6.182 1.35-0.768 2.59-1.502 3.727-2.197-13.937-33.886-21.975-56.119-26.479-72.947-5.68-21.222-5.7-33.873-5.7-47.433 0-17.711 7.436-71.138 40.721-121.126zM155.337 797.244c-0.012 0.004 0.084 0.107 0.317 0.303-0.19-0.205-0.306-0.303-0.317-0.303zM179.587 737.084c-0.003 0-0.052 0.051-0.137 0.149l0.112-0.119c0.019-0.021 0.027-0.030 0.025-0.030z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["discussions"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":454,"id":89,"name":"discussions","prevSize":32,"code":59741},"setIdx":0,"setId":3,"iconIdx":83},{"icon":{"paths":["M223.311 308.609c-47.831 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-9.348 103.236 27.547 175.996 86.848 226.377 60.643 51.516 146.604 80.998 235.291 90.893 88.61 9.886 186.223 1.399 263.339-14.976 38.562-8.188 70.929-18.121 93.888-28.126 8.909-3.883 15.838-7.535 20.945-10.765-2.377-1.515-5.248-3.196-8.678-5.030-8.883-4.749-19.055-9.263-29.611-13.897l-1.843-0.806c-9.314-4.083-19.725-8.649-27.61-13.077-14.293-8.030-28.318-17.156-38.545-26.867-5.035-4.783-10.761-11.196-14.596-19.183-4.058-8.457-6.959-21.082-1.259-34.436 30.814-72.188 45.457-111.727 52.548-137.835 6.622-24.38 6.622-36.655 6.622-56.171v-0.179c0-15.514-8.977-86.595-53.777-152.876-43.371-64.175-121.395-125.729-264.828-125.729-129.727 0-207.831 53.576-254.531 118.696zM173.267 272.433c58.132-81.062 154.749-144.433 304.574-144.433 164.894 0 261.594 72.589 315.857 152.878 52.838 78.182 64.414 161.879 64.414 187.641 0 21.658-0.017 40.337-8.794 72.64-7.834 28.834-22.626 68.617-50.048 133.423 5.069 4.036 12.591 9.003 22.507 14.571 5.12 2.876 12.992 6.34 24.055 11.191 10.261 4.51 22.579 9.933 33.89 15.979 10.846 5.798 23.526 13.572 32.951 23.74 9.86 10.633 20.211 28.868 12.817 51.136-4.873 14.673-16.183 25.092-25.609 32.017-10.24 7.522-22.946 14.281-36.86 20.343-27.947 12.177-64.585 23.181-105.681 31.906-82.197 17.451-186.522 26.688-282.906 15.936-96.311-10.743-195.347-43.174-268.314-105.165-74.031-62.891-119.295-154.778-108.551-277.854 0.276-63.411 19.118-157.053 75.696-235.949zM333.953 422.052c0-17.648 14.25-31.955 31.828-31.955h183.011c17.574 0 31.825 14.307 31.825 31.955s-14.251 31.955-31.825 31.955h-183.011c-17.578 0-31.828-14.306-31.828-31.955zM365.781 539.708c-17.578 0-31.828 14.306-31.828 31.957 0 17.647 14.25 31.953 31.828 31.953h224.12c17.579 0 31.825-14.306 31.825-31.953 0-17.651-14.246-31.957-31.825-31.957h-224.12z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["threads"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":455,"id":88,"name":"threads","prevSize":32,"code":59742},"setIdx":0,"setId":3,"iconIdx":84},{"icon":{"paths":["M877.402 153.128c-12.497-12.497-32.759-12.497-45.257 0l-668.244 668.244c-12.497 12.497-12.497 32.759 0 45.257s32.758 12.497 45.255 0l668.246-668.246c12.497-12.497 12.497-32.758 0-45.255zM309.94 170.672c-23.564 0-42.667 14.327-42.667 32s19.103 32 42.667 32h255.999c23.565 0 42.667-14.327 42.667-32s-19.102-32-42.667-32h-255.999zM585.446 309.329h-403.506c-41.237 0-74.667 33.429-74.667 74.667v384c0 6.012 0.71 11.853 2.051 17.455l61.949-61.952v-339.503c0-5.891 4.776-10.667 10.667-10.667h339.506l64-64zM387.612 778.662h306.327c5.892 0 10.667-4.774 10.667-10.667v-85.333c0-11.375 6.037-21.897 15.859-27.631 9.822-5.739 21.952-5.833 31.859-0.243l97.83 55.164c2.53 1.425 4.855 3.191 6.908 5.244 6.72 6.72 18.21 1.963 18.21-7.539v-263.322c0-9.502-11.49-14.263-18.206-7.543-2.057 2.057-4.382 3.819-6.912 5.248l-97.83 55.164c-9.907 5.585-22.037 5.495-31.859-0.243s-15.859-16.256-15.859-27.631v-7.663l110.78-73.495c47.42-41.99 123.887-8.7 123.887 56.164v263.322c0 64.862-76.467 98.155-123.887 56.162l-46.78-26.377v30.554c0 41.237-33.429 74.667-74.667 74.667h-370.327l64-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":456,"id":87,"name":"camera-disabled","prevSize":32,"code":59743},"setIdx":0,"setId":3,"iconIdx":85},{"icon":{"paths":["M312.195 170.672c-23.564 0-42.667 14.327-42.667 32s19.102 32 42.667 32h256.001c23.565 0 42.667-14.327 42.667-32s-19.102-32-42.667-32h-256.001z","M559.241 503.454c11.268-11.268 11.191-29.611-0.171-40.973-11.362-11.358-29.705-11.435-40.969-0.171l-71.403 71.403-71.997-71.996c-11.36-11.362-29.704-11.435-40.971-0.171-11.267 11.268-11.191 29.611 0.17 40.973l71.997 71.996-71.402 71.403c-11.267 11.264-11.191 29.606 0.17 40.969s29.704 11.439 40.971 0.171l71.404-71.403 71.996 71.996c11.358 11.362 29.705 11.439 40.969 0.171 11.268-11.264 11.191-29.611-0.171-40.969l-71.996-71.996 71.403-71.403z","M109.529 383.995c0-41.237 33.429-74.667 74.667-74.667h512.001c41.237 0 74.667 33.429 74.667 74.667v30.553l46.78-26.377c47.415-41.99 123.887-8.7 123.887 56.164v263.322c0 64.862-76.471 98.155-123.887 56.162l-46.78-26.377v30.554c0 41.237-33.429 74.667-74.667 74.667h-512.001c-41.237 0-74.667-33.429-74.667-74.667v-384.001zM184.195 373.328c-5.891 0-10.667 4.776-10.667 10.667v384.001c0 5.888 4.776 10.667 10.667 10.667h512.001c5.888 0 10.667-4.779 10.667-10.667v-85.333c0-11.375 6.037-21.897 15.859-27.631 9.822-5.739 21.948-5.833 31.859-0.243l97.826 55.164c2.534 1.425 4.855 3.191 6.912 5.244 6.72 6.72 18.21 1.963 18.21-7.539v-263.322c0-9.502-11.49-14.263-18.21-7.543-2.052 2.057-4.378 3.819-6.912 5.248l-97.826 55.164c-9.911 5.585-22.037 5.491-31.859-0.243-9.822-5.739-15.859-16.256-15.859-27.631v-85.334c0-5.891-4.779-10.667-10.667-10.667h-512.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-unavailable"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":457,"id":86,"name":"camera-unavailable","prevSize":32,"code":59744},"setIdx":0,"setId":3,"iconIdx":86},{"icon":{"paths":["M265.018 202.672c0-17.673 19.102-32 42.667-32h256.001c23.565 0 42.667 14.327 42.667 32s-19.102 32-42.667 32h-256.001c-23.564 0-42.667-14.327-42.667-32zM169.018 383.995c0-5.891 4.776-10.667 10.667-10.667h512.001c5.888 0 10.667 4.776 10.667 10.667v85.334c0 11.375 6.037 21.892 15.859 27.631 9.822 5.734 21.948 5.828 31.855 0.243l97.83-55.164c2.534-1.429 4.855-3.191 6.912-5.248 6.72-6.72 18.21-1.958 18.21 7.543v263.322c0 9.502-11.49 14.259-18.21 7.539-2.057-2.052-4.378-3.819-6.912-5.244l-97.83-55.164c-9.907-5.589-22.033-5.495-31.855 0.243-9.822 5.734-15.859 16.256-15.859 27.631v85.333c0 5.888-4.779 10.667-10.667 10.667h-512.001c-5.891 0-10.667-4.779-10.667-10.667v-384.001zM179.685 309.328c-41.237 0-74.667 33.429-74.667 74.667v384.001c0 41.237 33.43 74.667 74.667 74.667h512.001c41.237 0 74.667-33.429 74.667-74.667v-30.554l46.775 26.377c47.42 41.993 123.891 8.7 123.891-56.162v-263.322c0-64.864-76.471-98.155-123.891-56.164l-46.775 26.377v-30.553c0-41.237-33.429-74.667-74.667-74.667h-512.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":458,"id":85,"name":"camera","prevSize":32,"code":59745},"setIdx":0,"setId":3,"iconIdx":87},{"icon":{"paths":["M317.754 661.342h-118.99v-298.665h118.99l12.367-48.047c11.845-46.020 53.696-79.953 103.308-79.953h64v554.665h-64c-49.613 0-91.464-33.933-103.308-79.949l-12.367-48.051zM177.431 725.342h90.71c18.946 73.613 85.766 128 165.288 128h85.333c23.565 0 42.667-19.102 42.667-42.667v-597.332c0-23.564-19.102-42.667-42.667-42.667h-85.333c-79.522 0-146.342 54.391-165.288 128h-90.71c-23.564 0-42.667 19.103-42.667 42.667v341.332c0 23.565 19.103 42.667 42.667 42.667zM893.393 393.373c12.497 12.497 12.497 32.758 0 45.254l-73.374 73.374 73.374 73.374c12.497 12.497 12.497 32.755 0 45.252s-32.759 12.497-45.257 0l-73.374-73.37-73.37 73.37c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.755 0-45.252l73.374-73.374-73.374-73.374c-12.497-12.495-12.497-32.757 0-45.254s32.759-12.497 45.257 0l73.37 73.371 73.374-73.371c12.497-12.497 32.759-12.497 45.257 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio-unavailable"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":459,"id":84,"name":"audio-unavailable","prevSize":32,"code":59746},"setIdx":0,"setId":3,"iconIdx":88},{"icon":{"paths":["M870.639 153.127c-12.497-12.497-32.759-12.497-45.257 0l-668.244 668.244c-12.497 12.497-12.497 32.759 0 45.257s32.758 12.497 45.255 0l668.246-668.246c12.497-12.497 12.497-32.758 0-45.255zM679.155 480.358l55.919-55.923c16.256 79.564 14.515 161.864-5.231 240.844l-11.622 46.485c-4.284 17.148-21.658 27.571-38.805 23.283-17.143-4.284-27.567-21.658-23.283-38.805l11.622-46.485c13.875-55.509 17.677-112.875 11.401-169.399zM821.675 385.922l-11.315-36.77 51.204-51.206 21.278 69.154c33.344 108.369 31.996 224.448-3.857 332.010l-33.451 100.343c-5.589 16.764-23.71 25.826-40.474 20.237-16.768-5.589-25.83-23.71-20.241-40.478l33.446-100.339c31.637-94.912 32.828-197.333 3.409-292.952zM495.177 664.333l64-64v210.342c0 23.565-19.102 42.667-42.667 42.667h-85.333c-35.46 0-68.394-10.812-95.684-29.325l46.611-46.609c14.701 7.629 31.395 11.934 49.073 11.934h64v-125.009zM132.51 682.675c0 19.742 13.403 36.348 31.604 41.22l62.552-62.554h-30.156v-298.665h118.991l12.367-48.047c11.845-46.020 53.695-79.953 103.309-79.953h64v158.156l64-64v-115.49c0-23.564-19.102-42.667-42.667-42.667h-85.333c-79.523 0-146.343 54.391-165.289 128h-90.71c-23.564 0-42.667 19.103-42.667 42.667v341.332z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":460,"id":83,"name":"audio-disabled","prevSize":32,"code":59747},"setIdx":0,"setId":3,"iconIdx":89},{"icon":{"paths":["M300.099 631.121h-113.433v-280.89h113.433l12.367-48.048c11.055-42.953 50.122-74.619 96.423-74.619h58.665v526.223h-58.665c-46.301 0-85.367-31.667-96.423-74.62l-12.367-48.047zM163.555 695.121h86.931c18.156 70.541 82.193 122.667 158.403 122.667h81.778c22.583 0 40.887-18.308 40.887-40.892v-572.443c0-22.582-18.304-40.889-40.887-40.889h-81.778c-76.21 0-140.246 52.124-158.403 122.667h-86.931c-22.582 0-40.889 18.307-40.889 40.889v327.11c0 22.583 18.307 40.892 40.889 40.892zM646.46 316.516c17.148-4.286 34.522 6.138 38.805 23.284l11.14 44.551c20.804 83.229 20.804 170.303 0 253.529l-11.14 44.553c-4.284 17.148-21.658 27.571-38.805 23.283-17.143-4.284-27.571-21.658-23.283-38.805l11.14-44.553c18.257-73.037 18.257-149.444 0-222.484l-11.14-44.551c-4.288-17.146 6.14-34.519 23.283-38.806zM807.475 235.938c-5.201-16.892-23.108-26.372-39.996-21.175-16.892 5.198-26.372 23.104-21.175 39.996l35.533 115.49c28.117 91.37 26.978 189.239-3.251 279.931l-32.055 96.158c-5.589 16.768 3.473 34.889 20.237 40.478 16.768 5.589 34.889-3.473 40.478-20.237l32.055-96.162c34.449-103.343 35.746-214.869 3.708-318.99l-35.533-115.49z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":461,"id":82,"name":"audio","prevSize":32,"code":59748},"setIdx":0,"setId":3,"iconIdx":90},{"icon":{"paths":["M439.275 704.371c0-33.643 26.278-47.676 72.725-47.676s72.73 14.033 72.73 47.676c0 32.96-13.193 111.070-23.915 149.764-4.962 17.805-22.076 25.169-48.815 25.169-26.735 0-43.853-7.364-48.815-25.173-10.714-38.665-23.91-116.689-23.91-149.76zM616.781 870.374l0.021-0.094c6.178-22.289 12.57-53.862 17.387-83.447 4.685-28.791 8.721-60.71 8.721-82.462 0-35.891-15.812-67.802-46.797-87.113-26.035-16.226-57.152-19.925-84.113-19.925-26.957 0-58.078 3.699-84.113 19.925-30.982 19.311-46.795 51.221-46.795 87.113 0 21.807 4.036 53.73 8.726 82.517 4.819 29.577 11.21 61.12 17.381 83.392l0.024 0.085c7.401 26.556 24.983 45.666 46.871 56.469 19.473 9.609 40.427 11.831 57.907 11.831 17.485 0 38.434-2.223 57.907-11.831 21.888-10.799 39.471-29.905 46.874-56.461zM459.635 460.8c0-28.275 23.445-51.2 52.365-51.2s52.365 22.925 52.365 51.2c0 28.275-23.445 51.2-52.365 51.2s-52.365-22.925-52.365-51.2zM512 341.333c-67.477 0-122.182 53.487-122.182 119.467s54.704 119.467 122.182 119.467c67.477 0 122.18-53.487 122.18-119.467s-54.703-119.467-122.18-119.467zM677.803 537.254c-6.665 13.79-3.883 31.108 6.276 42.573 14.084 15.893 39.851 17.937 50.206-0.606 19.639-35.17 30.805-75.52 30.805-118.421 0-136.672-113.314-247.467-253.090-247.467-139.779 0-253.092 110.795-253.092 247.467 0 42.901 11.164 83.251 30.806 118.421 10.355 18.543 36.119 16.499 50.205 0.606 10.16-11.465 12.939-28.783 6.275-42.573-11.203-23.189-17.469-49.105-17.469-76.454 0-98.97 82.054-179.2 183.274-179.2 101.218 0 183.27 80.23 183.27 179.2 0 27.349-6.263 53.265-17.468 76.454zM730.159 699.793c-0.303-10.658 3.546-21.069 10.931-28.762 52.77-54.955 85.090-128.9 85.090-210.231 0-169.662-140.663-307.2-314.18-307.2s-314.182 137.538-314.182 307.2c0 81.331 32.323 155.277 85.094 210.231 7.386 7.693 11.234 18.103 10.928 28.762-0.832 29.018-31.273 47.915-52.121 27.716-70.223-68.041-113.718-162.406-113.718-266.709 0-207.365 171.923-375.467 384-375.467 212.079 0 384 168.102 384 375.467 0 104.303-43.494 198.673-113.719 266.709-20.851 20.203-51.29 1.306-52.122-27.716z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["live-streaming"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":462,"id":81,"name":"live-streaming","prevSize":32,"code":59749},"setIdx":0,"setId":3,"iconIdx":91},{"icon":{"paths":["M680.55 256c0-17.673-14.327-32-32-32s-32 14.327-32 32v56.876l-80.346 13.999c-17.412 3.034-29.069 19.607-26.035 37.018s19.61 29.066 37.022 26.032l69.359-12.085v69.371c-30.383 6.852-60.459 20.075-84.151 43.14-27.925 27.183-44.075 65.242-44.075 113.809 0 25.741 6.263 48.26 18.453 66.62 12.186 18.351 29.065 30.682 47.253 37.999 35.354 14.225 76.719 10.176 108.126-4.578l1.207-0.567c31.125-14.618 62.494-29.35 90.722-57.818 21.325-21.504 39.71-49.536 56.849-88.674 5.653 9.498 9.591 20.966 10.287 34.338 1.63 31.526-14.281 82.812-87.957 153.417-12.762 12.228-13.193 32.486-0.964 45.244 12.228 12.762 32.486 13.193 45.244 0.964 80.196-76.851 110.588-145.033 107.593-202.935-2.987-57.655-38.588-96.021-70.677-113.954-20.774-13.013-50.214-22.827-81.216-28.288-16.623-2.931-34.47-4.749-52.693-4.996v-74.242l101.717-17.723c17.412-3.034 29.069-19.607 26.035-37.018s-19.61-29.066-37.018-26.032l-90.735 15.809v-45.724zM577.041 536.209c10.163-9.894 23.565-17.476 39.509-22.703v138.65c-13.444 2.547-27.486 1.732-38.635-2.752-7.71-3.102-13.658-7.757-17.822-14.029-4.156-6.259-7.77-15.996-7.77-31.215 0-33.348 10.564-54.174 24.717-67.951zM708.642 600.751c-8.683 8.755-17.783 15.706-28.092 22.076v-117.888c14.127 0.239 28.224 1.66 41.587 4.015 13.589 2.394 25.681 5.623 35.763 9.165-16.879 42.039-33.067 66.304-49.259 82.633zM246.351 565.333h98.385l-49.193-184.882-49.193 184.882zM389.497 733.559l-27.732-104.226h-132.443l-27.732 104.226c-4.544 17.079-22.073 27.243-39.152 22.699s-27.241-22.076-22.696-39.151l106.323-399.601c13.494-50.714 85.464-50.713 98.957 0l106.323 399.601c4.544 17.075-5.615 34.607-22.694 39.151s-34.609-5.619-39.153-22.699z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["language"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":463,"id":80,"name":"language","prevSize":32,"code":59750},"setIdx":0,"setId":3,"iconIdx":92},{"icon":{"paths":["M224 224h320v576h-96v-112c0-8.836-7.164-16-16-16h-96c-8.837 0-16 7.164-16 16v112h-96v-576zM608 448h192v352h-192v-352zM832 384h-224v-192c0-17.673-14.327-32-32-32h-384c-17.673 0-32 14.327-32 32v640c0 17.673 14.327 32 32 32h640c17.673 0 32-14.327 32-32v-416c0-17.673-14.327-32-32-32zM304 288c-8.837 0-16 7.163-16 16v32c0 8.837 7.163 16 16 16h32c8.837 0 16-7.163 16-16v-32c0-8.837-7.163-16-16-16h-32zM288 432c0-8.837 7.163-16 16-16h32c8.837 0 16 7.163 16 16v32c0 8.836-7.163 16-16 16h-32c-8.837 0-16-7.164-16-16v-32zM304 544c-8.837 0-16 7.164-16 16v32c0 8.836 7.163 16 16 16h32c8.837 0 16-7.164 16-16v-32c0-8.836-7.163-16-16-16h-32zM416 304c0-8.837 7.163-16 16-16h32c8.836 0 16 7.163 16 16v32c0 8.837-7.164 16-16 16h-32c-8.837 0-16-7.163-16-16v-32zM432 416c-8.837 0-16 7.163-16 16v32c0 8.836 7.163 16 16 16h32c8.836 0 16-7.164 16-16v-32c0-8.837-7.164-16-16-16h-32zM416 560c0-8.836 7.163-16 16-16h32c8.836 0 16 7.164 16 16v32c0 8.836-7.164 16-16 16h-32c-8.837 0-16-7.164-16-16v-32zM688 512c-8.836 0-16 7.164-16 16v32c0 8.836 7.164 16 16 16h32c8.836 0 16-7.164 16-16v-32c0-8.836-7.164-16-16-16h-32zM672 656c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v32c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16v-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["business"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":464,"id":79,"name":"business","prevSize":32,"code":59751},"setIdx":0,"setId":3,"iconIdx":93},{"icon":{"paths":["M714.667 298.663c0-17.673-14.327-32-32-32s-32 14.327-32 32v426.666c0 17.673 14.327 32 32 32s32-14.327 32-32v-426.666z","M512 394.671c17.673 0 32 14.327 32 32v298.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-298.667c0-17.673 14.327-32 32-32z","M373.333 554.671c0-17.673-14.327-32-32-32s-32 14.327-32 32v170.667c0 17.677 14.327 32 32 32s32-14.323 32-32v-170.667z","M128 199.555v624.888c0 39.518 32.037 71.556 71.555 71.556h624.888c39.518 0 71.556-32.038 71.556-71.556v-624.888c0-39.519-32.038-71.555-71.556-71.555h-624.888c-39.519 0-71.555 32.037-71.555 71.555zM199.555 192h624.888c4.173 0 7.556 3.383 7.556 7.555v624.888c0 4.173-3.383 7.556-7.556 7.556h-624.888c-4.173 0-7.555-3.383-7.555-7.556v-624.888c0-4.173 3.383-7.555 7.555-7.555z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["dashboard"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":465,"id":78,"name":"dashboard","prevSize":32,"code":59752},"setIdx":0,"setId":3,"iconIdx":94},{"icon":{"paths":["M160 256c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32zM160 421.161c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32.001-32 32.001h-640c-17.673 0-32-14.327-32-32.001zM160 602.837c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32zM160 768c0-17.673 14.327-32 32-32h344.614c17.673 0 32 14.327 32 32s-14.327 32-32 32h-344.614c-17.673 0-32-14.327-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["document"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":466,"id":77,"name":"document","prevSize":32,"code":59753},"setIdx":0,"setId":3,"iconIdx":95},{"icon":{"paths":["M421.334 256c0-44.183-35.817-80-80-80s-80 35.817-80 80c0 44.183 35.817 80 80 80s80-35.817 80-80zM465.301 288c-14.208 55.207-64.324 96-123.967 96s-109.758-40.793-123.968-96h-89.367c-17.673 0-32-14.327-32-32s14.327-32 32-32h89.367c14.209-55.207 64.325-96 123.968-96s109.759 40.793 123.967 96h430.699c17.673 0 32 14.327 32 32s-14.327 32-32 32h-430.699zM96 768c0-17.673 14.327-32 32-32h89.367c14.209-55.206 64.325-96 123.968-96 60.782 0 111.671 42.368 124.744 99.179 4.207-2.035 8.93-3.179 13.922-3.179h416c17.673 0 32 14.327 32 32s-14.327 32-32 32h-416c-4.992 0-9.715-1.143-13.922-3.179-13.073 56.811-63.962 99.179-124.744 99.179-59.643 0-109.758-40.794-123.968-96h-89.367c-17.673 0-32-14.327-32-32zM341.334 848c44.183 0 80-35.819 80-80s-35.817-80-80-80c-44.183 0-80 35.819-80 80s35.817 80 80 80zM796.032 543.757c-14.127 55.33-64.303 96.243-124.032 96.243s-109.905-40.913-124.028-96.243c-1.301 0.162-2.628 0.243-3.972 0.243h-416c-17.673 0-32-14.327-32-32s14.327-32 32-32h416c1.344 0 2.671 0.081 3.972 0.243 14.123-55.331 64.299-96.243 124.028-96.243s109.905 40.913 124.032 96.243c1.297-0.162 2.624-0.243 3.968-0.243h96c17.673 0 32 14.327 32 32s-14.327 32-32 32h-96c-1.344 0-2.671-0.081-3.968-0.243zM752 512c0-44.181-35.819-80-80-80s-80 35.819-80 80c0 44.181 35.819 80 80 80s80-35.819 80-80z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["settings"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":467,"id":76,"name":"settings","prevSize":32,"code":59754},"setIdx":0,"setId":3,"iconIdx":96},{"icon":{"paths":["M464 272c-35.345 0-64 28.654-64 64v480c0 35.345 28.655 64 64 64h352c35.345 0 64-28.655 64-64v-304c0-6.511-1.984-12.864-5.692-18.214l-134.455-194.215c-11.955-17.267-31.616-27.57-52.621-27.57h-223.232zM464 336h144v144c0 35.345 28.655 64 64 64h144v272h-352v-480zM672 480v-144h15.232l99.695 144h-114.927zM144 208c0-35.346 28.654-64 64-64h241.843c18.031 0 35.226 7.607 47.356 20.949l39.138 43.051h-328.337v480h128v64h-128c-35.346 0-64-28.655-64-64v-480z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["copy"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":468,"id":75,"name":"copy","prevSize":32,"code":59755},"setIdx":0,"setId":3,"iconIdx":97},{"icon":{"paths":["M825.135 560.657l-84.617-11.038c1.237-9.481 1.882-19.187 1.882-29.086s-0.644-19.605-1.882-29.086l84.617-11.038c1.715 13.133 2.598 26.526 2.598 40.124s-0.883 26.991-2.598 40.124zM804.425 402.941l-78.827 32.686c-7.458-17.989-17.242-34.822-28.996-50.116l67.665-51.996c16.243 21.139 29.798 44.451 40.158 69.426zM707.55 276.801l-51.994 67.663c-15.296-11.753-32.128-21.534-50.116-28.995l32.687-78.825c24.973 10.356 48.286 23.913 69.423 40.157zM560.657 215.93l-11.038 84.617c-9.481-1.237-19.187-1.88-29.086-1.88s-19.605 0.643-29.086 1.88l-11.038-84.617c13.133-1.713 26.526-2.597 40.124-2.597s26.991 0.884 40.124 2.597zM402.941 236.644l32.686 78.825c-17.989 7.461-34.822 17.242-50.116 28.995l-51.996-67.663c21.139-16.244 44.451-29.801 69.426-40.157zM276.801 333.515l67.663 51.996c-11.753 15.294-21.534 32.126-28.995 50.116l-78.825-32.686c10.356-24.975 23.913-48.287 40.157-69.426zM215.93 480.41c-1.713 13.133-2.597 26.526-2.597 40.124s0.884 26.991 2.597 40.124l84.617-11.038c-1.237-9.481-1.88-19.187-1.88-29.086s0.643-19.605 1.88-29.086l-84.617-11.038zM236.644 638.127l78.825-32.687c7.461 17.988 17.242 34.82 28.995 50.116l-67.663 51.994c-16.244-21.137-29.801-44.45-40.157-69.423zM333.515 764.267l51.996-67.665c15.294 11.755 32.126 21.538 50.116 28.996l-32.686 78.827c-24.975-10.359-48.287-23.915-69.426-40.158zM480.41 825.135l11.038-84.617c9.481 1.237 19.187 1.882 29.086 1.882s19.605-0.644 29.086-1.882l11.038 84.617c-13.133 1.715-26.526 2.598-40.124 2.598s-26.991-0.883-40.124-2.598zM638.127 804.425l-32.687-78.827c17.988-7.458 34.82-17.242 50.116-28.996l51.994 67.665c-21.137 16.243-44.45 29.798-69.423 40.158zM764.267 707.55l-67.665-51.994c11.755-15.296 21.538-32.128 28.996-50.116l78.827 32.687c-10.359 24.973-23.915 48.286-40.158 69.423z"],"attrs":[{"fill":"rgb(158, 162, 168)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-loading"]},"attrs":[{"fill":"rgb(158, 162, 168)"}],"properties":{"order":469,"id":74,"name":"status-loading","prevSize":32,"code":59756},"setIdx":0,"setId":3,"iconIdx":98},{"icon":{"paths":["M520.533 742.4c-122.534 0-221.867-99.332-221.867-221.867s99.333-221.867 221.867-221.867c122.534 0 221.867 99.333 221.867 221.867s-99.332 221.867-221.867 221.867zM520.533 827.733c169.66 0 307.2-137.54 307.2-307.2 0-169.662-137.54-307.2-307.2-307.2-169.662 0-307.2 137.538-307.2 307.2 0 169.66 137.538 307.2 307.2 307.2z"],"attrs":[{"fill":"rgb(158, 162, 168)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-invisible"]},"attrs":[{"fill":"rgb(158, 162, 168)"}],"properties":{"order":470,"id":73,"name":"status-invisible","prevSize":32,"code":59757},"setIdx":0,"setId":3,"iconIdx":99},{"icon":{"paths":["M520.533 827.733c-169.662 0-307.2-137.54-307.2-307.2 0-169.662 137.538-307.2 307.2-307.2 169.66 0 307.2 137.538 307.2 307.2 0 169.66-137.54 307.2-307.2 307.2zM563.2 341.333c0-23.564-19.102-42.667-42.667-42.667s-42.667 19.103-42.667 42.667v199.706l144.013 115.213c18.402 14.72 45.252 11.738 59.972-6.665s11.738-45.252-6.665-59.972l-111.987-89.587v-158.694z"],"attrs":[{"fill":"rgb(243, 190, 8)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-away"]},"attrs":[{"fill":"rgb(243, 190, 8)"}],"properties":{"order":471,"id":72,"name":"status-away","prevSize":32,"code":59758},"setIdx":0,"setId":3,"iconIdx":100},{"icon":{"paths":["M520.533 827.733c169.66 0 307.2-137.54 307.2-307.2 0-169.662-137.54-307.2-307.2-307.2-169.662 0-307.2 137.538-307.2 307.2 0 169.66 137.538 307.2 307.2 307.2zM418.133 477.867c-23.564 0-42.667 19.102-42.667 42.667 0 23.561 19.103 42.667 42.667 42.667h204.8c23.565 0 42.667-19.106 42.667-42.667 0-23.565-19.102-42.667-42.667-42.667h-204.8z"],"attrs":[{"fill":"rgb(245, 69, 92)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-busy"]},"attrs":[{"fill":"rgb(245, 69, 92)"}],"properties":{"order":472,"id":71,"name":"status-busy","prevSize":32,"code":59759},"setIdx":0,"setId":3,"iconIdx":101},{"icon":{"paths":["M819.2 512c0 169.662-137.538 307.2-307.2 307.2s-307.2-137.538-307.2-307.2c0-169.662 137.538-307.2 307.2-307.2s307.2 137.538 307.2 307.2z"],"attrs":[{"fill":"rgb(45, 224, 165)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-online"]},"attrs":[{"fill":"rgb(45, 224, 165)"}],"properties":{"order":473,"id":70,"name":"status-online","prevSize":32,"code":59760},"setIdx":0,"setId":3,"iconIdx":102},{"icon":{"paths":["M430.844 103.994c80.695-16.052 164.339-7.814 240.354 23.673 76.011 31.486 140.983 84.806 186.692 153.216 45.713 68.411 70.11 148.839 70.11 231.117h-64c0-69.619-20.642-137.674-59.324-195.561-38.677-57.886-93.653-103.003-157.973-129.645-64.316-26.642-135.091-33.613-203.375-20.031-68.281 13.582-131.001 47.107-180.23 96.335s-82.753 111.949-96.335 180.23c-13.582 68.284-6.611 139.059 20.031 203.375 26.642 64.32 71.759 119.296 129.645 157.973 57.886 38.682 125.941 59.324 195.561 59.324v64c-82.278 0-162.706-24.397-231.117-70.11-68.41-45.709-121.731-110.682-153.216-186.692v0c-31.486-76.015-39.724-159.659-23.673-240.354 16.051-80.697 55.671-154.821 113.85-213s132.303-97.799 213-113.85z","M593.156 920.009c-80.695 16.051-164.339 7.812-240.353-23.676-76.014-31.484-140.984-84.804-186.695-153.216s-70.109-148.838-70.109-231.117h64c0 69.619 20.644 137.673 59.322 195.558 38.678 57.89 93.653 103.006 157.973 129.647 64.317 26.641 135.093 33.613 203.377 20.032 68.279-13.585 130.999-47.108 180.228-96.337s82.752-111.949 96.337-180.228c13.581-68.284 6.609-139.059-20.032-203.377-26.641-64.32-71.757-119.295-129.643-157.973-57.89-38.678-125.943-59.322-195.563-59.322v-64c82.278 0 162.705 24.398 231.117 70.109s121.732 110.681 153.216 186.695v0c31.488 76.014 39.727 159.657 23.676 240.353-16.055 80.695-55.676 154.82-113.852 213.001-58.18 58.176-132.305 97.796-213.001 113.852z"],"attrs":[{"fill":"rgb(228, 231, 234)"},{"fill":"rgb(9, 90, 210)"}],"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["loading"]},"attrs":[{"fill":"rgb(228, 231, 234)"},{"fill":"rgb(9, 90, 210)"}],"properties":{"order":474,"id":69,"name":"loading","prevSize":32,"code":59761,"codes":[59761,59762]},"setIdx":0,"setId":3,"iconIdx":103},{"icon":{"paths":["M305.297 498.091c-12.356-12.634-12.129-32.896 0.507-45.252l191.971-187.717c12.437-12.161 32.307-12.161 44.745 0l191.97 187.717c12.638 12.356 12.864 32.619 0.508 45.252-12.356 12.638-32.619 12.864-45.252 0.508l-137.6-134.55v372.11c0 17.673-14.323 32-32 32-17.673 0-32-14.327-32-32v-372.11l-137.595 134.55c-12.636 12.356-32.896 12.13-45.252-0.508z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-up"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":475,"id":68,"name":"arrow-up","prevSize":32,"code":59763},"setIdx":0,"setId":3,"iconIdx":104},{"icon":{"paths":["M734.997 526.067c12.356 12.634 12.13 32.896-0.508 45.248l-191.97 187.721c-12.437 12.16-32.307 12.16-44.745 0l-191.97-187.721c-12.636-12.352-12.863-32.614-0.507-45.248 12.356-12.638 32.616-12.864 45.252-0.508l137.595 134.549v-372.109c0-17.673 14.327-32 32-32 17.677 0 32 14.327 32 32v372.109l137.6-134.549c12.634-12.356 32.896-12.13 45.252 0.508z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":476,"id":67,"name":"arrow-down","prevSize":32,"code":59764},"setIdx":0,"setId":3,"iconIdx":105},{"icon":{"paths":["M832.678 512c0-176.731-143.27-320-320-320-176.733 0-320.001 143.269-320.001 320 0 176.73 143.269 320 320.001 320 176.73 0 320-143.27 320-320zM896.678 512c0 212.079-171.925 384-384 384-212.079 0-384.001-171.921-384.001-384 0-212.077 171.923-384 384.001-384 212.075 0 384 171.923 384 384zM512.678 704c-106.040 0-192.001-85.961-192.001-192s85.961-192 192.001-192c106.039 0 192 85.961 192 192s-85.961 192-192 192z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["record"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":477,"id":66,"name":"record","prevSize":32,"code":59765},"setIdx":0,"setId":3,"iconIdx":106},{"icon":{"paths":["M132.506 272c0-17.673 14.327-32 32-32h703.999c17.673 0 32 14.327 32 32s-14.327 32-32 32h-703.999c-17.673 0-32-14.327-32-32zM292.506 432c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-17.673 14.327-32 32-32s32 14.327 32 32zM292.506 752c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-17.673 14.327-32 32-32s32 14.327 32 32zM452.506 624c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-31.999 14.327-31.999 32s14.327 32 31.999 32zM388.506 400c-17.673 0-32 14.327-32 32s14.327 32 32 32h479.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-479.999zM356.506 752c0-17.673 14.327-32 32-32h479.999c17.673 0 32 14.327 32 32s-14.327 32-32 32h-479.999c-17.673 0-32-14.327-32-32zM580.506 560c-17.673 0-32 14.327-32 32s14.327 32 32 32h288c17.673 0 32-14.327 32-32s-14.327-32-32-32h-288z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["group-by-type"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":478,"id":65,"name":"group-by-type","prevSize":32,"code":59766},"setIdx":0,"setId":3,"iconIdx":107},{"icon":{"paths":["M825.374 153.373c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-671.999 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l672.001-672.001zM575.612 569.067l-44.706 44.71c7.846 5.905 15.846 11.452 23.535 16.013 9.199 5.461 22.835 12.578 36.971 13.222 16.226 0.738 31.445-5.299 42.47-10.974 11.742-6.042 22.793-13.824 31.744-20.945 1.054-0.836 2.133-1.229 2.884-1.34 0.401-0.060 0.61-0.030 0.687-0.013l121.097 59.076c-5.581 29.329-20.382 66.769-42.394 92.621-11.23 13.184-23.198 22.037-35.388 26.317-11.597 4.075-25.404 4.766-42.65-1.673-48.427-18.074-103.407-53.154-151.693-89.263-14.029-10.492-27.298-20.919-39.467-30.844l-44.39 44.39c14.187 11.686 29.841 24.064 46.447 36.48 49.805 37.248 110.447 76.561 167.258 97.762 30.276 11.298 59.149 11.238 85.197 2.086 25.455-8.939 46.093-25.783 62.246-44.757 31.868-37.423 50.637-88.141 57.062-126.246 4.719-27.985-11.776-51.767-33.139-62.191l-122.807-59.908c-24.41-11.908-51.46-6.003-69.837 8.61-7.006 5.572-14.541 10.739-21.44 14.289-5.577 2.867-8.93 3.742-10.313 4.006l-0.094-0.038c-0.99-0.401-3.729-1.515-8.567-4.386-3.277-1.946-6.874-4.301-10.714-7.006zM313.636 589.683l44.39-44.39c-9.92-12.164-20.35-25.438-30.839-39.462-36.108-48.286-71.191-103.267-89.262-151.691-6.437-17.249-5.748-31.055-1.675-42.651 4.282-12.192 13.134-24.16 26.319-35.388 25.85-22.012 63.29-36.814 92.623-42.392l59.075 121.096c0.018 0.076 0.047 0.285-0.012 0.687-0.111 0.75-0.504 1.83-1.343 2.884-7.118 8.951-14.9 20.001-20.943 31.741-5.673 11.025-11.714 26.246-10.976 42.472 0.643 14.135 7.761 27.772 13.222 36.971 4.562 7.689 10.109 15.684 16.014 23.535l44.705-44.71c-2.701-3.836-5.056-7.433-7.002-10.709-2.867-4.838-3.981-7.578-4.386-8.567l-0.038-0.094c0.265-1.382 1.139-4.738 4.006-10.312 3.55-6.9 8.717-14.437 14.289-21.443 14.613-18.374 20.523-45.425 8.614-69.835l-59.913-122.809c-10.421-21.361-34.203-37.856-62.191-33.137-38.103 6.425-88.824 25.194-126.246 57.060-18.972 16.155-35.817 36.795-44.758 62.246-9.15 26.048-9.211 54.92 2.088 85.196 21.201 56.813 60.513 117.455 97.761 167.26 12.417 16.606 24.795 32.256 36.478 46.443z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":479,"id":64,"name":"phone-disabled","prevSize":32,"code":59767},"setIdx":0,"setId":3,"iconIdx":108},{"icon":{"paths":["M801.122 657.591c22.477 7.735 50.957 2.581 67.409-20.544 22.4-31.488 44.992-80.623 48.922-129.617 1.993-24.841-0.691-51.345-12.365-75.665-11.951-24.887-32.324-45.347-61.722-58.766-55.164-25.181-125.841-40.262-187.396-49.144-61.948-8.937-117.495-11.981-143.966-11.981v62.47c23.198 0 75.755 2.787 135.044 11.341 59.674 8.61 123.358 22.681 170.377 44.143 16.751 7.646 26.027 17.894 31.347 28.975 5.589 11.648 7.795 26.372 6.409 43.635-2.714 33.839-18.718 70.784-35.516 95.467l-127.398-43.853c-0.068-0.043-0.239-0.171-0.478-0.495-0.452-0.61-0.939-1.651-1.092-2.991-1.297-11.362-3.605-24.678-7.633-37.252-3.785-11.806-10.278-26.842-22.272-37.794-10.449-9.54-25.126-14.148-35.494-16.794-11.917-3.038-25.566-5.18-38.946-6.716-26.859-3.085-55.77-4.092-74.351-4.092v62.468c16.9 0 43.341 0.939 67.217 3.686 11.994 1.374 22.554 3.123 30.643 5.184 5.449 1.391 8.175 2.543 9.161 2.957l0.094 0.043c0.789 1.161 2.543 4.151 4.459 10.121 2.368 7.39 4.041 16.375 5.056 25.267 2.662 23.326 17.613 46.63 43.294 55.471l129.199 44.476zM222.876 657.587c-22.474 7.735-50.954 2.581-67.407-20.544-22.4-31.488-44.993-80.623-48.922-129.617-1.992-24.841 0.691-51.345 12.367-75.665 11.948-24.887 32.321-45.347 61.719-58.765 55.165-25.181 125.841-40.263 187.399-49.144 61.946-8.937 117.494-11.981 143.964-11.981v62.47c-23.198 0-75.755 2.787-135.043 11.341-59.675 8.61-123.36 22.681-170.38 44.143-16.748 7.646-26.025 17.894-31.343 28.975-5.593 11.648-7.797 26.372-6.412 43.635 2.714 33.843 18.721 70.784 35.518 95.467l127.4-43.853c0.067-0.043 0.235-0.171 0.478-0.495 0.452-0.61 0.937-1.651 1.090-2.991 1.296-11.362 3.606-24.678 7.636-37.252 3.784-11.806 10.276-26.842 22.27-37.794 10.45-9.54 25.127-14.148 35.493-16.794 11.916-3.038 25.568-5.18 38.947-6.716 26.859-3.085 55.77-4.092 74.351-4.092v62.468c-16.9 0-43.341 0.939-67.221 3.686-11.989 1.378-22.551 3.123-30.638 5.184-5.452 1.391-8.175 2.543-9.162 2.957l-0.092 0.043c-0.791 1.161-2.546 4.151-4.459 10.121-2.368 7.39-4.044 16.375-5.058 25.267-2.66 23.326-17.611 46.63-43.292 55.471l-129.202 44.476z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone-end"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":480,"id":63,"name":"phone-end","prevSize":32,"code":59768},"setIdx":0,"setId":3,"iconIdx":109},{"icon":{"paths":["M410.504 204.615c-10.421-21.361-34.203-37.856-62.191-33.137-38.103 6.425-88.824 25.194-126.246 57.060-18.972 16.155-35.817 36.795-44.758 62.246-9.15 26.048-9.211 54.92 2.088 85.196 21.201 56.813 60.513 117.455 97.761 167.26 37.482 50.125 74.609 91.554 93.327 110.272l44.173-44.173c-16.405-16.405-51.598-55.539-87.472-103.509-36.108-48.286-71.191-103.267-89.262-151.691-6.437-17.249-5.748-31.055-1.675-42.651 4.282-12.192 13.134-24.16 26.319-35.388 25.85-22.012 63.29-36.814 92.623-42.392l59.075 121.096c0.018 0.075 0.047 0.285-0.012 0.687-0.111 0.75-0.504 1.83-1.343 2.884-7.118 8.951-14.9 20.001-20.943 31.741-5.673 11.025-11.714 26.246-10.976 42.472 0.643 14.135 7.761 27.772 13.222 36.971 6.277 10.577 14.417 21.743 22.79 32.29 16.809 21.175 36.542 42.33 49.679 55.467l44.173-44.173c-11.951-11.951-29.978-31.309-44.924-50.133-7.505-9.455-13.739-18.159-18.001-25.335-2.867-4.838-3.981-7.578-4.386-8.567l-0.038-0.094c0.265-1.382 1.139-4.738 4.006-10.312 3.55-6.9 8.717-14.437 14.289-21.443 14.613-18.374 20.523-45.425 8.614-69.835l-59.913-122.809zM819.383 613.5c21.363 10.423 37.858 34.206 33.139 62.191-6.426 38.106-25.195 88.823-57.062 126.246-16.154 18.974-36.791 35.819-62.246 44.757-26.048 9.152-54.921 9.212-85.197-2.086-56.811-21.201-117.453-60.514-167.258-97.762-50.125-37.483-91.555-74.607-110.273-93.325l44.173-44.173c16.407 16.405 55.54 51.597 103.511 87.471 48.286 36.109 103.266 71.189 151.693 89.263 17.246 6.434 31.053 5.747 42.65 1.673 12.19-4.279 24.158-13.133 35.388-26.317 22.012-25.852 36.813-63.292 42.394-92.621l-121.097-59.076c-0.077-0.017-0.286-0.047-0.687 0.013-0.751 0.111-1.83 0.503-2.884 1.34-8.951 7.121-20.002 14.903-31.744 20.945-11.025 5.675-26.244 11.712-42.47 10.974-14.135-0.644-27.772-7.761-36.971-13.222-10.577-6.276-21.743-14.417-32.29-22.788-21.175-16.811-42.33-36.544-55.467-49.681l44.173-44.173c11.951 11.951 31.309 29.982 50.133 44.928 9.455 7.501 18.159 13.734 25.335 17.997 4.838 2.871 7.578 3.985 8.567 4.386l0.094 0.038c1.382-0.265 4.736-1.139 10.313-4.006 6.899-3.55 14.434-8.717 21.44-14.289 18.377-14.613 45.427-20.518 69.837-8.61l122.807 59.908z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":481,"id":62,"name":"phone","prevSize":32,"code":59769},"setIdx":0,"setId":3,"iconIdx":110},{"icon":{"paths":["M802.257 544c0 176.73-143.27 320-320 320-176.733 0-320.002-143.27-320.002-320 0-176.731 143.269-320 320.002-320v-64c-212.079 0-384.002 171.923-384.002 384 0 212.079 171.922 384 384.002 384 212.075 0 384-171.921 384-384 0-10.778-0.444-21.449-1.314-32h-64.269c1.045 10.526 1.583 21.201 1.583 32zM802.257 112c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32v112h-112c-17.677 0-32 14.327-32 32s14.323 32 32 32h112v112c0 17.673 14.323 32 32 32 17.673 0 32-14.327 32-32v-112h112c17.673 0 32-14.327 32-32s-14.327-32-32-32h-112v-112zM386.255 512c35.346 0 64.002-28.655 64.002-64 0-35.346-28.656-64-64.002-64s-64 28.654-64 64c0 35.345 28.654 64 64 64zM642.257 448c0 35.345-28.655 64-64 64-35.349 0-64-28.655-64-64 0-35.346 28.651-64 64-64 35.345 0 64 28.654 64 64zM332.060 589.077c-10.451-14.251-30.477-17.335-44.729-6.882-14.252 10.449-17.332 30.477-6.881 44.727 37.658 51.354 77.753 84.625 119.178 102.665 41.737 18.18 82.796 19.989 120.364 11.652 73.468-16.307 132.211-70.874 164.070-114.317 10.449-14.251 7.369-34.278-6.882-44.727-14.251-10.453-34.278-7.369-44.727 6.882-26.812 36.557-73.668 77.99-126.332 89.681-25.502 5.662-52.642 4.476-80.938-7.846-28.608-12.459-60.38-37.188-93.122-81.835z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reaction-add"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":482,"id":61,"name":"reaction-add","prevSize":32,"code":59770},"setIdx":0,"setId":3,"iconIdx":111},{"icon":{"paths":["M226.503 304.026c-49.642 68.947-66.502 152.644-66.502 207.97v1.451l-0.131 1.446c-9.702 106.718 28.59 181.931 90.135 234.005 62.938 53.257 152.152 83.733 244.195 93.961 91.968 10.219 193.271 1.446 273.31-15.479 40.017-8.465 73.613-18.735 97.438-29.077 9.246-4.015 16.439-7.787 21.739-11.127-2.466-1.566-5.449-3.302-9.003-5.197-9.225-4.911-19.78-9.579-30.733-14.366l-1.916-0.836c-9.664-4.224-20.471-8.943-28.655-13.521-14.835-8.294-29.389-17.732-40.004-27.772-5.227-4.941-11.166-11.571-15.147-19.827-4.211-8.742-7.223-21.794-1.306-35.601 31.979-74.62 47.177-115.49 54.537-142.481 6.874-25.199 6.874-37.888 6.874-58.061v-0.183c0-16.038-9.318-89.517-55.812-158.033-45.013-66.338-125.99-129.968-274.854-129.968-134.636 0-215.696 55.382-264.163 122.698zM174.565 266.63c60.333-83.796 160.607-149.302 316.102-149.302 171.136 0 271.497 75.037 327.812 158.032 54.839 80.818 66.854 167.337 66.854 193.969 0 22.383-0.021 41.694-9.126 75.085-8.128 29.807-23.484 70.929-51.942 137.924 5.261 4.169 13.069 9.306 23.356 15.061 5.316 2.974 13.487 6.554 24.964 11.571 10.654 4.655 23.437 10.266 35.174 16.512 11.26 5.995 24.418 14.033 34.202 24.542 10.231 10.991 20.975 29.845 13.299 52.864-5.056 15.164-16.794 25.937-26.577 33.092-10.628 7.774-23.817 14.763-38.251 21.030-29.009 12.587-67.029 23.962-109.683 32.981-85.312 18.039-193.583 27.588-293.615 16.474-99.956-11.106-202.741-44.629-278.47-108.71-76.833-65.011-123.811-159.996-112.66-287.223 0.286-65.549 19.841-162.346 78.561-243.902zM496 320c17.673 0 32 14.327 32 32v224c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32zM528 672c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 17.673 14.327 32 32 32s32-14.327 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["canned-response"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":483,"id":60,"name":"canned-response","prevSize":32,"code":59771},"setIdx":0,"setId":3,"iconIdx":112},{"icon":{"paths":["M332.328 644.885c-21.571-19.618-6.007-52.885 23.151-52.885 8.965 0 17.527 3.529 24.272 9.438 21.605 18.918 42.641 31.957 62.702 40.508 36.565 15.59 71.808 17.109 104.794 9.941 37.632-8.179 72.657-27.81 102.11-51.345 6.724-5.376 14.997-8.542 23.607-8.542 30.217 0 45.615 34.257 22.396 53.594-36.911 30.741-82.79 57.591-134.519 68.834-44.8 9.732-93.67 7.629-143.486-13.606-28.791-12.275-57.234-30.656-85.026-55.936z","M402.423 480c35.345 0 64-28.655 64-64 0-35.346-28.655-64-64-64s-64 28.654-64 64c0 35.345 28.654 64 64 64z","M626.423 480c35.345 0 64-28.655 64-64 0-35.346-28.655-64-64-64s-64 28.654-64 64c0 35.345 28.655 64 64 64z","M930.257 512c0 229.751-186.253 416-416 416-229.752 0-416.002-186.249-416.002-416s186.249-416 416.002-416c229.747 0 416 186.249 416 416zM866.257 512c0-194.404-157.598-352-352-352-194.406 0-352.002 157.596-352.002 352 0 194.402 157.596 352 352.002 352 194.402 0 352-157.598 352-352z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["emoji"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":484,"id":59,"name":"emoji","prevSize":32,"code":59772},"setIdx":0,"setId":3,"iconIdx":113},{"icon":{"paths":["M480.094 128c104.26 0 181.252 29.018 237.495 70.652l-44.318 44.317c-45.521-31.22-107.746-53.057-193.178-53.057-129.725 0-207.828 53.576-254.528 118.696-47.832 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-7.328 80.926 13.761 143.13 51.784 190.498l-43.879 43.883c-50.179-59.904-78.131-139.046-69.44-238.596 0.276-63.411 19.117-157.053 75.696-235.949 58.132-81.062 154.749-144.433 304.572-144.433zM825.105 332.498l-47.010 47.009c16.503 42.634 20.608 78.524 20.608 89.011v0.179c0 19.516 0 31.791-6.622 56.171-7.091 26.108-21.739 65.647-52.553 137.835-5.7 13.355-2.795 25.98 1.263 34.436 3.831 7.987 9.557 14.4 14.592 19.183 10.227 9.711 24.252 18.837 38.545 26.867 7.889 4.429 18.3 8.994 27.614 13.077l1.843 0.806c10.556 4.634 20.727 9.148 29.611 13.897 3.426 1.835 6.302 3.516 8.674 5.030-5.103 3.23-12.036 6.882-20.941 10.765-22.959 10.005-55.33 19.938-93.888 28.126-77.12 16.375-174.729 24.862-263.339 14.976-43.746-4.881-86.83-14.528-126.537-29.231l-47.543 47.543c52.771 23.019 110.493 36.89 167.266 43.221 96.388 10.752 200.708 1.515 282.91-15.936 41.097-8.725 77.734-19.729 105.681-31.906 13.909-6.063 26.615-12.821 36.855-20.343 9.425-6.925 20.74-17.344 25.609-32.017 7.394-22.268-2.953-40.503-12.813-51.136-9.429-10.167-22.106-17.941-32.956-23.74-11.311-6.046-23.625-11.469-33.89-15.979-11.059-4.851-18.931-8.316-24.051-11.191-9.916-5.568-17.442-10.534-22.507-14.571 27.422-64.806 42.214-104.589 50.048-133.423 8.772-32.303 8.794-50.982 8.794-72.64 0-20.105-7.053-75.5-35.264-136.020zM838.293 153.373c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-682.667 682.665c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l682.665-682.668z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":485,"id":58,"name":"message-disabled","prevSize":32,"code":59773},"setIdx":0,"setId":3,"iconIdx":114},{"icon":{"paths":["M223.311 308.609c-47.831 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-9.348 103.236 27.547 175.996 86.848 226.377 60.643 51.516 146.604 80.998 235.291 90.893 88.61 9.886 186.223 1.399 263.339-14.976 38.562-8.188 70.929-18.121 93.888-28.126 8.909-3.883 15.838-7.535 20.945-10.765-2.377-1.515-5.248-3.196-8.678-5.030-8.883-4.749-19.055-9.263-29.611-13.897l-1.843-0.806c-9.314-4.083-19.725-8.649-27.61-13.077-14.293-8.030-28.318-17.156-38.545-26.867-5.035-4.783-10.761-11.196-14.596-19.183-4.058-8.457-6.959-21.082-1.259-34.436 30.814-72.188 45.457-111.727 52.553-137.835 6.622-24.38 6.622-36.655 6.618-56.171v-0.179c0-15.514-8.977-86.595-53.777-152.876-43.371-64.175-121.395-125.729-264.828-125.729-129.727 0-207.831 53.576-254.531 118.696zM173.267 272.433c58.132-81.062 154.749-144.433 304.574-144.433 164.894 0 261.594 72.589 315.857 152.878 52.838 78.182 64.414 161.879 64.414 187.641 0 21.658-0.017 40.337-8.794 72.64-7.834 28.834-22.626 68.617-50.048 133.423 5.069 4.036 12.591 9.003 22.507 14.571 5.12 2.876 12.992 6.34 24.055 11.191 10.261 4.51 22.579 9.933 33.89 15.979 10.846 5.798 23.526 13.572 32.951 23.74 9.86 10.633 20.211 28.868 12.817 51.136-4.873 14.673-16.183 25.092-25.609 32.017-10.24 7.522-22.946 14.281-36.86 20.343-27.947 12.177-64.58 23.181-105.681 31.906-82.197 17.451-186.522 26.688-282.906 15.936-96.31-10.743-195.347-43.174-268.314-105.165-74.031-62.891-119.295-154.778-108.551-277.854 0.276-63.411 19.118-157.053 75.696-235.949z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":486,"id":57,"name":"message","prevSize":32,"code":59774},"setIdx":0,"setId":3,"iconIdx":115},{"icon":{"paths":["M273.129 356.679c6.747-28.299 25.696-52.005 51.209-67.153 25.601-15.2 56.323-20.858 84.699-14.531 27.639 6.164 55.116 24.12 74.871 60.34l22.541 41.324 30.135-36.162c27.238-32.687 85.129-39.712 134.716-14.063 23.561 12.187 42.206 30.617 52.062 53.489 9.655 22.406 12.113 51.897-1.434 89.149l-15.616 42.935h45.687c53.606 0 82.419 15.881 97.643 33.749 15.65 18.377 21.897 44.651 18.556 74.718-3.345 30.084-16.055 60.514-33.455 82.889-17.941 23.066-36.651 32.644-50.743 32.644h-543.998c-18.791 0-37.067-10.359-52.194-31.578-15.217-21.342-24.977-51.051-25.818-81.31-0.84-30.229 7.238-57.916 23.734-77.491 15.795-18.748 42.268-33.621 86.278-33.621h59.792l-33.166-49.749c-27.882-41.825-32.118-77.815-25.498-105.58zM518.221 272.084c-26.342-32.073-59.669-51.619-95.255-59.554-45.624-10.173-92.902-0.833-131.301 21.965-38.487 22.85-69.538 60.142-80.791 107.342-8.278 34.72-5.369 72.759 10.731 111.77-35.67 8.465-64.099 26.185-84.825 50.782-29.004 34.423-39.927 78.737-38.767 120.508 1.159 41.741 14.399 84.032 37.682 116.689 23.373 32.785 59.097 58.423 104.305 58.423h543.999c41.907 0 77.201-26.419 101.261-57.353 24.597-31.629 41.886-73.195 46.545-115.115 4.659-41.933-3.098-87.659-33.446-123.285-24.102-28.297-59.503-46.771-105.613-53.453 5.833-35.195 1.685-67.675-10.607-96.206-16.644-38.627-46.997-67.197-81.438-85.009-54.899-28.397-128.734-32.652-182.481 2.495zM512 437.333c17.673 0 32 14.327 32 32v53.329h53.333c17.673 0 32 14.323 32 32 0 17.673-14.327 32-32 32h-53.333v53.338c0 17.673-14.327 32-32 32s-32-14.327-32-32v-53.338h-53.333c-17.672 0-31.999-14.327-31.999-32 0-17.677 14.327-32 31.999-32h53.333v-53.329c0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cloud-connectivity"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":487,"id":56,"name":"cloud-connectivity","prevSize":32,"code":59775},"setIdx":0,"setId":3,"iconIdx":116},{"icon":{"paths":["M515.558 142.7c12.915-3.408 26.505-3.288 39.36 0.345l245.197 69.318c29.163 8.245 51.255 33.818 53.504 65.14 24.503 341.143-184.397 520.424-272.614 580.213-33.050 22.404-75.277 22.473-108.416 0.26-88.759-59.503-300.099-238.588-276.509-579.794 2.205-31.89 24.96-57.772 54.753-65.632l264.726-69.849zM537.506 204.631c-1.835-0.519-3.776-0.536-5.623-0.049l-264.722 69.849c-4.4 1.161-6.999 4.776-7.233 8.165-21.305 308.167 168.215 468.534 248.301 522.217 11.507 7.714 25.434 7.68 36.873-0.073 79.377-53.803 266.833-214.289 244.681-522.653-0.239-3.358-2.786-6.925-7.078-8.139l-245.197-69.318z","M492.928 337.331c-19.759 4.672-47.249 12.307-81.151 24.553 20.812 108.773 53.221 187.015 81.151 238.829v-263.382zM495.125 271.311c33.766-6.897 61.803 19.742 61.803 51.124v360.966c0 17.024-10.334 31.748-25.643 37.521-15.671 5.909-33.779 1.327-44.86-12.676-32.657-41.267-106.324-152.951-141.192-354.565-3.241-18.744 7.065-37.694 25.329-44.725 56.185-21.63 99.095-32.442 124.562-37.644z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":488,"id":55,"name":"shield","prevSize":32,"code":59776},"setIdx":0,"setId":3,"iconIdx":117},{"icon":{"paths":["M664.081 405.067c11.639-13.3 10.291-33.517-3.008-45.155s-33.519-10.29-45.154 3.011l-125.252 143.142-39.919-45.619c-11.635-13.299-31.852-14.647-45.153-3.008-13.3 11.635-14.648 31.851-3.011 45.154l64.001 73.143c6.076 6.942 14.852 10.927 24.081 10.927s18.005-3.985 24.081-10.927l149.333-170.669z","M541.99 143.045c-12.855-3.633-26.449-3.753-39.364-0.345l-264.722 69.849c-29.793 7.861-52.548 33.743-54.753 65.632-23.59 341.206 187.75 520.291 276.51 579.794 33.135 22.212 75.362 22.144 108.416-0.26 88.218-59.789 297.118-239.070 272.614-580.213-2.253-31.321-24.341-56.895-53.508-65.14l-245.193-69.318zM518.955 204.582c1.847-0.487 3.789-0.47 5.623 0.049l245.197 69.318c4.292 1.214 6.839 4.781 7.078 8.139 22.153 308.364-165.303 468.85-244.685 522.653-11.435 7.753-25.365 7.787-36.873 0.073-80.083-53.683-269.603-214.050-248.298-522.217 0.234-3.389 2.833-7.004 7.234-8.165l264.723-69.849z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-check"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":489,"id":54,"name":"shield-check","prevSize":32,"code":59777},"setIdx":0,"setId":3,"iconIdx":118},{"icon":{"paths":["M373.333 448c0-17.673 14.327-32 32-32h213.334c17.673 0 32 14.327 32 32s-14.327 32-32 32h-213.334c-17.673 0-32-14.327-32-32z","M405.333 544c-17.673 0-32 14.327-32 32s14.327 32 32 32h213.334c17.673 0 32-14.327 32-32s-14.327-32-32-32h-213.334z","M256 128c-17.673 0-32 14.327-32 32v704c0 17.673 14.327 32 32 32h512c17.673 0 32-14.327 32-32v-501.745c0-6.67-2.082-13.172-5.961-18.6l-144.469-202.255c-6.003-8.41-15.701-13.4-26.039-13.4h-367.531zM736 372.51v459.49h-448v-640h319.066l128.934 180.51z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-document"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":490,"id":53,"name":"file-document","prevSize":32,"code":59778},"setIdx":0,"setId":3,"iconIdx":119},{"icon":{"paths":["M352 445.44c0-17.677 14.327-32.002 32-32.002h256c17.673 0 32 14.325 32 32.002 0 17.673-14.327 32-32 32h-256c-17.673 0-32-14.327-32-32z","M352 322.558c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32s-14.327 32-32 32h-256c-17.673 0-32-14.327-32-32z","M864 679.68c0 17.673-14.327 32-32 32h-18.509c-8.218 40.546-8.218 82.334 0 122.88h19.789c16.964 0 30.72 13.756 30.72 30.72s-13.756 30.72-30.72 30.72h-545.28c-70.692 0-128-55.014-128-122.88v-453.12c0-106.039 85.961-192 192-192h480c17.673 0 32 14.327 32 32v519.68zM748.42 834.56c-6.788-40.678-6.788-82.202 0-122.88h-460.42c-35.346 0-64 27.507-64 61.44s28.654 61.44 64 61.44h460.42zM224 647.68h576v-455.68h-448c-70.692 0-128 57.308-128 128v327.68z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["book"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":491,"id":52,"name":"book","prevSize":32,"code":59779},"setIdx":0,"setId":3,"iconIdx":120},{"icon":{"paths":["M778.641 213.333c-17.673 0-32 14.327-32 32v533.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-533.333c0-17.673-14.327-32-32-32z","M600.87 341.333c-17.673 0-32 14.327-32 32v405.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-405.333c0-17.673-14.327-32-32-32z","M423.103 810.667c-17.673 0-32-14.327-32-32v-277.333c0-17.673 14.327-32 32-32s32.001 14.327 32.001 32v277.333c0 17.673-14.327 32-32.001 32z","M245.333 597.333c-17.673 0-32 14.327-32 32v149.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-149.333c0-17.673-14.327-32-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["signal"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":492,"id":51,"name":"signal","prevSize":32,"code":59780},"setIdx":0,"setId":3,"iconIdx":121},{"icon":{"paths":["M160 512c0-17.673 13.133-32 29.333-32h645.333c16.201 0 29.333 14.327 29.333 32s-13.133 32-29.333 32h-645.333c-16.201 0-29.333-14.327-29.333-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["h-bar"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":493,"id":50,"name":"h-bar","prevSize":32,"code":59781},"setIdx":0,"setId":3,"iconIdx":122},{"icon":{"paths":["M292.515 192c13.364 0 25.321 8.305 29.987 20.829l96 257.699c6.17 16.559-2.255 34.987-18.816 41.156s-34.988-2.253-41.158-18.816l-20.476-54.963h-91.075l-20.476 54.963c-6.17 16.563-24.596 24.986-41.158 18.816s-24.985-24.597-18.816-41.156l96-257.699c4.666-12.524 16.623-20.829 29.987-20.829zM314.211 373.904l-21.695-58.238-21.696 58.238h43.391z","M525.286 626.436c-12.156 12.826-11.61 33.079 1.22 45.235l160 151.586c12.343 11.695 31.676 11.695 44.019 0l160-151.586c12.826-12.156 13.376-32.41 1.22-45.235-12.156-12.83-32.41-13.38-45.239-1.225l-105.993 100.42v-501.632c0-17.673-14.327-32-32-32s-32 14.327-32 32v501.632l-105.988-100.42c-12.83-12.156-33.084-11.605-45.239 1.225z","M196.515 570.944c-17.673 0-32 14.327-32 32 0 17.677 14.327 32 32 32h116.145l-139.065 142.733c-8.979 9.216-11.565 22.916-6.564 34.769 5.001 11.857 16.617 19.563 29.484 19.563h192c17.673 0 32-14.327 32-32s-14.327-32-32-32h-116.145l139.065-142.733c8.979-9.216 11.565-22.912 6.564-34.769-5.001-11.853-16.617-19.563-29.484-19.563h-192z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-az"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":494,"id":49,"name":"sort-az","prevSize":32,"code":59782},"setIdx":0,"setId":3,"iconIdx":123},{"icon":{"paths":["M922.551 666.351c12.587-12.237 12.86-32.35 0.606-44.919-12.254-12.574-32.393-12.843-44.983-0.606l-105.971 103.019v-500.139c0-17.545-14.242-31.767-31.808-31.767-17.57 0-31.812 14.223-31.812 31.767v500.139l-105.971-103.019c-12.591-12.237-32.73-11.968-44.983 0.606-12.254 12.57-11.981 32.683 0.606 44.919l159.97 155.511c12.352 12.006 32.026 12.006 44.378 0l159.97-155.511zM564.425 333.229c17.57 0 31.808-14.223 31.808-31.767s-14.238-31.767-31.808-31.767h-431.919c-17.568 0-31.809 14.223-31.809 31.767s14.242 31.767 31.809 31.767h431.919zM468.442 535.394c17.57 0 31.812-14.221 31.812-31.765s-14.242-31.77-31.812-31.77h-335.936c-17.568 0-31.809 14.225-31.809 31.77s14.242 31.765 31.809 31.765h335.936zM388.458 722.010c17.568 0 31.809-14.221 31.809-31.765s-14.241-31.77-31.809-31.77h-255.952c-17.568 0-31.809 14.225-31.809 31.77s14.241 31.765 31.809 31.765h255.952z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":495,"id":48,"name":"sort","prevSize":32,"code":59783},"setIdx":0,"setId":3,"iconIdx":124},{"icon":{"paths":["M822.242 183.279c-37.141-37.936-98.074-38.532-135.953-1.331l-348.017 341.786c-12.722 12.497-21.706 28.284-25.943 45.594l-23.65 96.623c-6.931 28.318 16.732 54.771 45.685 51.072l90.573-11.563c20.195-2.577 39.050-11.503 53.834-25.485l360.013-340.471c38.933-36.82 40.119-98.352 2.633-136.64l-19.174-19.584zM731.166 227.52c12.625-12.4 32.934-12.201 45.316 0.444l19.174 19.584c12.497 12.762 12.1 33.273-0.879 45.547l-69.871 66.081-63.364-63.28 69.623-68.376zM615.906 340.713l62.511 62.427-243.652 230.426c-4.928 4.659-11.211 7.633-17.943 8.495l-58.231 7.433 15.908-64.99c1.412-5.769 4.407-11.034 8.648-15.198l232.76-228.593zM192.001 265.251c0-17.65 14.327-31.958 32-31.958h280.959c17.673 0 32-14.308 32-31.958s-14.327-31.958-32-31.958h-280.959c-53.019 0-96 42.924-96 95.874v533.695c0 52.949 42.981 95.872 96 95.872h529.065c53.022 0 96-42.923 96-95.872v-279.044c0-17.651-14.327-31.962-32-31.962s-32 14.31-32 31.962v279.044c0 17.651-14.327 31.957-32 31.957h-529.065c-17.673 0-32-14.306-32-31.957v-533.695z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["create"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":496,"id":47,"name":"create","prevSize":32,"code":59784},"setIdx":0,"setId":3,"iconIdx":125},{"icon":{"paths":["M252.138 275.61l-13.972 14.271c-37.092 37.884-36.449 98.665 1.435 135.757l145.62 142.576c37.885 37.090 98.665 36.446 135.756-1.438l13.973-14.268c0.725-0.742 1.434-1.489 2.129-2.244l0.098 0.098c5.807-5.905 13.888-9.566 22.822-9.566 17.673 0 32 14.327 32 32 0 9.6-4.228 18.21-10.923 24.077l-14.37 14.677c-61.82 63.142-163.119 64.213-226.26 2.394l-145.62-142.575c-63.141-61.82-64.212-163.121-2.392-226.262l13.972-14.271c61.82-63.141 163.121-64.212 226.263-2.392l74.692 73.131c0.977 0.847 1.899 1.752 2.769 2.71l0.371 0.366-0.026 0.026c4.937 5.63 7.923 13.005 7.923 21.079 0 17.673-14.327 32-32 32-7.829 0-15.002-2.813-20.565-7.482l-0.107 0.108-77.833-76.207c-37.885-37.092-98.665-36.449-135.757 1.435zM787.861 768l13.973-14.268c37.090-37.884 36.45-98.667-1.434-135.757l-145.621-142.575c-37.888-37.090-98.667-36.45-135.757 1.434l-13.973 14.272c-0.725 0.738-1.434 1.485-2.129 2.244l-0.098-0.102c-5.807 5.905-13.888 9.57-22.822 9.57-17.673 0-32-14.327-32-32 0-9.6 4.228-18.214 10.923-24.077l0.397-0.41 13.973-14.272c61.82-63.139 163.119-64.21 226.261-2.389l145.617 142.575c63.142 61.82 64.213 163.119 2.394 226.261l-13.973 14.268c-61.82 63.142-163.119 64.213-226.261 2.394l-74.692-73.131c-0.977-0.849-1.899-1.754-2.769-2.709l-0.371-0.367 0.026-0.026c-4.937-5.632-7.923-13.005-7.923-21.077 0-17.673 14.327-32 32-32 7.829 0 15.002 2.812 20.565 7.479l0.107-0.107 77.833 76.207c37.884 37.090 98.667 36.45 135.757-1.438z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["link"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":497,"id":46,"name":"link","prevSize":32,"code":59785},"setIdx":0,"setId":3,"iconIdx":126},{"icon":{"paths":["M234.667 170.662c0-47.128 38.205-85.333 85.333-85.333h384c47.13 0 85.333 38.205 85.333 85.333v682.668c0 47.125-38.204 85.333-85.333 85.333h-384c-47.128 0-85.333-38.208-85.333-85.333v-682.668zM298.667 170.662v682.668c0 11.78 9.551 21.333 21.333 21.333h384c11.78 0 21.333-9.553 21.333-21.333v-682.668c0-11.782-9.553-21.333-21.333-21.333h-96.29c-2.654 24.002-23.002 42.672-47.71 42.672h-96c-24.708 0-45.056-18.67-47.707-42.672h-96.293c-11.782 0-21.333 9.551-21.333 21.333z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mobile"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":498,"id":45,"name":"mobile","prevSize":32,"code":59786},"setIdx":0,"setId":3,"iconIdx":127},{"icon":{"paths":["M261.333 96c-17.673 0-32 14.327-32 32v768c0 17.673 14.327 32 32 32h512c17.673 0 32-14.327 32-32v-96c0-17.673-14.327-32-32-32s-32 14.327-32 32v64h-448v-704h448v64c0 17.673 14.327 32 32 32s32-14.327 32-32v-96c0-17.673-14.327-32-32-32h-512zM987.959 489.374l-160-160.001c-12.497-12.497-32.755-12.497-45.252 0s-12.497 32.758 0 45.255l105.37 105.373h-418.743c-17.673 0-32 14.327-32 32s14.327 32 32 32h418.743l-105.37 105.374c-12.497 12.497-12.497 32.755 0 45.252s32.755 12.497 45.252 0l160-160c12.497-12.497 12.497-32.755 0-45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["logout"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":499,"id":44,"name":"logout","prevSize":32,"code":59787},"setIdx":0,"setId":3,"iconIdx":128},{"icon":{"paths":["M736 864.209v-447.408h-448v447.408h448zM224 864.209v-447.408c-35.346 0-64-28.616-64-63.916v-127.831c0-35.299 28.654-63.916 64-63.916h224c0-35.299 28.655-63.915 64-63.915s64 28.616 64 63.915h224c35.345 0 64 28.616 64 63.916v127.831c0 35.299-28.655 63.916-64 63.916v447.408c0 35.298-28.655 63.915-64 63.915h-448c-35.346 0-64-28.617-64-63.915zM800 225.055h-576v127.831h576v-127.831zM416 544.631v191.748c0 17.647 14.327 31.957 32 31.957s32-14.31 32-31.957v-191.748c0-17.647-14.327-31.957-32-31.957s-32 14.31-32 31.957zM576 512.674c17.673 0 32 14.31 32 31.957v191.748c0 17.647-14.327 31.957-32 31.957s-32-14.31-32-31.957v-191.748c0-17.647 14.327-31.957 32-31.957z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["delete"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":500,"id":43,"name":"delete","prevSize":32,"code":59788},"setIdx":0,"setId":3,"iconIdx":129},{"icon":{"paths":["M891.494 238.961l-297.476 637.446c-16.853 36.113-69.619 31.458-79.889-7.049l-66.001-247.501 151.249-189.056-219.993 109.995-226.834-113.417c-35.43-17.715-29.696-69.947 8.733-79.555l681.2-170.3c34.837-8.71 64.196 26.892 49.011 59.436z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send-filled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":501,"id":42,"name":"send-filled","prevSize":32,"code":59789},"setIdx":0,"setId":3,"iconIdx":130},{"icon":{"paths":["M878.020 192.976c7.851 9.521 9.527 22.708 4.309 33.891l-298.667 640c-5.692 12.194-18.402 19.529-31.808 18.342-13.406-1.182-24.64-10.628-28.109-23.629l-81.617-306.074-285.774-142.884c-11.977-5.989-18.959-18.802-17.498-32.113s11.056-24.305 24.048-27.553l682.667-170.667c11.972-2.993 24.597 1.165 32.448 10.686zM505.822 545.967l57.079 214.054 233.028-499.351-533.581 133.396 203.605 101.802 69.513-52.134c14.14-10.603 34.197-7.735 44.8 6.4 10.603 14.14 7.74 34.197-6.4 44.8l-68.045 51.034z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":502,"id":41,"name":"send","prevSize":32,"code":59790},"setIdx":0,"setId":3,"iconIdx":131},{"icon":{"paths":["M800 384v306.743l54.626 54.63c9.152 9.152 11.891 22.916 6.938 34.871-4.954 11.959-16.619 19.755-29.564 19.755h-465.73l64.002-64h305.728v-305.732l63.492-63.492c0.337 5.699 0.508 11.441 0.508 17.224zM715.021 179.728l-45.257 45.255c-40.486-40.169-96.226-64.983-157.764-64.983-123.711 0-224 100.288-224 224v222.746l-64 64v-286.746c0-159.058 128.942-288 288-288 79.211 0 150.95 31.978 203.021 83.728zM416 832c0 53.018 42.982 96 96 96 53.022 0 96-42.982 96-96h-192zM879.872 150.62c-12.493-12.497-32.755-12.497-45.252 0l-675.617 675.616c-12.497 12.497-12.497 32.759 0 45.257s32.758 12.497 45.255 0l675.614-675.618c12.497-12.497 12.497-32.758 0-45.255z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["notification-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":503,"id":40,"name":"notification-disabled","prevSize":32,"code":59791},"setIdx":0,"setId":3,"iconIdx":132},{"icon":{"paths":["M608 832h-192c0 53.018 42.983 96 96 96s96-42.982 96-96zM736 736v-352c0-123.712-100.288-224-224-224s-224 100.288-224 224v352h448zM192 800c-12.943 0-24.611-7.795-29.564-19.755-4.953-11.955-2.215-25.719 6.937-34.871l54.627-54.63v-306.743c0-159.058 128.942-288 288-288 159.057 0 288 128.942 288 288v309.333l41.6 55.467c7.27 9.698 8.444 22.669 3.021 33.51-5.419 10.842-16.499 17.69-28.621 17.69h-624z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["notification"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":504,"id":39,"name":"notification","prevSize":32,"code":59792},"setIdx":0,"setId":3,"iconIdx":133},{"icon":{"paths":["M194.254 320c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M322.254 224c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M194.254 576c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M322.254 480c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M194.254 832c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M322.254 736c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-medium"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":505,"id":38,"name":"view-medium","prevSize":32,"code":59793},"setIdx":0,"setId":3,"iconIdx":134},{"icon":{"paths":["M206.090 672c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M334.090 576c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 480c35.346 0 64-28.655 64-64 0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.345 28.654 64 64 64z","M334.090 384c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 288c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M334.090 192c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 864c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M334.090 768c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-condensed"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":506,"id":37,"name":"view-condensed","prevSize":32,"code":59794},"setIdx":0,"setId":3,"iconIdx":135},{"icon":{"paths":["M288 192c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 288c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 304c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M288 464c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 560c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 576c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M288 736c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 832c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 848c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-extended"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":507,"id":36,"name":"view-extended","prevSize":32,"code":59795},"setIdx":0,"setId":3,"iconIdx":136},{"icon":{"paths":["M825.374 153.173c12.497-12.48 32.755-12.48 45.252 0s12.497 32.715 0 45.195l-671.999 671.115c-12.497 12.48-32.758 12.48-45.255 0s-12.497-32.717 0-45.197l672.002-671.113zM107.307 479.424c101.801-153.5 303.397-327.248 541.397-225.142l-49.51 49.446c-56.452-19.184-107.934-19.478-153.323-9.713-114.755 24.688-215.37 118.236-280.422 213.585 38.582 47.94 75.869 86.711 111.715 117.73l-45.303 45.244c-39.632-34.692-80.083-77.333-121.157-129.007-14.229-17.903-16.035-43.089-3.398-62.144zM794.227 349.994l-45.269 45.21c35.494 31.386 72.239 70.857 110.037 119.924-61.461 95.061-158.443 188.433-271.761 213.342-46.694 10.266-100.493 9.719-160.384-11.588l-49.266 49.203c244.342 107.191 442.802-69.069 540.065-223.957 11.657-18.564 9.882-42.598-3.392-60.049-40.439-53.163-80.533-96.801-120.030-132.084zM511.599 346.974c13.692 0 26.999 1.57 39.753 4.534l-203.471 203.201c-3.895-13.82-5.974-28.365-5.974-43.379 0-90.772 75.975-164.356 169.693-164.356zM675.494 468.57l-202.995 202.726c12.553 2.867 25.643 4.386 39.1 4.386 93.722 0 169.694-73.583 169.694-164.352 0-14.793-2.018-29.129-5.798-42.761z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["unread-on-top-disabled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":508,"id":35,"name":"unread-on-top-disabled","prevSize":32,"code":59796},"setIdx":0,"setId":3,"iconIdx":137},{"icon":{"paths":["M587.234 728.474c-107.23 23.573-251.957-9.865-421.785-220.868 65.051-95.352 165.667-188.9 280.422-213.588 108.169-23.272 250.953 10.59 413.124 221.111-61.461 95.061-158.443 188.433-271.761 213.346zM914.257 482.082c-347.132-456.361-668.857-210.879-806.95-2.658-12.638 19.055-10.832 44.241 3.398 62.144 362.307 455.817 676.142 208.862 806.945 0.563 11.657-18.569 9.882-42.598-3.392-60.049zM617.293 511.33c0-53.641-45.457-100.439-105.694-100.439s-105.693 46.799-105.693 100.439c0 53.636 45.456 100.437 105.693 100.437s105.694-46.801 105.694-100.437zM681.293 511.33c0 90.769-75.972 164.352-169.694 164.352-93.718 0-169.693-73.583-169.693-164.352 0-90.771 75.975-164.355 169.693-164.355 93.722 0 169.694 73.584 169.694 164.355z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["unread-on-top"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":509,"id":34,"name":"unread-on-top","prevSize":32,"code":59797},"setIdx":0,"setId":3,"iconIdx":138},{"icon":{"paths":["M544 208c0-17.673-14.323-32-32-32-17.673 0-32 14.327-32 32v271.996h-271.997c-17.673 0-32 14.327-32 32s14.327 32 32 32h271.997v272.004c0 17.673 14.327 32 32 32 17.677 0 32-14.327 32-32v-272.004h272.004c17.673 0 32-14.327 31.996-32 0-17.673-14.323-32-32-32h-272v-271.996z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["add"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":510,"id":33,"name":"add","prevSize":32,"code":59798},"setIdx":0,"setId":3,"iconIdx":139},{"icon":{"paths":["M806.626 262.627c12.497-12.497 12.497-32.758 0-45.255s-32.755-12.497-45.252 0l-249.374 249.371-249.373-249.371c-12.497-12.497-32.758-12.497-45.255 0s-12.497 32.758 0 45.255l249.371 249.373-249.371 249.374c-12.497 12.497-12.497 32.755 0 45.252s32.758 12.497 45.255 0l249.373-249.37 249.374 249.37c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-249.37-249.374 249.37-249.373z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["close"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":511,"id":32,"name":"close","prevSize":32,"code":59799},"setIdx":0,"setId":3,"iconIdx":140},{"icon":{"paths":["M416 128c-35.346 0-64 28.654-64 64v226.636c20.775-2.822 42.133-3.455 64-1.527v-225.11h176v180.706c0 35.346 28.655 64 64 64h176v331.294h-120.555c-17.156 22.178-36.322 43.87-57.31 64h177.865c35.345 0 64-28.655 64-64v-363.294c0-6.41-1.924-12.672-5.525-17.975l-156.634-230.682c-11.913-17.545-31.74-28.049-52.945-28.049h-264.896zM656 372.706v-180.706h24.896l122.697 180.706h-147.593zM165.234 668.544c120.524 156.143 218.24 174.588 285.134 158.741 73.293-17.361 139.23-81.647 183.462-151.761-115.153-155.87-211.58-174.588-279.148-158.916-74.361 17.25-142.706 81.685-189.449 151.936zM101.723 649.037c100.621-162.799 337.62-357.19 593.253 1.788 9.071 12.74 10.321 29.82 2.423 43.315-95.275 162.854-326.394 358.345-593.236-0.222-9.745-13.094-11.022-30.997-2.44-44.881zM398.961 735.991c31.512 0 60.269-26.854 60.269-64 0-37.141-28.757-64-60.269-64-31.514 0-60.268 26.859-60.268 64 0 37.146 28.755 64 60.268 64zM398.961 799.991c68.632 0 124.269-57.306 124.269-128 0-70.69-55.637-128-124.269-128s-124.268 57.31-124.268 128c0 70.694 55.637 128 124.268 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["auditing"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":512,"id":31,"name":"auditing","prevSize":32,"code":59800},"setIdx":0,"setId":3,"iconIdx":141},{"icon":{"paths":["M416 586.445v177.233l75.516-62.929c11.866-9.89 29.103-9.89 40.969 0l75.516 62.929v-177.233c-29.094 13.82-61.645 21.555-96 21.555s-66.906-7.735-96-21.555zM352 540.766v291.234c0 12.416 7.183 23.71 18.427 28.979 11.244 5.265 24.521 3.554 34.059-4.395l107.514-89.596 107.516 89.596c9.536 7.949 22.814 9.66 34.057 4.395 11.247-5.269 18.428-16.563 18.428-28.979v-291.234c39.59-40.401 64-95.731 64-156.766 0-123.712-100.288-224-224-224s-224 100.288-224 224c0 61.035 24.41 116.365 64 156.766zM416 512.013l-0.015-0.013c-38.854-29.193-63.985-75.661-63.985-128 0-88.366 71.634-160 160-160 88.367 0 160 71.634 160 160 0 52.34-25.131 98.808-63.983 128l-0.017 0.013c-26.739 20.083-59.981 31.987-96 31.987s-69.261-11.904-96-31.987z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["enterprise-feature"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":513,"id":30,"name":"enterprise-feature","prevSize":32,"code":59801},"setIdx":0,"setId":3,"iconIdx":142},{"icon":{"paths":["M749.756 660.215c16.887-16.435 17.259-43.447 0.823-60.335-16.431-16.887-43.443-17.259-60.335-0.823l59.511 61.158zM512 832.021l-29.756 30.579c16.563 16.115 42.948 16.115 59.511 0l-29.756-30.579zM333.754 599.057c-16.889-16.435-43.901-16.064-60.334 0.823s-16.064 43.9 0.825 60.335l59.509-61.158zM690.244 599.057l-208 202.385 59.511 61.158 208-202.385-59.511-61.158zM541.756 801.442l-208.001-202.385-59.509 61.158 207.999 202.385 59.511-61.158z","M749.756 363.805c16.887 16.433 17.259 43.445 0.823 60.334-16.431 16.889-43.443 17.26-60.335 0.826l59.511-61.16zM512 191.999l-29.756-30.58c16.563-16.116 42.948-16.116 59.511 0l-29.756 30.58zM333.754 424.965c-16.889 16.434-43.901 16.063-60.334-0.825s-16.064-43.901 0.825-60.334l59.509 61.159zM690.244 424.965l-208-202.386 59.511-61.159 208 202.386-59.511 61.16zM541.756 222.579l-208.001 202.386-59.509-61.159 207.999-202.386 59.511 61.159z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"isMulticolor":true,"isMulticolor2":true,"grid":0,"tags":["ordering-ascending"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"properties":{"order":514,"id":29,"name":"ordering-ascending","prevSize":32,"code":59802,"codes":[59802,59803]},"setIdx":0,"setId":3,"iconIdx":143},{"icon":{"paths":["M274.246 363.785c-16.889 16.433-17.258 43.445-0.825 60.334s43.445 17.259 60.334 0.825l-59.509-61.159zM512 191.978l29.756-30.58c-16.563-16.116-42.948-16.116-59.511 0l29.756 30.58zM690.244 424.944c16.892 16.434 43.904 16.063 60.335-0.825 16.435-16.889 16.064-43.901-0.823-60.334l-59.511 61.159zM333.754 424.944l208.001-202.386-59.511-61.16-207.999 202.386 59.509 61.159zM482.244 222.558l208 202.386 59.511-61.159-208-202.386-59.511 61.16z","M274.246 660.194c-16.889-16.431-17.258-43.443-0.825-60.335 16.433-16.887 43.445-17.259 60.334-0.823l-59.509 61.158zM512 832l29.756 30.579c-16.563 16.115-42.948 16.115-59.511 0l29.756-30.579zM690.244 599.036c16.892-16.435 43.904-16.064 60.335 0.823 16.435 16.892 16.064 43.904-0.823 60.335l-59.511-61.158zM333.754 599.036l208.001 202.385-59.511 61.158-207.999-202.385 59.509-61.158zM482.244 801.421l208-202.385 59.511 61.158-208 202.385-59.511-61.158z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"isMulticolor":true,"isMulticolor2":true,"grid":0,"tags":["ordering-descending"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"properties":{"order":515,"id":28,"name":"ordering-descending","prevSize":32,"code":59804,"codes":[59804,59805]},"setIdx":0,"setId":3,"iconIdx":144},{"icon":{"paths":["M448 192c-17.673 0-32 14.327-32 32v128c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-128c0-17.673-14.327-32-32-32h-128z","M416 448c0-17.673 14.327-32 32-32h128c17.673 0 32 14.327 32 32v128c0 17.673-14.327 32-32 32h-128c-17.673 0-32-14.327-32-32v-128z","M416 672c0-17.673 14.327-32 32-32h128c17.673 0 32 14.327 32 32v128c0 17.673-14.327 32-32 32h-128c-17.673 0-32-14.327-32-32v-128z"],"attrs":[{"fill":"rgb(245, 69, 92)"},{"fill":"rgb(245, 69, 92)"},{"fill":"rgb(245, 69, 92)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["priority-high"]},"attrs":[{"fill":"rgb(245, 69, 92)"},{"fill":"rgb(245, 69, 92)"},{"fill":"rgb(245, 69, 92)"}],"properties":{"order":516,"id":27,"name":"priority-high","prevSize":32,"code":59806},"setIdx":0,"setId":3,"iconIdx":145},{"icon":{"paths":["M448 416c-17.673 0-32 14.327-32 32v128c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-128c0-17.673-14.327-32-32-32h-128zM448 640c-17.673 0-32 14.327-32 32v128c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-128c0-17.673-14.327-32-32-32h-128z","M448 192h128c17.673 0 32 14.327 32 32v128c0 17.673-14.327 32-32 32h-128c-17.673 0-32-14.327-32-32v-128c0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(243, 190, 8)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["priority-medium"]},"attrs":[{"fill":"rgb(243, 190, 8)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":517,"id":26,"name":"priority-medium","prevSize":32,"code":59807,"codes":[59807,59808]},"setIdx":0,"setId":3,"iconIdx":146},{"icon":{"paths":["M448 640h128c17.673 0 32 14.327 32 32v128c0 17.673-14.327 32-32 32h-128c-17.673 0-32-14.327-32-32v-128c0-17.673 14.327-32 32-32z","M448 192c-17.673 0-32 14.327-32 32v128c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-128c0-17.673-14.327-32-32-32h-128zM448 416c-17.673 0-32 14.327-32 32v128c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-128c0-17.673-14.327-32-32-32h-128z"],"attrs":[{"fill":"rgb(45, 224, 165)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["priority-low"]},"attrs":[{"fill":"rgb(45, 224, 165)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":518,"id":25,"name":"priority-low","prevSize":32,"code":59809,"codes":[59809,59810]},"setIdx":0,"setId":3,"iconIdx":147},{"icon":{"paths":["M768 224c17.673 0 32 14.327 32 32v544c0 17.673-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32v-128c17.673 0 32-14.327 32-32s-14.327-32-32-32v-192c17.673 0 32-14.327 32-32s-14.327-32-32-32v-96c0-17.673 14.327-32 32-32h544zM128 608c-17.673 0-32 14.327-32 32s14.327 32 32 32v128c0 53.018 42.981 96 96 96h544c53.018 0 96-42.982 96-96v-544c0-53.019-42.982-96-96-96h-544c-53.019 0-96 42.981-96 96v96c-17.673 0-32 14.327-32 32s14.327 32 32 32v192zM425.205 514.266c13.315-3.567 27.309-3.789 40.732-0.644l31.518 7.39c8.73 2.044 17.83 1.903 26.492-0.418l22.182-5.943c14.289-3.823 29.329-4.036 43.682-0.674 40.431 9.476 69.325 45.585 69.325 87.279v24.337c0 31.812-25.788 57.6-57.6 57.6h-180.736c-31.812 0-57.6-25.788-57.6-57.6v-30.528c0-37.862 25.432-71.002 62.005-80.798zM454.251 563.473c-5.205-1.22-10.633-1.135-15.799 0.247-14.186 3.802-24.051 16.657-24.051 31.343v30.528c0 3.533 2.865 6.4 6.4 6.4h180.736c3.537 0 6.4-2.867 6.4-6.4v-24.337c0-17.749-12.365-33.34-29.807-37.427-6.182-1.451-12.659-1.353-18.752 0.282l-22.187 5.943c-16.811 4.501-34.475 4.779-51.418 0.806l-31.522-7.386zM536.448 420.48c0-13.962-11.315-25.28-25.28-25.28-13.961 0-25.28 11.319-25.28 25.28 0 13.96 11.319 25.28 25.28 25.28 13.965 0 25.28-11.319 25.28-25.28zM587.648 420.48c0 42.24-34.24 76.48-76.48 76.48s-76.48-34.24-76.48-76.48c0-42.239 34.24-76.48 76.48-76.48s76.48 34.242 76.48 76.48z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["contacts"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":519,"id":24,"name":"contacts","prevSize":32,"code":59811},"setIdx":0,"setId":3,"iconIdx":148},{"icon":{"paths":["M302.769 192h418.464l23.185 72.727h-0.213l23.795 64c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818c0-18.477-14.327-33.455-32-33.455s-32 14.978-32 33.455c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818c0-18.477-14.327-33.455-32-33.455s-32 14.978-32 33.455c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818l23.793-64h-0.213l23.188-72.727zM212.406 264.727l36.491-114.448c4.231-13.27 16.56-22.279 30.488-22.279h465.23c13.931 0 26.257 9.009 30.49 22.279l56.896 178.448c0 33.937-12.083 64.925-32 88.515v350.775c0 53.022-42.982 96-96 96h-384c-53.019 0-96-42.978-96-96v-350.775c-19.916-23.59-32-54.577-32-88.515l20.406-64zM288 458.33v309.687c0 17.673 14.327 32 32 32h128v-192.017h128v192.017h128c17.673 0 32-14.327 32-32v-309.687c-10.227 2.752-20.949 4.215-32 4.215-38.229 0-72.546-17.523-96-45.303-23.454 27.78-57.771 45.303-96 45.303s-72.546-17.523-96-45.303c-23.454 27.78-57.77 45.303-96 45.303-11.049 0-21.772-1.463-32-4.215z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["marketplace"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":520,"id":23,"name":"marketplace","prevSize":32,"code":59812},"setIdx":0,"setId":3,"iconIdx":149},{"icon":{"paths":["M224 224c-17.673 0-32 14.327-32 32v544c0 17.673 14.327 32 32 32h544c17.673 0 32-14.327 32-32v-544c0-17.673-14.327-32-32-32h-544zM128 256c0-53.019 42.981-96 96-96h544c53.018 0 96 42.981 96 96v544c0 53.018-42.982 96-96 96h-544c-53.019 0-96-42.982-96-96v-544zM608 460.813c0 41.801-26.714 77.359-64 90.539v133.461h-64v-133.461c-37.286-13.18-64-48.738-64-90.539 0-53.020 42.982-96 96-96s96 42.981 96 96zM608 588.826c38.861-29.193 64-75.665 64-128.013 0-88.366-71.633-160-160-160-88.366 0-160 71.635-160 160 0 52.348 25.138 98.82 64 128.013v127.987c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-127.987z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["auth"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":521,"id":22,"name":"auth","prevSize":32,"code":59813},"setIdx":0,"setId":3,"iconIdx":150},{"icon":{"paths":["M623.091 896c-2.803 0-5.299-0.623-7.168-0.939-61.999-16.858-102.818-39.646-145.813-80.858-55.147-54.007-85.369-125.815-85.369-202.3 0-65.873 56.705-119.573 126.495-119.573 69.794 0 126.498 53.7 126.498 119.573 0 34.965 31.467 63.373 69.79 63.373s69.794-28.407 69.794-63.373c0-135.808-119.646-246.636-266.705-246.636-104.997 0-200.337 57.132-243.334 145.793-14.332 29.35-21.498 63.066-21.498 100.843 0 28.719 2.493 73.365 24.925 131.746 2.804 7.181 2.493 14.673-0.623 21.854-3.116 6.865-9.036 11.861-16.201 14.357-2.804 1.25-6.231 1.562-9.658 1.562-11.84 0-22.433-7.181-26.484-18.108-19.005-50.261-28.352-99.9-28.352-151.727 0-46.204 9.036-88.35 26.795-125.5 52.343-108.020 167.935-177.64 294.431-177.64 178.53 0 323.721 135.805 323.721 302.828 0 65.873-56.704 119.573-126.81 119.573-70.101 0-126.805-53.7-126.805-119.573 0-34.965-31.471-63.373-69.794-63.373-38.635 0-69.79 28.407-69.79 63.373 0 61.504 24.303 119.262 68.544 162.342 35.209 34.031 68.855 53.073 120.576 67.123 7.168 1.873 13.397 6.554 17.139 13.111 3.738 6.558 4.672 14.362 2.803 21.231-2.803 11.861-14.020 20.915-27.106 20.915zM426.803 888.196c-7.79 0-15.267-3.123-20.253-8.742-33.338-32.781-51.72-54.012-77.892-100.527-26.795-47.142-41.127-105.212-41.127-167.339 0-116.446 100.948-211.354 224.95-211.354 124.006 0 224.956 94.908 224.956 211.354 0 15.612-12.463 28.1-28.356 28.1-15.889 0-28.663-12.177-28.663-28.1 0-85.538-75.401-155.157-168.247-155.157-92.846 0-168.245 69.619-168.245 155.157 0 52.45 11.84 100.527 34.272 139.554 23.368 41.519 38.946 59.004 68.855 88.661 10.906 11.238 10.906 28.723 0 39.65-5.918 5.931-13.086 8.742-20.25 8.742zM699.738 818.889c-47.36 0-88.798-11.866-123.383-34.965-59.196-39.65-94.716-103.962-94.716-172.335 0-15.607 12.463-28.096 28.352-28.096s28.352 12.489 28.352 28.096c0 49.643 26.172 96.781 69.794 125.504 25.237 16.858 55.147 24.977 91.601 24.977 7.787 0 22.43-0.939 38.012-3.746 1.557-0.311 3.426-0.311 4.983-0.311 13.709 0 25.237 9.988 27.729 23.411 1.246 7.181-0.311 14.673-4.361 20.608-4.365 6.242-10.906 10.611-18.697 11.861-23.364 4.685-43.93 4.996-47.667 4.996zM188.765 435.823c-5.608 0-11.216-1.562-16.201-4.992-6.543-4.062-10.593-10.616-12.151-18.109-1.246-7.493 0.311-14.985 4.985-21.229 38.634-53.698 87.862-95.844 146.125-125.502 60.133-30.595 129.612-46.829 200.96-46.829 71.040 0 140.207 15.922 200.030 46.205 58.573 29.658 107.802 71.493 146.125 124.566 4.361 5.932 6.229 13.425 4.983 20.917s-5.606 14.049-11.84 18.42c-4.983 3.435-10.594 4.992-16.512 4.992-9.037 0-17.758-4.369-23.057-11.861-33.335-45.893-75.712-82.107-125.559-107.083-52.343-26.224-112.789-40.273-174.481-40.273-62.31 0-122.756 14.049-175.1 40.585-49.851 25.912-92.535 62.127-126.185 108.644-3.739 6.869-12.463 11.55-22.121 11.55zM733.696 239.142c-4.672 0-9.344-1.249-13.397-3.434-71.347-35.902-133.35-51.512-207.505-51.512-74.462 0-144.254 17.483-207.814 51.825-4.050 2.185-8.724 3.122-13.397 3.122-10.282 0-19.629-5.62-24.925-14.361-3.739-6.556-4.674-14.361-2.493-21.542s7.166-13.424 13.709-16.858c72.596-38.712 151.733-58.38 234.921-58.38 82.569 0 154.85 17.795 233.988 58.068 6.857 3.434 11.84 9.366 14.332 16.858 2.185 7.18 1.246 14.673-2.18 21.229-4.983 9.053-14.643 14.985-25.237 14.985z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["fingerprint"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":522,"id":21,"name":"fingerprint","prevSize":32,"code":59814},"setIdx":0,"setId":3,"iconIdx":151},{"icon":{"paths":["M737.779 361.376c12.497 12.497 12.497 32.758 0 45.255l-110.946 110.95 110.946 110.946c12.497 12.497 12.497 32.759 0 45.257s-32.759 12.497-45.252 0l-110.95-110.95-110.95 110.95c-12.497 12.497-32.755 12.497-45.254 0-12.497-12.497-12.497-32.759 0-45.257l110.947-110.946-110.947-110.95c-12.497-12.497-12.497-32.758 0-45.255 12.498-12.497 32.756-12.497 45.254 0l110.95 110.948 110.95-110.948c12.493-12.497 32.755-12.497 45.252 0z","M312.247 218.073c12.061-16.393 31.199-26.073 51.551-26.073h468.202c35.345 0 64 28.654 64 64v512c0 35.345-28.655 64-64 64h-468.202c-20.352 0-39.491-9.681-51.551-26.074l-188.343-256c-16.598-22.562-16.598-53.291 0-75.853l188.343-256zM363.798 256l-188.343 256 188.343 256h468.202v-512h-468.202z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["backspace"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":523,"id":20,"name":"backspace","prevSize":32,"code":59815},"setIdx":0,"setId":3,"iconIdx":152},{"icon":{"paths":["M208 176c-35.346 0-64 28.654-64 64v576c0 35.345 28.654 64 64 64h576c35.345 0 64-28.655 64-64v-576c0-35.346-28.655-64-64-64h-576zM208 240h576v576h-576v-576zM698.628 558.153c-0.192 17.673-14.669 31.842-32.341 31.654s-31.846-14.669-31.659-32.341l1.323-123.588-309.344 308.77c-12.508 12.484-32.77 12.467-45.255-0.043-12.485-12.506-12.466-32.768 0.042-45.252l309.199-308.627-123.388 1.318c-17.673 0.189-32.149-13.984-32.337-31.657-0.192-17.672 13.982-32.151 31.654-32.34l201.92-2.156c8.606-0.092 16.887 3.286 22.972 9.371s9.459 14.364 9.37 22.969l-2.155 201.922z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["new-window"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":524,"id":19,"name":"new-window","prevSize":32,"code":59816},"setIdx":0,"setId":3,"iconIdx":153},{"icon":{"paths":["M464 318.068c0-51.548-42.408-94.366-96-94.366s-96 42.819-96 94.366c0 51.548 42.408 94.367 96 94.367s96-42.819 96-94.367zM528 318.068c0 87.417-71.633 158.284-160 158.284-88.366 0-160-70.867-160-158.284s71.634-158.282 160-158.282c88.367 0 160 70.865 160 158.282zM281.778 525.035c-27.649-7.693-56.976-7.159-84.32 1.536-60.46 19.226-101.459 74.863-101.459 137.685v103.633c0 52.45 42.981 94.967 96 94.967h352c3.605 0 7.164-0.196 10.667-0.576v-64.538c-3.337 1.169-6.925 1.801-10.667 1.801h-352c-17.673 0-32-14.17-32-31.654v-103.633c0-35.319 23.051-66.603 57.043-77.41 15.374-4.89 31.862-5.188 47.407-0.866l48.489 13.491c36.311 10.103 74.826 9.404 110.737-2.018l30.1-9.57c17.502-5.564 36.275-5.905 53.978-0.981 18.901 5.257 35.063 16.038 46.912 30.281v-79.714c-9.31-4.749-19.204-8.627-29.585-11.516-29.803-8.29-61.414-7.714-90.889 1.655l-30.101 9.57c-23.941 7.616-49.617 8.081-73.825 1.348l-48.489-13.491zM763.366 489.033c-12.326-12.651-32.585-12.919-45.248-0.61s-12.937 32.542-0.61 45.193l102.839 105.506h-324.348c-17.673 0-32 14.31-32 31.962 0 17.647 14.327 31.957 32 31.957h324.348l-102.839 105.51c-12.326 12.646-12.053 32.879 0.61 45.188 12.663 12.314 32.922 12.041 45.248-0.606l155.721-159.761c12.092-12.407 12.092-32.175 0-44.582l-155.721-159.757z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-forward"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":525,"id":18,"name":"user-forward","prevSize":32,"code":59817},"setIdx":0,"setId":3,"iconIdx":154},{"icon":{"paths":["M320 159.791c0-17.649-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.916c0 17.65 14.327 31.958 32 31.958h64c17.673 0 32-14.308 32-31.958v-63.916zM320 585.894c0-17.651-14.327-31.957-32-31.957h-64c-17.673 0-32 14.306-32 31.957v63.915c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.915zM448 159.791c0-17.649 14.327-31.958 32-31.958h64c17.673 0 32 14.308 32 31.958v63.916c0 17.65-14.327 31.958-32 31.958h-64c-17.673 0-32-14.308-32-31.958v-63.916zM576 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914zM448 585.894c0-17.651 14.327-31.957 32-31.957h64c17.673 0 32 14.306 32 31.957v63.915c0 17.651-14.327 31.957-32 31.957h-64c-17.673 0-32-14.306-32-31.957v-63.915zM576 798.946c0-17.651-14.327-31.957-32-31.957h-64c-17.673 0-32 14.306-32 31.957v63.915c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.915zM704 159.791c0-17.649 14.327-31.958 32-31.958h64c17.673 0 32 14.308 32 31.958v63.916c0 17.65-14.327 31.958-32 31.958h-64c-17.673 0-32-14.308-32-31.958v-63.916zM832 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914zM704 585.894c0-17.651 14.327-31.957 32-31.957h64c17.673 0 32 14.306 32 31.957v63.915c0 17.651-14.327 31.957-32 31.957h-64c-17.673 0-32-14.306-32-31.957v-63.915zM320 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["dialpad"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":526,"id":17,"name":"dialpad","prevSize":32,"code":59818},"setIdx":0,"setId":3,"iconIdx":155},{"icon":{"paths":["M482.325 229.047c19.767-23.931 49.89-36.827 80.849-34.615l143.936 10.281c42.052 3.004 77.222 33.091 86.703 74.17l30.349 131.517c6.643 28.785-0.337 59.028-18.923 81.991l-252.369 311.744c-11.119 13.739-31.27 15.859-45.005 4.74-13.734-11.123-15.859-31.27-4.736-45.009l252.365-311.744c6.195-7.654 8.521-17.737 6.306-27.331l-30.349-131.517c-3.157-13.693-14.882-23.722-28.898-24.723l-143.936-10.281c-10.321-0.737-20.361 3.561-26.953 11.538l-265.999 322c-5.438 6.583-8.020 15.066-7.17 23.565l7.581 75.81c1.636 16.358 15.401 28.817 31.841 28.817h104.305c9.439 0 18.397-4.169 24.476-11.388l227.597-270.268-10.052-70.344h-69.257l-230.406 276.488c-11.314 13.577-31.492 15.411-45.069 4.096s-15.412-31.492-4.097-45.069l230.403-276.487c12.16-14.592 30.174-23.028 49.169-23.028h69.257c31.851 0 58.854 23.419 63.356 54.949l10.052 70.345c2.577 18.044-2.662 36.331-14.404 50.274l-227.593 270.268c-18.24 21.662-45.116 34.163-73.432 34.163h-104.305c-49.321 0-90.616-37.372-95.524-86.447l-7.581-75.814c-2.549-25.489 5.196-50.944 21.511-70.694l266-321.997z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["attach"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":527,"id":16,"name":"attach","prevSize":32,"code":59819},"setIdx":0,"setId":3,"iconIdx":156},{"icon":{"paths":["M896 512h-63.987c0-175.415-145.749-320-328.516-320-115.27 0-215.817 57.513-274.36 144h110.768c17.673 0 32 14.327 32 32s-14.327 32-32 32h-179.904c-17.673 0-32-14.327-32-32v-192c0-17.673 14.327-32 32-32s32 14.327 32 32v102.32c71.751-91.401 184.591-150.32 311.497-150.32 216.781 0 392.503 171.923 392.503 384 0 1.114 0.009 2.227 0 3.337v-3.337z","M127.997 512h63.986c0 175.415 145.751 320 328.52 320 115.268 0 215.817-57.515 274.359-144h-110.767c-17.677 0-32-14.327-32-32s14.323-32 32-32h179.904c17.673 0 32 14.327 32 32v192c0 17.673-14.327 32-32 32s-32-14.327-32-32v-102.319c-71.753 91.401-184.593 150.319-311.497 150.319-216.783 0-392.506-171.921-392.506-384 0-1.084-0.009-2.163 0-3.247v3.247z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["refresh"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":528,"id":15,"name":"refresh","prevSize":32,"code":59820},"setIdx":0,"setId":3,"iconIdx":157},{"icon":{"paths":["M512 832c176.73 0 320-143.27 320-320 0-176.731-143.27-320-320-320-111.713 0-210.056 57.245-267.295 144h107.295c17.673 0 32 14.327 32 32s-14.327 32-32 32h-176c-17.673 0-32-14.327-32-32v-192c0-17.673 14.327-32 32-32s32 14.327 32 32v101.364c70.228-90.856 180.282-149.364 304-149.364 212.079 0 384 171.923 384 384 0 212.079-171.921 384-384 384-212.077 0-384-171.921-384-384h64c0 176.73 143.269 320 320 320z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["undo"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":529,"id":14,"name":"undo","prevSize":32,"code":59821},"setIdx":0,"setId":3,"iconIdx":158},{"icon":{"paths":["M553.412 325.606c0 91.58-75.849 165.821-169.412 165.821s-169.412-74.241-169.412-165.821c0-91.579 75.848-165.819 169.412-165.819s169.412 74.24 169.412 165.819zM485.649 325.606c0-54.947-45.513-99.491-101.649-99.491-56.138 0-101.647 44.544-101.647 99.491s45.509 99.492 101.647 99.492c56.137 0 101.649-44.544 101.649-99.492z","M203.427 510.558c28.952-9.109 60.004-9.668 89.279-1.613l51.342 14.135c25.632 7.057 52.819 6.566 78.167-1.408l31.873-10.027c31.206-9.822 64.678-10.423 96.23-1.737 71.962 19.81 121.681 84.049 121.681 157.218v96.239c0 54.946-45.508 99.49-101.649 99.49h-372.704c-56.138 0-101.647-44.544-101.647-99.49v-108.565c0-65.818 43.411-124.105 107.427-144.243zM274.359 572.796c-16.46-4.531-33.918-4.215-50.196 0.905-35.992 11.324-60.399 44.096-60.399 81.101v108.565c0 18.317 15.169 33.165 33.882 33.165h372.704c18.714 0 33.886-14.848 33.886-33.165v-96.239c0-43.452-29.53-81.604-72.265-93.367-18.739-5.158-38.618-4.8-57.148 1.028l-31.872 10.031c-38.024 11.959-78.804 12.698-117.251 2.112l-51.342-14.135z","M797.090 188.646c0-15.913-13.022-28.812-29.090-28.812s-29.090 12.9-29.090 28.812v100.844h-101.82c-16.064 0-29.090 12.9-29.090 28.812s13.026 28.813 29.090 28.813h101.82v100.842c0 15.915 13.022 28.813 29.090 28.813s29.090-12.898 29.090-28.813v-100.842h101.82c16.064 0 29.090-12.9 29.090-28.813s-13.026-28.812-29.090-28.812h-101.82v-100.844z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-add"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":530,"id":13,"name":"user-add","prevSize":32,"code":59822},"setIdx":0,"setId":3,"iconIdx":159},{"icon":{"paths":["M608 192c0-53.019-42.982-96-96-96s-96 42.981-96 96h-192c-17.673 0-32 14.327-32 32v672c0 17.673 14.327 32 32 32h576c17.673 0 32-14.327 32-32v-672c0-17.673-14.327-32-32-32h-192zM256 864v-608h96v64c0 17.673 14.327 32 32 32h256c17.673 0 32-14.327 32-32v-64h96v608h-512zM512 224c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-32 14.327-32 32s14.327 32 32 32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clipboard"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":531,"id":12,"name":"clipboard","prevSize":32,"code":59823},"setIdx":0,"setId":3,"iconIdx":160},{"icon":{"paths":["M341.335 778.671c-17.673 0-32 14.327-32 32s14.327 32 32 32h341.332c17.673 0 32-14.327 32-32s-14.327-32-32-32h-341.332zM85.335 298.672c0-70.692 57.308-128 128-128h597.332c70.694 0 128 57.308 128 128v298.666c0 70.694-57.306 128-128 128h-597.332c-70.693 0-128-57.306-128-128v-298.666zM213.335 234.672c-35.346 0-64 28.654-64 64v298.666c0 35.349 28.654 64 64 64h597.332c35.349 0 64-28.655 64-64v-298.666c0-35.346-28.651-64-64-64h-597.332zM256 320c0-11.782 9.551-21.333 21.333-21.333h170.667c11.78 0 21.333 9.551 21.333 21.333s-9.553 21.333-21.333 21.333h-170.667c-11.782 0-21.333-9.551-21.333-21.333zM277.333 554.667c-11.782 0-21.333 9.553-21.333 21.333s9.551 21.333 21.333 21.333h85.333c11.782 0 21.333-9.553 21.333-21.333s-9.551-21.333-21.333-21.333h-85.333zM554.667 320c0-11.782 9.553-21.333 21.333-21.333h170.667c11.78 0 21.333 9.551 21.333 21.333s-9.553 21.333-21.333 21.333h-170.667c-11.78 0-21.333-9.551-21.333-21.333zM448 554.667c-11.78 0-21.333 9.553-21.333 21.333s9.553 21.333 21.333 21.333h298.667c11.78 0 21.333-9.553 21.333-21.333s-9.553-21.333-21.333-21.333h-298.667zM256 448c0-11.78 9.551-21.333 21.333-21.333h213.333c11.78 0 21.333 9.553 21.333 21.333s-9.553 21.333-21.333 21.333h-213.333c-11.782 0-21.333-9.553-21.333-21.333zM618.667 426.667c-11.78 0-21.333 9.553-21.333 21.333s9.553 21.333 21.333 21.333h128c11.78 0 21.333-9.553 21.333-21.333s-9.553-21.333-21.333-21.333h-128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["log-view"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":532,"id":11,"name":"log-view","prevSize":32,"code":59824},"setIdx":0,"setId":3,"iconIdx":161},{"icon":{"paths":["M341.335 778.671c-17.673 0-32 14.327-32 32s14.327 32 32 32h341.332c17.673 0 32-14.327 32-32s-14.327-32-32-32h-341.332z","M85.335 298.672c0-70.692 57.308-128 128-128h597.332c70.694 0 128 57.308 128 128v298.666c0 70.694-57.306 128-128 128h-597.332c-70.693 0-128-57.306-128-128v-298.666zM213.335 234.672c-35.346 0-64 28.654-64 64v298.666c0 35.349 28.654 64 64 64h597.332c35.349 0 64-28.655 64-64v-298.666c0-35.346-28.651-64-64-64h-597.332z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["desktop"]},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":533,"id":10,"name":"desktop","prevSize":32,"code":59825},"setIdx":0,"setId":3,"iconIdx":162},{"icon":{"paths":["M403.11 199.401c17.543-17.543 45.987-17.543 63.531 0 17.54 17.543 17.54 45.987 0 63.53l-43.66 43.659 177.83 177.827 60.339-60.338c24.994-24.994 65.515-24.994 90.509 0l45.257 45.256-331.87 331.87-45.255-45.257c-24.994-24.994-24.994-65.515 0-90.509l60.338-60.339-177.827-177.83-43.66 43.661c-17.543 17.545-45.987 17.545-63.53 0s-17.543-45.986 0-63.529l207.999-207.999zM149.856 362.145c-42.537 42.537-42.537 111.502 0 154.041 42.001 42.001 109.771 42.53 152.421 1.583l87.335 87.339-15.077 15.074c-49.987 49.988-49.987 131.034 0 181.022l67.884 67.878c12.497 12.497 32.755 12.497 45.252 0l167.936-167.932 88.013 92.002c12.215 12.77 32.474 13.222 45.244 1.003 12.77-12.215 13.218-32.474 1.003-45.244l-88.994-93.026 163.925-163.925c12.493-12.497 12.493-32.759 0-45.257l-67.883-67.88c-49.988-49.987-131.034-49.987-181.022 0l-15.906 15.907-87.322-87.322c41.762-42.596 41.506-110.984-0.772-153.262-42.534-42.537-111.502-42.537-154.038 0l-207.999 207.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":534,"id":9,"name":"pin","prevSize":32,"code":59826},"setIdx":0,"setId":3,"iconIdx":163},{"icon":{"paths":["M593.937 170.244c-20.864-57.726-103.155-55.816-121.318 2.816l-58.978 190.35h-221.512c-58.947 0-86.549 72.943-42.37 111.965l157.591 139.204-53.209 216.93c-14.17 57.771 51.25 101.935 99.535 67.2l177.739-127.881 177.741 127.881c48.286 34.735 113.707-9.429 99.537-67.2l-53.389-217.651 147.891-139.968c42.031-39.778 13.879-110.481-43.994-110.481h-195.439l-69.824-193.165z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star-filled"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":535,"id":8,"name":"star-filled","prevSize":32,"code":59827},"setIdx":0,"setId":3,"iconIdx":164},{"icon":{"paths":["M472.619 172.834c18.163-58.554 100.454-60.462 121.318-2.812l69.824 192.91h195.439c57.873 0 86.025 70.608 43.994 110.335l-147.891 139.785 53.389 217.361c14.17 57.694-51.251 101.803-99.537 67.11l-177.741-127.706-177.739 127.706c-48.284 34.692-113.705-9.417-99.535-67.11l53.209-216.64-157.591-139.021c-44.179-38.976-16.577-111.82 42.37-111.82h221.512l58.978-190.098zM603.571 384.66l-69.82-192.91-58.978 190.098c-8.303 26.758-33.084 44.998-61.133 44.998h-221.512l157.591 139.025c17.834 15.731 25.456 40.047 19.787 63.125l-53.209 216.644 177.74-127.706c22.332-16.047 52.425-16.047 74.756 0l177.741 127.706-53.389-217.361c-5.478-22.319 1.455-45.854 18.167-61.649l147.887-139.785h-195.439c-26.957 0-51.025-16.868-60.19-42.186z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":536,"id":7,"name":"star","prevSize":32,"code":59828},"setIdx":0,"setId":3,"iconIdx":165},{"icon":{"paths":["M708.723 449.771c0-142.502-115.674-258.022-258.364-258.022-142.687 0-258.359 115.52-258.359 258.022s115.673 258.022 258.359 258.022c142.69 0 258.364-115.52 258.364-258.022zM654.379 699.042c-55.573 45.419-126.609 72.666-204.019 72.666-178.033 0-322.359-144.137-322.359-321.937s144.326-321.937 322.359-321.937c178.035 0 322.364 144.137 322.364 321.937 0 77.303-27.281 148.245-72.755 203.742l186.59 186.347c12.591 12.574 12.591 32.956 0 45.53-12.587 12.574-32.998 12.574-45.589 0l-186.59-186.347z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["search"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":537,"id":6,"name":"search","prevSize":32,"code":59829},"setIdx":0,"setId":3,"iconIdx":166},{"icon":{"paths":["M336 304c0-97.202 78.798-176 176-176 97.203 0 176 78.798 176 176v142.477h16c53.018 0 96 42.978 96 96v257.523c0 53.018-42.982 96-96 96h-384c-53.019 0-96-42.982-96-96v-257.523c0-53.022 42.981-96 96-96h16v-142.477zM400 446.477h224v-142.477c0-61.856-50.146-112-112-112s-112 50.144-112 112v142.477zM320 510.477c-17.673 0-32 14.327-32 32v257.523c0 17.673 14.327 32 32 32h384c17.673 0 32-14.327 32-32v-257.523c0-17.673-14.327-32-32-32h-384z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["channel-private"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":538,"id":5,"name":"channel-private","prevSize":32,"code":59830},"setIdx":0,"setId":3,"iconIdx":167},{"icon":{"paths":["M512 864c-194.404 0-352-157.598-352-352 0-194.404 157.596-352 352-352 194.406 0 352 157.596 352 352 0 194.402-157.594 352-352 352zM512 928c229.751 0 416-186.249 416-416s-186.249-416-416-416c-229.751 0-416 186.249-416 416s186.249 416 416 416zM662.626 361.373c12.497 12.497 12.497 32.758 0 45.255l-105.37 105.373 105.37 105.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-105.374-105.37-105.373 105.37c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l105.371-105.374-105.371-105.373c-12.497-12.497-12.497-32.758 0-45.255s32.758-12.497 45.255 0l105.373 105.371 105.374-105.371c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["input-clear"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":539,"id":4,"name":"input-clear","prevSize":32,"code":59831},"setIdx":0,"setId":3,"iconIdx":168},{"icon":{"paths":["M875.273 512c0-86.89-31.484-166.427-83.665-227.826l-496.163 496.161c61.399 52.181 140.937 83.665 227.828 83.665 194.402 0 352-157.598 352-352zM250.62 734.652l495.3-495.302c-60.659-49.597-138.18-79.349-222.647-79.349-194.406 0-352.002 157.596-352.002 352 0 84.467 29.752 161.988 79.349 222.652zM939.273 512c0 229.751-186.253 416-416 416-229.752 0-416.002-186.249-416.002-416s186.249-416 416.002-416c229.747 0 416 186.249 416 416z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["ban"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":540,"id":3,"name":"ban","prevSize":32,"code":59832},"setIdx":0,"setId":3,"iconIdx":169},{"icon":{"paths":["M368 412.435c-53.592 0-96-42.819-96-94.367s42.408-94.366 96-94.366c53.592 0 96 42.819 96 94.366s-42.408 94.367-96 94.367zM368 476.352c88.367 0 160-70.867 160-158.284s-71.633-158.282-160-158.282c-88.366 0-160 70.865-160 158.282s71.634 158.284 160 158.284zM713.6 396.606c-35.921 0-64-28.646-64-62.71s28.079-62.71 64-62.71c35.921 0 64 28.646 64 62.71s-28.079 62.71-64 62.71zM713.6 460.523c70.69 0 128-56.693 128-126.626s-57.31-126.626-128-126.626c-70.694 0-128 56.692-128 126.626s57.306 126.626 128 126.626zM197.459 526.571c27.344-8.695 56.671-9.229 84.32-1.536l48.489 13.491c24.207 6.733 49.884 6.268 73.824-1.348l30.101-9.57c29.474-9.37 61.086-9.946 90.889-1.655 67.964 18.91 114.918 80.226 114.918 150.071v91.866c0 52.45-42.982 94.967-96 94.967h-352c-53.019 0-96-42.517-96-94.967v-103.633c0-62.822 40.999-118.46 101.459-137.685zM264.451 585.98c-15.545-4.322-32.034-4.023-47.407 0.866-33.993 10.807-57.044 42.091-57.044 77.41v103.633c0 17.485 14.327 31.654 32 31.654h352c17.673 0 32-14.17 32-31.654v-91.866c0-41.476-27.887-77.892-68.245-89.122-17.702-4.924-36.476-4.582-53.978 0.981l-30.1 9.57c-35.91 11.422-74.426 12.122-110.737 2.018l-48.489-13.491zM691.2 777.685h140.8c53.018 0 96-42.923 96-95.872v-30.571c0-56.802-38.613-106.351-93.747-120.294-21.397-5.414-43.853-5.035-65.058 1.092l-16.397 4.745c-21.845 6.31-44.975 6.703-67.017 1.126l-29.807-7.539c-19.908-5.035-40.798-4.685-60.523 1.015-1.062 0.311-2.121 0.631-3.174 0.969 10.714 4.045 20.898 9.527 30.255 16.354l2.428 1.771 15.040 13.193c9.937 8.713 18.142 19.221 24.183 30.972l2.206 4.292 3.678 0.934c33.062 8.363 67.759 7.778 100.523-1.694l16.401-4.74c10.283-2.974 21.171-3.157 31.548-0.529 26.735 6.763 45.461 30.788 45.461 58.334v30.571c0 17.651-14.327 31.957-32 31.957h-140.8v63.915z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["team"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":541,"id":2,"name":"team","prevSize":32,"code":59833},"setIdx":0,"setId":3,"iconIdx":170},{"icon":{"paths":["M608 336c0-53.019-42.982-96-96-96s-96 42.981-96 96c0 53.019 42.982 96 96 96s96-42.981 96-96zM672 336c0 88.366-71.633 160-160 160-88.366 0-160-71.634-160-160s71.634-160 160-160c88.367 0 160 71.634 160 160zM412.451 551.799c-24.621-5.769-50.286-5.363-74.713 1.178-67.087 17.971-113.738 78.763-113.738 148.215v66.807c0 53.018 42.981 96 96 96h384c53.018 0 96-42.982 96-96v-58.53c0-74.3-51.149-138.825-123.49-155.78l-6.455-1.51c-25.673-6.020-52.437-5.598-77.909 1.225l-49.626 13.295c-20.378 5.457-41.788 5.794-62.327 0.981l-67.741-15.881zM354.297 614.797c14.238-3.81 29.199-4.049 43.55-0.683l67.744 15.876c30.805 7.219 62.921 6.716 93.487-1.472l49.626-13.295c15.283-4.092 31.343-4.343 46.746-0.734l6.455 1.51c43.405 10.176 74.095 48.887 74.095 93.47v58.53c0 17.673-14.327 32-32 32h-384c-17.673 0-32-14.327-32-32v-66.807c0-40.486 27.193-75.921 66.297-86.396z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user"]},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":542,"id":1,"name":"user","prevSize":32,"code":59834},"setIdx":0,"setId":3,"iconIdx":171}],"height":1024,"metadata":{"name":"custom"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"custom","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"noie8":true,"ie7":false,"showSelector":false,"showMetrics":false,"showMetadata":false,"showVersion":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16}}
\ No newline at end of file
+{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M537.267 295.385c33.135 0 74.667-22.134 99.401-51.645 22.4-26.745 38.733-64.095 38.733-101.445 0-5.072-0.465-10.144-1.399-14.295-36.868 1.383-81.199 24.439-107.802 55.334-21.001 23.517-40.132 60.406-40.132 98.218 0 5.533 0.934 11.066 1.399 12.911 2.334 0.461 6.067 0.922 9.801 0.922zM420.601 853.333c45.268 0 65.334-29.973 121.799-29.973 57.399 0 70.003 29.052 120.401 29.052 49.468 0 82.598-45.188 113.865-89.455 35.004-50.726 49.468-100.527 50.402-102.831-3.268-0.922-98.001-39.194-98.001-146.633 0-93.146 74.667-135.108 78.869-138.336-49.468-70.089-124.604-71.934-145.135-71.934-55.535 0-100.8 33.2-129.267 33.2-30.801 0-71.398-31.356-119.466-31.356-91.467 0-184.333 74.701-184.333 215.802 0 87.612 34.533 180.297 77 240.239 36.4 50.722 68.133 92.224 113.867 92.224z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["apple-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":664,"id":181,"name":"apple-monochromatic","prevSize":32,"code":59749},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M544 208c0-17.673-14.323-32-32-32-17.673 0-32 14.327-32 32v271.996h-271.997c-17.673 0-32 14.327-32 32s14.327 32 32 32h271.997v272.004c0 17.673 14.327 32 32 32 17.677 0 32-14.327 32-32v-272.004h272.004c17.673 0 32-14.327 31.996-32 0-17.673-14.323-32-32-32h-272v-271.996z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["add"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":483,"id":180,"name":"add","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M512 928c-14.217 0-28.275-0.713-42.142-2.112-16.695-1.681-30.558-12.531-36.689-27.392l-40.54-98.308-98.161 40.841c-14.837 6.174-32.3 4.045-45.292-6.554-21.839-17.818-41.831-37.811-59.65-59.648-10.601-12.992-12.726-30.455-6.553-45.295l40.839-98.159-98.307-40.542c-14.864-6.127-25.714-19.994-27.395-36.689-1.396-13.867-2.111-27.925-2.111-42.142s0.714-28.275 2.11-42.142c1.681-16.695 12.531-30.558 27.395-36.685l98.307-40.543-40.84-98.163c-6.173-14.836-4.047-32.3 6.553-45.292 17.818-21.838 37.81-41.83 59.648-59.648 12.992-10.601 30.455-12.726 45.292-6.554l98.163 40.84 40.54-98.309c6.131-14.865 19.994-25.714 36.689-27.395 13.867-1.396 27.925-2.111 42.142-2.111s28.275 0.714 42.142 2.111c16.695 1.681 30.558 12.53 36.689 27.395l40.542 98.309 98.163-40.84c14.835-6.173 32.299-4.047 45.291 6.554 21.837 17.818 41.83 37.81 59.648 59.648 10.598 12.992 12.723 30.455 6.554 45.292l-40.841 98.163 98.308 40.543c14.861 6.127 25.711 19.994 27.392 36.685 1.399 13.867 2.112 27.925 2.112 42.142s-0.713 28.275-2.112 42.142c-1.681 16.695-12.531 30.562-27.392 36.689l-98.308 40.542 40.836 98.159c6.174 14.839 4.049 32.303-6.549 45.295-17.822 21.837-37.811 41.83-59.652 59.648-12.992 10.598-30.455 12.727-45.291 6.554l-98.159-40.841-40.542 98.308c-6.131 14.861-19.994 25.711-36.689 27.392-13.867 1.399-27.925 2.112-42.142 2.112zM444.454 757.982l43.383 105.203c7.979 0.542 16.034 0.815 24.162 0.815s16.183-0.273 24.162-0.815l43.383-105.203c9.455-22.921 35.733-33.805 58.628-24.282l105.054 43.708c12.156-10.598 23.578-22.020 34.18-34.176l-43.708-105.058c-9.527-22.895 1.357-49.173 24.282-58.624l105.203-43.383c0.542-7.983 0.815-16.038 0.815-24.166 0-8.124-0.273-16.183-0.815-24.162l-105.203-43.383c-22.925-9.455-33.809-35.731-24.282-58.625l43.708-105.058c-10.603-12.156-22.020-23.577-34.176-34.177l-105.058 43.709c-22.895 9.525-49.173-1.359-58.628-24.283l-43.383-105.204c-7.979-0.54-16.038-0.815-24.162-0.815s-16.183 0.275-24.162 0.815l-43.383 105.204c-9.455 22.924-35.732 33.809-58.627 24.283l-105.058-43.709c-12.156 10.6-23.577 22.022-34.177 34.177l43.709 105.058c9.525 22.895-1.359 49.17-24.283 58.625l-105.203 43.383c-0.54 7.979-0.815 16.038-0.815 24.162 0 8.128 0.275 16.183 0.815 24.166l105.202 43.383c22.924 9.451 33.809 35.729 24.283 58.624l-43.708 105.058c10.601 12.156 22.022 23.578 34.179 34.176l105.056-43.708c22.895-9.523 49.172 1.361 58.627 24.282zM416 512c0-53.018 42.982-96 96-96s96 42.982 96 96c0 53.018-42.982 96-96 96s-96-42.982-96-96zM512 352c-88.366 0-160 71.634-160 160 0 88.367 71.634 160 160 160 88.367 0 160-71.633 160-160 0-88.366-71.633-160-160-160z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["administration"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":484,"id":179,"name":"administration","prevSize":32,"code":59649},"setIdx":0,"setId":2,"iconIdx":2},{"icon":{"paths":["M892.437 567.625c-41.856-41.139-161.237-29.828-220.928-22.285-59.004-35.998-98.458-85.705-126.242-158.727 13.376-55.195 34.645-139.187 18.526-191.982-14.409-89.82-129.677-80.907-146.143-20.227-15.094 55.194-1.372 131.987 24.014 230.035-34.305 81.935-85.42 191.981-121.44 255.059-68.611 35.311-161.235 89.822-174.957 158.387-11.321 54.165 89.194 189.239 261.063-106.961 76.845-25.37 160.548-56.567 234.647-68.907 64.836 34.965 140.651 58.278 191.424 58.278 87.479 0 96.055-96.674 60.036-132.672zM212.848 834.342c17.496-46.967 84.048-101.133 104.288-119.991-65.18 103.876-104.288 122.389-104.288 119.991zM492.779 180.918c25.387 0 22.985 110.047 6.174 139.872-15.091-47.653-14.75-139.872-6.174-139.872zM409.074 649.216c33.277-57.937 61.748-126.844 84.733-187.524 28.476 51.767 64.836 93.248 103.262 121.702-71.356 14.741-133.449 44.911-187.995 65.822zM860.531 632.077c0 0-17.152 20.57-127.957-26.743 120.41-8.913 140.309 18.513 127.957 26.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["adobe-reader-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":485,"id":178,"name":"adobe-reader-monochromatic","prevSize":32,"code":59650},"setIdx":0,"setId":2,"iconIdx":3},{"icon":{"paths":["M498.697 141.563l-298.668 136.533c-11.39 5.207-18.696 16.58-18.696 29.103v386.846c0 11.819 6.513 22.673 16.941 28.233l298.668 159.292c9.408 5.018 20.706 5.018 30.114 0l298.667-159.292c10.428-5.559 16.943-16.414 16.943-28.233v-386.846c0-12.524-7.305-23.896-18.697-29.103l-298.667-136.533c-8.448-3.862-18.159-3.862-26.607 0zM245.333 357.014l234.667 107.276v335.71l-234.667-125.154v-317.832zM544 800l234.667-125.154v-317.832l-234.667 107.276v335.71zM512 408.548l-221.699-101.348 221.699-101.348 221.7 101.348-221.7 101.348z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["apps"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":486,"id":177,"name":"apps","prevSize":32,"code":59651},"setIdx":0,"setId":2,"iconIdx":4},{"icon":{"paths":["M385 297.373c-12.497-12.497-32.758-12.497-45.255 0l-192 192.001c-12.497 12.497-12.497 32.755 0 45.252l192 192c12.497 12.497 32.758 12.497 45.255 0s12.497-32.755 0-45.252l-137.373-137.374h578.745v128c0 17.673 14.327 32 32 32s32-14.327 32-32v-160c0-17.673-14.327-32-32-32h-610.745l137.373-137.373c12.497-12.497 12.497-32.758 0-45.255z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-back"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":487,"id":176,"name":"arrow-back","prevSize":32,"code":59652},"setIdx":0,"setId":2,"iconIdx":5},{"icon":{"paths":["M550.037 242.504c0.192-17.672 14.669-31.845 32.341-31.657s31.846 14.668 31.659 32.34l-1.318 123.488 193.438-193.438c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-193.438 193.438 123.49-1.319c17.673-0.189 32.149 13.984 32.337 31.658 0.192 17.673-13.982 32.149-31.654 32.337l-201.92 2.159c-8.606 0.090-16.887-3.285-22.972-9.374-6.084-6.084-9.459-14.362-9.37-22.967l2.155-201.921zM474.628 782.148c-0.192 17.673-14.669 31.846-32.341 31.659-17.672-0.192-31.845-14.669-31.656-32.341l1.318-123.49-193.438 193.438c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l193.438-193.438-123.488 1.318c-17.672 0.188-32.151-13.986-32.34-31.659s13.984-32.149 31.657-32.337l201.919-2.159c8.606-0.090 16.887 3.285 22.972 9.37 6.084 6.089 9.463 14.366 9.37 22.972l-2.155 201.92z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-collapse"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":488,"id":175,"name":"arrow-collapse","prevSize":32,"code":59653},"setIdx":0,"setId":2,"iconIdx":6},{"icon":{"paths":["M586.372 672c-17.673 0-32 14.327-32 32s14.327 32 32 32h224c17.673 0 32-14.327 32-32v-208c0-17.673-14.327-32-32-32s-32 14.327-32 32v130.743l-233.374-233.371c-12.497-12.497-32.755-12.497-45.252 0l-73.374 73.371-169.373-169.371c-12.497-12.497-32.758-12.497-45.255 0s-12.497 32.758 0 45.255l192 191.999c12.497 12.497 32.756 12.497 45.254 0l73.374-73.37 210.743 210.743h-146.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-decrease"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":489,"id":174,"name":"arrow-decrease","prevSize":32,"code":59654},"setIdx":0,"setId":2,"iconIdx":7},{"icon":{"paths":["M329.373 550.626c-12.497-12.497-12.497-32.755 0-45.252s32.758-12.497 45.255 0l105.373 105.37v-418.743c0-17.673 14.327-32 32-32s32 14.327 32 32v418.743l105.374-105.37c12.497-12.497 32.755-12.497 45.252 0s12.497 32.755 0 45.252l-160 160c-12.497 12.497-32.755 12.497-45.252 0l-160.001-160zM112 864c0 17.673 14.327 32 32 32h768c17.673 0 32-14.327 32-32v-512c0-17.673-14.327-32-32-32h-96c-17.673 0-32 14.327-32 32s14.327 32 32 32h64v448h-704v-448h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-96c-17.673 0-32 14.327-32 32v512z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down-box"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":490,"id":173,"name":"arrow-down-box","prevSize":32,"code":59655},"setIdx":0,"setId":2,"iconIdx":8},{"icon":{"paths":["M864 511.326c0-194.147-157.598-351.535-352-351.535-194.404 0-352 157.388-352 351.535 0 194.15 157.596 351.535 352 351.535 194.402 0 352-157.385 352-351.535zM928 511.326c0 229.449-186.249 415.454-416 415.454s-416-186.005-416-415.454c0-229.447 186.249-415.451 416-415.451s416 186.004 416 415.451zM694.259 570.615l-159.97 155.511c-12.425 12.079-32.218 12.079-44.642 0l-159.968-155.511c-12.664-12.309-12.937-32.542-0.61-45.188 12.328-12.651 32.587-12.924 45.251-0.61l105.65 102.707v-275.987c0-17.65 14.327-31.958 32-31.958s32 14.308 32 31.958v275.987l105.647-102.707c12.668-12.314 32.926-12.041 45.252 0.61 12.326 12.646 12.053 32.879-0.61 45.188z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down-circle"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":491,"id":172,"name":"arrow-down-circle","prevSize":32,"code":59656},"setIdx":0,"setId":2,"iconIdx":9},{"icon":{"paths":["M734.997 526.067c12.356 12.634 12.13 32.896-0.508 45.248l-191.97 187.721c-12.437 12.16-32.307 12.16-44.745 0l-191.97-187.721c-12.636-12.352-12.863-32.614-0.507-45.248 12.356-12.638 32.616-12.864 45.252-0.508l137.595 134.549v-372.109c0-17.673 14.327-32 32-32 17.677 0 32 14.327 32 32v372.109l137.6-134.549c12.634-12.356 32.896-12.13 45.252 0.508z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-down"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":492,"id":171,"name":"arrow-down","prevSize":32,"code":59657},"setIdx":0,"setId":2,"iconIdx":10},{"icon":{"paths":["M858.624 398.148c-0.188 17.672-14.669 31.847-32.337 31.655-17.673-0.188-31.846-14.666-31.659-32.338l1.318-123.488-193.438 193.437c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.755 0-45.254l193.442-193.439-123.49 1.319c-17.673 0.189-32.154-13.984-32.341-31.657s13.986-32.151 31.659-32.34l201.92-2.156c8.606-0.092 16.883 3.286 22.967 9.37s9.463 14.365 9.374 22.969l-2.159 201.921zM166.041 626.517c0.189-17.673 14.668-31.842 32.34-31.654s31.845 14.669 31.657 32.341l-1.319 123.486 193.438-193.438c12.497-12.497 32.759-12.497 45.257 0s12.497 32.759 0 45.257l-193.44 193.438 123.488-1.318c17.672-0.192 32.153 13.982 32.341 31.654s-13.985 32.154-31.657 32.341l-201.921 2.155c-8.605 0.094-16.884-3.285-22.969-9.37s-9.463-14.366-9.37-22.967l2.156-201.924z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-expand"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":493,"id":170,"name":"arrow-expand","prevSize":32,"code":59658},"setIdx":0,"setId":2,"iconIdx":11},{"icon":{"paths":["M584.115 352c-17.673 0-32-14.327-32-32s14.327-32 32-32h224c17.677 0 32 14.327 32 32v208c0 17.673-14.323 32-32 32-17.673 0-32-14.327-32-32v-130.745l-233.37 233.371c-12.497 12.497-32.759 12.497-45.257 0l-73.372-73.37-169.373 169.37c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l192-192c12.497-12.497 32.759-12.497 45.256 0l73.37 73.37 210.748-210.743h-146.748z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-increase"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":494,"id":169,"name":"arrow-increase","prevSize":32,"code":59659},"setIdx":0,"setId":2,"iconIdx":12},{"icon":{"paths":["M307.745 105.235c12.497-12.48 32.758-12.48 45.255 0s12.497 32.715 0 45.195l-105.373 105.233h610.745c17.673 0 32 14.308 32 31.958v111.852c0 17.65-14.327 31.959-32 31.959s-32-14.309-32-31.959v-79.894h-578.745l105.373 105.233c12.497 12.482 12.497 32.715 0 45.195s-32.758 12.48-45.255 0l-160-159.788c-12.497-12.48-12.497-32.715 0-45.195l160-159.789zM720.998 917.414c-12.497 12.48-32.755 12.48-45.252 0s-12.497-32.713 0-45.193l105.37-105.233h-610.743c-17.673 0-32-14.31-32-31.957v-111.855c0-17.647 14.327-31.957 32-31.957s32 14.31 32 31.957v79.898h578.743l-105.37-105.237c-12.497-12.48-12.497-32.713 0-45.193s32.755-12.48 45.252 0l160 159.787c12.497 12.48 12.497 32.717 0 45.197l-160 159.787z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-looping"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":495,"id":168,"name":"arrow-looping","prevSize":32,"code":59660},"setIdx":0,"setId":2,"iconIdx":13},{"icon":{"paths":["M385 769.293c-12.497 12.497-32.758 12.497-45.255 0l-192-192c-12.497-12.497-12.497-32.755 0-45.252l192-192.001c12.497-12.497 32.758-12.497 45.255 0s12.497 32.758 0 45.255l-137.373 137.373h578.745v-192h-192c-17.673 0-32-14.327-32-32s14.327-32 32-32h224c17.673 0 32 14.327 32 32v256c0 17.673-14.327 32-32 32h-610.745l137.373 137.374c12.497 12.497 12.497 32.755 0 45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-return"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":496,"id":167,"name":"arrow-return","prevSize":32,"code":59661},"setIdx":0,"setId":2,"iconIdx":14},{"icon":{"paths":["M694.626 473.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-105.374-105.371v418.745c0 17.673-14.327 32-32 32s-32-14.327-32-32v-418.745l-105.373 105.371c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l160.001-160.001c12.497-12.497 32.755-12.497 45.252 0l160 160.001zM912 160c0-17.673-14.327-32-32-32h-768c-17.673 0-32 14.327-32 32v512c0 17.673 14.327 32 32 32h96c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64v-448h704v448h-64c-17.673 0-32 14.327-32 32s14.327 32 32 32h96c17.673 0 32-14.327 32-32v-512z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-up-box"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":497,"id":166,"name":"arrow-up-box","prevSize":32,"code":59662},"setIdx":0,"setId":2,"iconIdx":15},{"icon":{"paths":["M305.297 498.091c-12.356-12.634-12.129-32.896 0.507-45.252l191.971-187.717c12.437-12.161 32.307-12.161 44.745 0l191.97 187.717c12.638 12.356 12.864 32.619 0.508 45.252-12.356 12.638-32.619 12.864-45.252 0.508l-137.6-134.55v372.11c0 17.673-14.323 32-32 32-17.673 0-32-14.327-32-32v-372.11l-137.595 134.55c-12.636 12.356-32.896 12.13-45.252-0.508z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow-up"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":498,"id":165,"name":"arrow-up","prevSize":32,"code":59663},"setIdx":0,"setId":2,"iconIdx":16},{"icon":{"paths":["M482.325 229.047c19.767-23.931 49.89-36.827 80.849-34.615l143.936 10.281c42.052 3.004 77.222 33.091 86.703 74.17l30.349 131.517c6.643 28.785-0.337 59.028-18.923 81.991l-252.369 311.744c-11.119 13.739-31.27 15.859-45.005 4.74-13.734-11.123-15.859-31.27-4.736-45.009l252.365-311.744c6.195-7.654 8.521-17.737 6.306-27.331l-30.349-131.517c-3.157-13.693-14.882-23.722-28.898-24.723l-143.936-10.281c-10.321-0.737-20.361 3.561-26.953 11.538l-265.999 322c-5.438 6.583-8.020 15.066-7.17 23.565l7.581 75.81c1.636 16.358 15.401 28.817 31.841 28.817h104.305c9.439 0 18.397-4.169 24.476-11.388l227.597-270.268-10.052-70.344h-69.257l-230.406 276.488c-11.314 13.577-31.492 15.411-45.069 4.096s-15.412-31.492-4.097-45.069l230.403-276.487c12.16-14.592 30.174-23.028 49.169-23.028h69.257c31.851 0 58.854 23.419 63.356 54.949l10.052 70.345c2.577 18.044-2.662 36.331-14.404 50.274l-227.593 270.268c-18.24 21.662-45.116 34.163-73.432 34.163h-104.305c-49.321 0-90.616-37.372-95.524-86.447l-7.581-75.814c-2.549-25.489 5.196-50.944 21.511-70.694l266-321.997z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["attach"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":499,"id":164,"name":"attach","prevSize":32,"code":59664},"setIdx":0,"setId":2,"iconIdx":17},{"icon":{"paths":["M866.129 153.127c-12.497-12.497-32.759-12.497-45.257 0l-668.245 668.244c-12.497 12.497-12.497 32.759 0 45.257s32.758 12.497 45.255 0l668.247-668.246c12.497-12.497 12.497-32.758 0-45.255zM674.645 480.358l55.919-55.923c16.256 79.564 14.515 161.864-5.231 240.844l-11.622 46.485c-4.284 17.148-21.658 27.571-38.805 23.283-17.143-4.284-27.571-21.658-23.283-38.805l11.622-46.485c13.875-55.509 17.677-112.875 11.401-169.399zM817.161 385.922l-11.311-36.77 51.204-51.206 21.278 69.154c33.344 108.369 31.996 224.448-3.861 332.010l-33.446 100.343c-5.589 16.764-23.71 25.826-40.478 20.237-16.764-5.589-25.826-23.71-20.237-40.478l33.446-100.339c31.637-94.912 32.828-197.333 3.405-292.952zM490.667 664.333l64-64v210.342c0 23.565-19.102 42.667-42.667 42.667h-85.333c-35.46 0-68.395-10.812-95.685-29.325l46.611-46.609c14.701 7.629 31.395 11.934 49.073 11.934h64v-125.009zM128 682.675c0 19.742 13.403 36.348 31.604 41.22l62.552-62.554h-30.156v-298.665h118.99l12.367-48.047c11.845-46.020 53.696-79.953 103.31-79.953h64v158.156l64-64v-115.49c0-23.564-19.102-42.667-42.667-42.667h-85.333c-79.524 0-146.344 54.391-165.29 128h-90.71c-23.564 0-42.667 19.103-42.667 42.667v341.332z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":500,"id":163,"name":"audio-disabled","prevSize":32,"code":59665},"setIdx":0,"setId":2,"iconIdx":18},{"icon":{"paths":["M317.754 661.342h-118.99v-298.665h118.99l12.367-48.047c11.845-46.020 53.696-79.953 103.308-79.953h64v554.665h-64c-49.613 0-91.464-33.933-103.308-79.949l-12.367-48.051zM177.431 725.342h90.71c18.946 73.613 85.766 128 165.288 128h85.333c23.565 0 42.667-19.102 42.667-42.667v-597.332c0-23.564-19.102-42.667-42.667-42.667h-85.333c-79.522 0-146.342 54.391-165.288 128h-90.71c-23.564 0-42.667 19.103-42.667 42.667v341.332c0 23.565 19.103 42.667 42.667 42.667zM893.393 393.373c12.497 12.497 12.497 32.758 0 45.254l-73.374 73.374 73.374 73.374c12.497 12.497 12.497 32.755 0 45.252s-32.759 12.497-45.257 0l-73.374-73.37-73.37 73.37c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.755 0-45.252l73.374-73.374-73.374-73.374c-12.497-12.495-12.497-32.757 0-45.254s32.759-12.497 45.257 0l73.37 73.371 73.374-73.371c12.497-12.497 32.759-12.497 45.257 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio-unavailable"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":501,"id":162,"name":"audio-unavailable","prevSize":32,"code":59666},"setIdx":0,"setId":2,"iconIdx":19},{"icon":{"paths":["M300.099 631.121h-113.433v-280.89h113.433l12.367-48.048c11.055-42.953 50.122-74.619 96.423-74.619h58.665v526.223h-58.665c-46.301 0-85.367-31.667-96.423-74.62l-12.367-48.047zM163.555 695.121h86.931c18.156 70.541 82.193 122.667 158.403 122.667h81.778c22.583 0 40.887-18.308 40.887-40.892v-572.443c0-22.582-18.304-40.889-40.887-40.889h-81.778c-76.21 0-140.246 52.124-158.403 122.667h-86.931c-22.582 0-40.889 18.307-40.889 40.889v327.11c0 22.583 18.307 40.892 40.889 40.892zM646.46 316.516c17.148-4.286 34.522 6.138 38.805 23.284l11.14 44.551c20.804 83.229 20.804 170.303 0 253.529l-11.14 44.553c-4.284 17.148-21.658 27.571-38.805 23.283-17.143-4.284-27.571-21.658-23.283-38.805l11.14-44.553c18.257-73.037 18.257-149.444 0-222.484l-11.14-44.551c-4.288-17.146 6.14-34.519 23.283-38.806zM807.475 235.938c-5.201-16.892-23.108-26.372-39.996-21.175-16.892 5.198-26.372 23.104-21.175 39.996l35.533 115.49c28.117 91.37 26.978 189.239-3.251 279.931l-32.055 96.158c-5.589 16.768 3.473 34.889 20.237 40.478 16.768 5.589 34.889-3.473 40.478-20.237l32.055-96.162c34.449-103.343 35.746-214.869 3.708-318.99l-35.533-115.49z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["audio"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":502,"id":161,"name":"audio","prevSize":32,"code":59667},"setIdx":0,"setId":2,"iconIdx":20},{"icon":{"paths":["M416 128c-35.346 0-64 28.654-64 64v226.636c20.775-2.822 42.133-3.455 64-1.527v-225.11h176v180.706c0 35.346 28.655 64 64 64h176v331.294h-120.555c-17.156 22.178-36.322 43.87-57.31 64h177.865c35.345 0 64-28.655 64-64v-363.294c0-6.41-1.924-12.672-5.525-17.975l-156.634-230.682c-11.913-17.545-31.74-28.049-52.945-28.049h-264.896zM656 372.706v-180.706h24.896l122.697 180.706h-147.593zM165.234 668.544c120.524 156.143 218.24 174.588 285.134 158.741 73.293-17.361 139.23-81.647 183.462-151.761-115.153-155.87-211.58-174.588-279.148-158.916-74.361 17.25-142.706 81.685-189.449 151.936zM101.723 649.037c100.621-162.799 337.62-357.19 593.253 1.788 9.071 12.74 10.321 29.82 2.423 43.315-95.275 162.854-326.394 358.345-593.236-0.222-9.745-13.094-11.022-30.997-2.44-44.881zM398.961 735.991c31.512 0 60.269-26.854 60.269-64 0-37.141-28.757-64-60.269-64-31.514 0-60.268 26.859-60.268 64 0 37.146 28.755 64 60.268 64zM398.961 799.991c68.632 0 124.269-57.306 124.269-128 0-70.69-55.637-128-124.269-128s-124.268 57.31-124.268 128c0 70.694 55.637 128 124.268 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["auditing"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":503,"id":160,"name":"auditing","prevSize":32,"code":59668},"setIdx":0,"setId":2,"iconIdx":21},{"icon":{"paths":["M224 224c-17.673 0-32 14.327-32 32v544c0 17.673 14.327 32 32 32h544c17.673 0 32-14.327 32-32v-544c0-17.673-14.327-32-32-32h-544zM128 256c0-53.019 42.981-96 96-96h544c53.018 0 96 42.981 96 96v544c0 53.018-42.982 96-96 96h-544c-53.019 0-96-42.982-96-96v-544zM608 460.813c0 41.801-26.714 77.359-64 90.539v133.461h-64v-133.461c-37.286-13.18-64-48.738-64-90.539 0-53.020 42.982-96 96-96s96 42.981 96 96zM608 588.826c38.861-29.193 64-75.665 64-128.013 0-88.366-71.633-160-160-160-88.366 0-160 71.635-160 160 0 52.348 25.138 98.82 64 128.013v127.987c0 17.673 14.327 32 32 32h128c17.673 0 32-14.327 32-32v-127.987z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["auth"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":504,"id":159,"name":"auth","prevSize":32,"code":59669},"setIdx":0,"setId":2,"iconIdx":22},{"icon":{"paths":["M512 554.018c66.466 0 120.346-52.493 120.346-117.239 0-64.753-53.879-117.243-120.346-117.243s-120.347 52.491-120.347 117.243c0 64.747 53.881 117.239 120.347 117.239zM512 490.018c-32.708 0-56.346-25.404-56.346-53.239 0-27.84 23.637-53.243 56.346-53.243 32.704 0 56.346 25.403 56.346 53.243 0 27.836-23.642 53.239-56.346 53.239z","M364.125 894.144h-172.125c-17.673 0-32-14.327-32-32v-702.144c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32v702.144c0 17.673-14.327 32-32 32h-172.126c-6.242 1.276-12.702 1.946-19.319 1.946h-257.109c-6.618 0-13.079-0.67-19.32-1.946zM224 830.144h68.244c-3.114-9.455-4.799-19.558-4.799-30.054v-93.449c0-56.107 37.881-105.143 92.172-119.309 19.171-5.005 39.265-5.312 58.583-0.9l42.773 9.766c20.049 4.578 40.909 4.254 60.813-0.939l28.070-7.326c20.693-5.397 42.381-5.73 63.232-0.973 60.531 13.824 103.467 67.665 103.467 129.758v83.371c0 10.496-1.685 20.599-4.8 30.054h68.245v-638.144h-576v638.144zM651.567 830.144c12.245-4.489 20.988-16.252 20.988-30.054v-83.371c0-32.235-22.289-60.186-53.713-67.362-10.825-2.47-22.084-2.3-32.828 0.503l-28.070 7.326c-29.85 7.791-61.141 8.273-91.217 1.404l-42.775-9.766c-9.29-2.121-18.955-1.971-28.175 0.435-26.112 6.814-44.331 30.396-44.331 57.382v93.449c0 13.803 8.74 25.566 20.989 30.054h279.133z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["avatar"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":505,"id":158,"name":"avatar","prevSize":32,"code":59670},"setIdx":0,"setId":2,"iconIdx":23},{"icon":{"paths":["M737.779 361.376c12.497 12.497 12.497 32.758 0 45.255l-110.946 110.95 110.946 110.946c12.497 12.497 12.497 32.759 0 45.257s-32.759 12.497-45.252 0l-110.95-110.95-110.95 110.95c-12.497 12.497-32.755 12.497-45.254 0-12.497-12.497-12.497-32.759 0-45.257l110.947-110.946-110.947-110.95c-12.497-12.497-12.497-32.758 0-45.255 12.498-12.497 32.756-12.497 45.254 0l110.95 110.948 110.95-110.948c12.493-12.497 32.755-12.497 45.252 0z","M312.246 218.073c12.061-16.393 31.2-26.073 51.552-26.073h468.202c35.345 0 64 28.654 64 64v512c0 35.345-28.655 64-64 64h-468.202c-20.352 0-39.491-9.681-51.551-26.074l-188.343-256c-16.598-22.562-16.598-53.291 0-75.853l188.343-256zM363.798 256l-188.343 256 188.343 256h468.202v-512h-468.202z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["backspace"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":506,"id":157,"name":"backspace","prevSize":32,"code":59671},"setIdx":0,"setId":2,"iconIdx":24},{"icon":{"paths":["M875.273 512c0-86.89-31.484-166.427-83.665-227.826l-496.163 496.161c61.399 52.181 140.937 83.665 227.828 83.665 194.402 0 352-157.598 352-352zM250.62 734.652l495.3-495.302c-60.659-49.597-138.18-79.349-222.647-79.349-194.406 0-352.002 157.596-352.002 352 0 84.467 29.752 161.988 79.349 222.652zM939.273 512c0 229.751-186.253 416-416 416-229.752 0-416.002-186.249-416.002-416s186.249-416 416.002-416c229.747 0 416 186.249 416 416z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["ban"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":507,"id":156,"name":"ban","prevSize":32,"code":59672},"setIdx":0,"setId":2,"iconIdx":25},{"icon":{"paths":["M384 775.415c-17.673 0-32-14.327-32-32v-453.166c0-17.673 14.327-32 32-32h137.075c56.119 0 98.27 11.603 126.447 34.81 28.416 23.206 42.624 57.542 42.624 103.007 0 24.153-6.865 45.585-20.599 64.29-13.734 18.47-32.444 32.798-56.124 42.978 27.942 7.817 49.967 22.733 66.069 44.757 16.337 21.786 24.508 47.834 24.508 78.144 0 46.409-15.036 82.876-45.111 109.397-30.071 26.522-72.576 39.782-127.514 39.782h-147.375zM420.198 533.53v186.121h112.598c31.731 0 56.713-8.171 74.944-24.508 18.47-16.576 27.708-39.309 27.708-68.198 0-62.276-33.865-93.414-101.585-93.414h-113.664zM420.198 478.827h103.006c29.837 0 53.636-7.458 71.394-22.379 17.997-14.916 26.995-35.163 26.995-60.737 0-28.416-8.286-49.017-24.862-61.804-16.576-13.024-41.796-19.536-75.657-19.536h-100.877v164.456z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bold"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":508,"id":155,"name":"bold","prevSize":32,"code":59673},"setIdx":0,"setId":2,"iconIdx":26},{"icon":{"paths":["M352 445.44c0-17.677 14.327-32.002 32-32.002h256c17.673 0 32 14.325 32 32.002 0 17.673-14.327 32-32 32h-256c-17.673 0-32-14.327-32-32z","M352 322.558c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32s-14.327 32-32 32h-256c-17.673 0-32-14.327-32-32z","M864 679.68c0 17.673-14.327 32-32 32h-18.509c-8.218 40.546-8.218 82.334 0 122.88h19.789c16.964 0 30.72 13.756 30.72 30.72s-13.756 30.72-30.72 30.72h-545.28c-70.692 0-128-55.014-128-122.88v-453.12c0-106.039 85.961-192 192-192h480c17.673 0 32 14.327 32 32v519.68zM748.42 834.56c-6.788-40.678-6.788-82.202 0-122.88h-460.42c-35.346 0-64 27.507-64 61.44s28.654 61.44 64 61.44h460.42zM224 647.68h576v-455.68h-448c-70.692 0-128 57.308-128 128v327.68z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["book"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":509,"id":154,"name":"book","prevSize":32,"code":59674},"setIdx":0,"setId":2,"iconIdx":27},{"icon":{"paths":["M224 224h320v576h-96v-112c0-8.836-7.164-16-16-16h-96c-8.837 0-16 7.164-16 16v112h-96v-576zM608 448h192v352h-192v-352zM832 384h-224v-192c0-17.673-14.327-32-32-32h-384c-17.673 0-32 14.327-32 32v640c0 17.673 14.327 32 32 32h640c17.673 0 32-14.327 32-32v-416c0-17.673-14.327-32-32-32zM304 288c-8.837 0-16 7.163-16 16v32c0 8.837 7.163 16 16 16h32c8.837 0 16-7.163 16-16v-32c0-8.837-7.163-16-16-16h-32zM288 432c0-8.837 7.163-16 16-16h32c8.837 0 16 7.163 16 16v32c0 8.836-7.163 16-16 16h-32c-8.837 0-16-7.164-16-16v-32zM304 544c-8.837 0-16 7.164-16 16v32c0 8.836 7.163 16 16 16h32c8.837 0 16-7.164 16-16v-32c0-8.836-7.163-16-16-16h-32zM416 304c0-8.837 7.163-16 16-16h32c8.836 0 16 7.163 16 16v32c0 8.837-7.164 16-16 16h-32c-8.837 0-16-7.163-16-16v-32zM432 416c-8.837 0-16 7.163-16 16v32c0 8.836 7.163 16 16 16h32c8.836 0 16-7.164 16-16v-32c0-8.837-7.164-16-16-16h-32zM416 560c0-8.836 7.163-16 16-16h32c8.836 0 16 7.164 16 16v32c0 8.836-7.164 16-16 16h-32c-8.837 0-16-7.164-16-16v-32zM688 512c-8.836 0-16 7.164-16 16v32c0 8.836 7.164 16 16 16h32c8.836 0 16-7.164 16-16v-32c0-8.836-7.164-16-16-16h-32zM672 656c0-8.836 7.164-16 16-16h32c8.836 0 16 7.164 16 16v32c0 8.836-7.164 16-16 16h-32c-8.836 0-16-7.164-16-16v-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["business"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":510,"id":153,"name":"business","prevSize":32,"code":59675},"setIdx":0,"setId":2,"iconIdx":28},{"icon":{"paths":["M276.48 157.44c0-16.259 13.181-29.44 29.44-29.44s29.44 13.181 29.44 29.44v58.877h353.28v-58.877c0-16.259 13.18-29.44 29.44-29.44s29.44 13.181 29.44 29.44v58.877h85.76c17.673 0 32 14.327 32 32v583.679c0 17.673-14.327 32-32 32h-642.56c-17.673 0-32-14.327-32-32v-583.679c0-1.105 0.056-2.196 0.165-3.272 1.639-16.136 15.267-28.728 31.835-28.728h85.76v-58.877zM801.28 392.957h-578.56v407.039h578.56v-407.039zM423.68 644.476c0-8.836 7.163-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM321.92 510.72c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.837 0 16-7.164 16-16v-26.88c0-8.836-7.163-16-16-16h-26.88zM541.44 526.72c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM321.92 628.476c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.837 0 16-7.164 16-16v-26.88c0-8.836-7.163-16-16-16h-26.88zM541.44 644.476c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM439.68 510.72c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.836 0 16-7.164 16-16v-26.88c0-8.836-7.164-16-16-16h-26.88zM659.2 526.72c0-8.836 7.164-16 16-16h26.88c8.836 0 16 7.164 16 16v26.88c0 8.836-7.164 16-16 16h-26.88c-8.836 0-16-7.164-16-16v-26.88zM675.2 628.476c-8.836 0-16 7.164-16 16v26.88c0 8.836 7.164 16 16 16h26.88c8.836 0 16-7.164 16-16v-26.88c0-8.836-7.164-16-16-16h-26.88z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["calendar"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":511,"id":152,"name":"calendar","prevSize":32,"code":59676},"setIdx":0,"setId":2,"iconIdx":29},{"icon":{"paths":["M877.402 153.128c-12.497-12.497-32.759-12.497-45.257 0l-668.244 668.244c-12.497 12.497-12.497 32.759 0 45.257s32.758 12.497 45.255 0l668.246-668.246c12.497-12.497 12.497-32.758 0-45.255zM309.94 170.672c-23.564 0-42.667 14.327-42.667 32s19.103 32 42.667 32h255.999c23.565 0 42.667-14.327 42.667-32s-19.102-32-42.667-32h-255.999zM585.446 309.329h-403.506c-41.237 0-74.667 33.429-74.667 74.667v384c0 6.012 0.71 11.853 2.051 17.455l61.949-61.952v-339.503c0-5.891 4.776-10.667 10.667-10.667h339.506l64-64zM387.612 778.662h306.327c5.892 0 10.667-4.774 10.667-10.667v-85.333c0-11.375 6.037-21.897 15.859-27.631 9.822-5.739 21.952-5.833 31.859-0.243l97.83 55.164c2.53 1.425 4.855 3.191 6.908 5.244 6.72 6.72 18.21 1.963 18.21-7.539v-263.322c0-9.502-11.49-14.263-18.206-7.543-2.057 2.057-4.382 3.819-6.912 5.248l-97.83 55.164c-9.907 5.585-22.037 5.495-31.859-0.243s-15.859-16.256-15.859-27.631v-7.663l110.78-73.495c47.42-41.99 123.887-8.7 123.887 56.164v263.322c0 64.862-76.467 98.155-123.887 56.162l-46.78-26.377v30.554c0 41.237-33.429 74.667-74.667 74.667h-370.327l64-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":512,"id":151,"name":"camera-disabled","prevSize":32,"code":59677},"setIdx":0,"setId":2,"iconIdx":30},{"icon":{"paths":["M288 224c-17.673 0-32 14.327-32 32s14.327 32 32 32h256c17.673 0 32-14.327 32-32s-14.327-32-32-32h-256z","M170.667 341.328c-23.564 0-42.667 19.102-42.667 42.667v384.001c0 23.565 19.103 42.667 42.667 42.667h512c23.565 0 42.667-19.102 42.667-42.667v-128l97.83 97.83c26.88 26.876 72.836 7.842 72.836-30.17v-263.322c0-38.012-45.956-57.049-72.836-30.17l-97.83 97.831v-128.001c0-23.564-19.102-42.667-42.667-42.667h-512z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-filled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":513,"id":150,"name":"camera-filled","prevSize":32,"code":59678},"setIdx":0,"setId":2,"iconIdx":31},{"icon":{"paths":["M195.378 191.997c0-15.807 12.815-28.622 28.622-28.622h128c15.807 0 28.622 12.815 28.622 28.622s-12.815 28.622-28.622 28.622h-128c-15.807 0-28.622-12.814-28.622-28.622z","M736 559.996c0 97.203-78.797 176-176 176s-176-78.797-176-176c0-97.203 78.797-175.999 176-175.999s176 78.796 176 175.999zM672 559.996c0-61.854-50.146-112-112-112s-112 50.146-112 112c0 61.858 50.146 112 112 112s112-50.142 112-112z","M192 255.997c-35.346 0-64 28.654-64 64v447.999c0 35.345 28.654 64 64 64h640c35.345 0 64-28.655 64-64v-447.999c0-35.346-28.655-64-64-64h-640zM832 319.997v447.999h-640v-447.999h640z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-photo"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":514,"id":149,"name":"camera-photo","prevSize":32,"code":59679},"setIdx":0,"setId":2,"iconIdx":32},{"icon":{"paths":["M312.195 170.672c-23.564 0-42.667 14.327-42.667 32s19.102 32 42.667 32h256.001c23.565 0 42.667-14.327 42.667-32s-19.102-32-42.667-32h-256.001z","M559.241 503.454c11.268-11.268 11.191-29.611-0.171-40.973-11.362-11.358-29.705-11.435-40.969-0.171l-71.403 71.403-71.997-71.996c-11.36-11.362-29.704-11.435-40.971-0.171-11.267 11.268-11.191 29.611 0.17 40.973l71.997 71.996-71.402 71.403c-11.267 11.264-11.191 29.606 0.17 40.969s29.704 11.439 40.971 0.171l71.404-71.403 71.996 71.996c11.358 11.362 29.705 11.439 40.969 0.171 11.268-11.264 11.191-29.611-0.171-40.969l-71.996-71.996 71.403-71.403z","M109.529 383.995c0-41.237 33.429-74.667 74.667-74.667h512.001c41.237 0 74.667 33.429 74.667 74.667v30.553l46.78-26.377c47.415-41.99 123.887-8.7 123.887 56.164v263.322c0 64.862-76.471 98.155-123.887 56.162l-46.78-26.377v30.554c0 41.237-33.429 74.667-74.667 74.667h-512.001c-41.237 0-74.667-33.429-74.667-74.667v-384.001zM184.195 373.328c-5.891 0-10.667 4.776-10.667 10.667v384.001c0 5.888 4.776 10.667 10.667 10.667h512.001c5.888 0 10.667-4.779 10.667-10.667v-85.333c0-11.375 6.037-21.897 15.859-27.631 9.822-5.739 21.948-5.833 31.859-0.243l97.826 55.164c2.534 1.425 4.855 3.191 6.912 5.244 6.72 6.72 18.21 1.963 18.21-7.539v-263.322c0-9.502-11.49-14.263-18.21-7.543-2.052 2.057-4.378 3.819-6.912 5.248l-97.826 55.164c-9.911 5.585-22.037 5.491-31.859-0.243-9.822-5.739-15.859-16.256-15.859-27.631v-85.334c0-5.891-4.779-10.667-10.667-10.667h-512.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-unavailable"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":515,"id":148,"name":"camera-unavailable","prevSize":32,"code":59680},"setIdx":0,"setId":2,"iconIdx":33},{"icon":{"paths":["M265.018 202.672c0-17.673 19.102-32 42.667-32h256.001c23.565 0 42.667 14.327 42.667 32s-19.102 32-42.667 32h-256.001c-23.564 0-42.667-14.327-42.667-32zM169.018 383.995c0-5.891 4.776-10.667 10.667-10.667h512.001c5.888 0 10.667 4.776 10.667 10.667v85.334c0 11.375 6.037 21.892 15.859 27.631 9.822 5.734 21.948 5.828 31.855 0.243l97.83-55.164c2.534-1.429 4.855-3.191 6.912-5.248 6.72-6.72 18.21-1.958 18.21 7.543v263.322c0 9.502-11.49 14.259-18.21 7.539-2.057-2.052-4.378-3.819-6.912-5.244l-97.83-55.164c-9.907-5.589-22.033-5.495-31.855 0.243-9.822 5.734-15.859 16.256-15.859 27.631v85.333c0 5.888-4.779 10.667-10.667 10.667h-512.001c-5.891 0-10.667-4.779-10.667-10.667v-384.001zM179.685 309.328c-41.237 0-74.667 33.429-74.667 74.667v384.001c0 41.237 33.43 74.667 74.667 74.667h512.001c41.237 0 74.667-33.429 74.667-74.667v-30.554l46.775 26.377c47.42 41.993 123.891 8.7 123.891-56.162v-263.322c0-64.864-76.471-98.155-123.891-56.164l-46.775 26.377v-30.553c0-41.237-33.429-74.667-74.667-74.667h-512.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":516,"id":147,"name":"camera","prevSize":32,"code":59681},"setIdx":0,"setId":2,"iconIdx":34},{"icon":{"paths":["M226.503 304.026c-49.642 68.947-66.502 152.644-66.502 207.97v1.451l-0.131 1.446c-9.702 106.718 28.59 181.931 90.135 234.005 62.938 53.257 152.152 83.733 244.195 93.961 91.968 10.219 193.271 1.446 273.31-15.479 40.017-8.465 73.613-18.735 97.438-29.077 9.246-4.015 16.439-7.787 21.739-11.127-2.466-1.566-5.449-3.302-9.003-5.197-9.225-4.911-19.78-9.579-30.733-14.366l-1.916-0.836c-9.664-4.224-20.471-8.943-28.655-13.521-14.835-8.294-29.389-17.732-40.004-27.772-5.227-4.941-11.166-11.571-15.147-19.827-4.211-8.742-7.223-21.794-1.306-35.601 31.979-74.62 47.177-115.49 54.537-142.481 6.874-25.199 6.874-37.888 6.874-58.061v-0.183c0-16.038-9.318-89.517-55.812-158.033-45.013-66.338-125.99-129.968-274.854-129.968-134.636 0-215.696 55.382-264.163 122.698zM174.565 266.63c60.333-83.796 160.607-149.302 316.102-149.302 171.136 0 271.497 75.037 327.812 158.032 54.839 80.818 66.854 167.337 66.854 193.969 0 22.383-0.021 41.694-9.126 75.085-8.128 29.807-23.484 70.929-51.942 137.924 5.261 4.169 13.069 9.306 23.356 15.061 5.316 2.974 13.487 6.554 24.964 11.571 10.654 4.655 23.437 10.266 35.174 16.512 11.26 5.995 24.418 14.033 34.202 24.542 10.231 10.991 20.975 29.845 13.299 52.864-5.056 15.164-16.794 25.937-26.577 33.092-10.628 7.774-23.817 14.763-38.251 21.030-29.009 12.587-67.029 23.962-109.683 32.981-85.312 18.039-193.583 27.588-293.615 16.474-99.956-11.106-202.741-44.629-278.47-108.71-76.833-65.011-123.811-159.996-112.66-287.223 0.286-65.549 19.841-162.346 78.561-243.902zM496 320c17.673 0 32 14.327 32 32v224c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32zM528 672c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 17.673 14.327 32 32 32s32-14.327 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["canned-response"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":517,"id":146,"name":"canned-response","prevSize":32,"code":59682},"setIdx":0,"setId":2,"iconIdx":35},{"icon":{"paths":["M192 181.333c-53.019 0-96 42.981-96 96v469.333c0 53.018 42.981 96 96 96h640c53.018 0 96-42.982 96-96v-469.333c0-53.019-42.982-96-96-96h-640zM160 277.333c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32v202.671h-704v-202.671zM160 544.004h704v202.662c0 17.673-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32v-202.662zM810.667 661.333c0-35.345-28.655-64-64-64s-64 28.655-64 64c0 35.345 28.655 64 64 64s64-28.655 64-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["card"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":518,"id":145,"name":"card","prevSize":32,"code":59683},"setIdx":0,"setId":2,"iconIdx":36},{"icon":{"paths":["M336 304c0-97.202 78.798-176 176-176 97.203 0 176 78.798 176 176v142.477h16c53.018 0 96 42.978 96 96v257.523c0 53.018-42.982 96-96 96h-384c-53.019 0-96-42.982-96-96v-257.523c0-53.022 42.981-96 96-96h16v-142.477zM400 446.477h224v-142.477c0-61.856-50.146-112-112-112s-112 50.144-112 112v142.477zM320 510.477c-17.673 0-32 14.327-32 32v257.523c0 17.673 14.327 32 32 32h384c17.673 0 32-14.327 32-32v-257.523c0-17.673-14.327-32-32-32h-384z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["channel-private"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":519,"id":144,"name":"channel-private","prevSize":32,"code":59684},"setIdx":0,"setId":2,"iconIdx":37},{"icon":{"paths":["M336 128c17.673 0 32 14.327 32 32v144h288v-144c0-17.673 14.327-32 32-32s32 14.327 32 32v144h144c17.673 0 32 14.327 32 32s-14.327 32-32 32h-144v288h144c17.673 0 32 14.327 32 32s-14.327 32-32 32h-144v144c0 17.673-14.327 32-32 32s-32-14.327-32-32v-144h-288v144c0 17.673-14.327 32-32 32s-32-14.327-32-32v-144h-144c-17.673 0-32-14.327-32-32s14.327-32 32-32h144v-288h-144c-17.673 0-32-14.327-32-32s14.327-32 32-32h144v-144c0-17.673 14.327-32 32-32zM368 368v288h288v-288h-288z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["channel-public"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":520,"id":143,"name":"channel-public","prevSize":32,"code":59685},"setIdx":0,"setId":2,"iconIdx":38},{"icon":{"paths":["M854.507 233.251c12.561 12.43 12.672 32.691 0.243 45.254l-474.877 480.001c-6.013 6.076-14.208 9.498-22.757 9.493s-16.743-3.426-22.752-9.506l-165.124-167.091c-12.423-12.57-12.303-32.828 0.268-45.252s32.832-12.305 45.254 0.269l142.376 144.068 452.113-456.993c12.429-12.564 32.691-12.672 45.257-0.243z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["check"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":521,"id":142,"name":"check","prevSize":32,"code":59686},"setIdx":0,"setId":2,"iconIdx":39},{"icon":{"paths":["M282.726 436.041c12.497-12.498 32.758-12.498 45.255 0l185.372 185.37 185.374-185.37c12.497-12.498 32.755-12.498 45.252 0s12.497 32.755 0 45.252l-208 208c-12.497 12.497-32.755 12.497-45.252 0l-208.001-208c-12.497-12.497-12.497-32.755 0-45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-down"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":522,"id":141,"name":"chevron-down","prevSize":32,"code":59687},"setIdx":0,"setId":2,"iconIdx":40},{"icon":{"paths":["M671.057 183.163c16.683 16.663 16.683 43.677 0 60.34l-268.852 268.497 268.852 268.497c16.683 16.661 16.683 43.678 0 60.339-16.687 16.661-43.738 16.661-60.42 0l-299.061-298.667c-8.012-8-12.513-18.854-12.513-30.17s4.501-22.17 12.513-30.17l299.061-298.667c16.683-16.662 43.733-16.662 60.42 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-left-big"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":523,"id":140,"name":"chevron-left-big","prevSize":32,"code":59688},"setIdx":0,"setId":2,"iconIdx":41},{"icon":{"paths":["M593.822 281.373c12.497 12.497 12.497 32.758 0 45.255l-185.372 185.373 185.372 185.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-208.002-208c-12.497-12.497-12.497-32.755 0-45.252l208.002-208.001c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-left"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":524,"id":139,"name":"chevron-left","prevSize":32,"code":59689},"setIdx":0,"setId":2,"iconIdx":42},{"icon":{"paths":["M444.156 742.626c-12.497-12.497-12.497-32.755 0-45.252l185.374-185.374-185.374-185.373c-12.497-12.497-12.497-32.758 0-45.255s32.759-12.497 45.257 0l208 208.001c12.497 12.497 12.497 32.755 0 45.252l-208 208c-12.497 12.497-32.759 12.497-45.257 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-right"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":525,"id":138,"name":"chevron-right","prevSize":32,"code":59690},"setIdx":0,"setId":2,"iconIdx":43},{"icon":{"paths":["M746.236 587.959c-12.497 12.497-32.759 12.497-45.257 0l-185.374-185.371-185.371 185.371c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l208-208.001c12.497-12.497 32.759-12.497 45.257 0l208 208.001c12.497 12.497 12.497 32.755 0 45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chevron-up"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":526,"id":137,"name":"chevron-up","prevSize":32,"code":59691},"setIdx":0,"setId":2,"iconIdx":44},{"icon":{"paths":["M512 864c194.402 0 352-157.598 352-352 0-46.519-9.024-90.932-25.417-131.582l48.516-48.518c26.214 54.496 40.9 115.584 40.9 180.1 0 229.751-186.249 416-416 416s-416-186.249-416-416c0-229.751 186.249-416 416-416 95.377 0 183.253 32.096 253.423 86.076l-45.709 45.712c-58.223-42.623-130.031-67.788-207.714-67.788-194.404 0-352 157.596-352 352 0 194.402 157.596 352 352 352zM902.63 230.624c12.497-12.499 12.493-32.76-0.009-45.255-12.497-12.495-32.759-12.491-45.252 0.008l-345.387 345.504-105.339-105.491c-12.488-12.506-32.749-12.52-45.255-0.032-12.506 12.489-12.52 32.747-0.032 45.253l127.971 128.158c6.003 6.007 14.144 9.387 22.635 9.387 8.495 0.004 16.636-3.371 22.643-9.374l368.026-368.156z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-check"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":527,"id":136,"name":"circle-check","prevSize":32,"code":59692},"setIdx":0,"setId":2,"iconIdx":45},{"icon":{"paths":["M608 192c0-53.019-42.982-96-96-96s-96 42.981-96 96h-192c-17.673 0-32 14.327-32 32v672c0 17.673 14.327 32 32 32h576c17.673 0 32-14.327 32-32v-672c0-17.673-14.327-32-32-32h-192zM256 864v-608h96v64c0 17.673 14.327 32 32 32h256c17.673 0 32-14.327 32-32v-64h96v608h-512zM512 224c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-32 14.327-32 32s14.327 32 32 32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clipboard"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":528,"id":135,"name":"clipboard","prevSize":32,"code":59693},"setIdx":0,"setId":2,"iconIdx":46},{"icon":{"paths":["M864 512c0 194.402-157.598 352-352 352-194.404 0-352-157.598-352-352 0-194.404 157.596-352 352-352 194.402 0 352 157.596 352 352zM928 512c0-229.751-186.249-416-416-416s-416 186.249-416 416c0 229.751 186.249 416 416 416s416-186.249 416-416zM544 288c0-17.673-14.327-32-32-32s-32 14.327-32 32v224c0 8.486 3.371 16.627 9.374 22.626l96 96c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-86.626-86.63v-210.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["clock"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":529,"id":134,"name":"clock","prevSize":32,"code":59694},"setIdx":0,"setId":2,"iconIdx":47},{"icon":{"paths":["M806.626 262.627c12.497-12.497 12.497-32.758 0-45.255s-32.755-12.497-45.252 0l-249.374 249.371-249.373-249.371c-12.497-12.497-32.758-12.497-45.255 0s-12.497 32.758 0 45.255l249.371 249.373-249.371 249.374c-12.497 12.497-12.497 32.755 0 45.252s32.758 12.497 45.255 0l249.373-249.37 249.374 249.37c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-249.37-249.374 249.37-249.373z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["close"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":530,"id":133,"name":"close","prevSize":32,"code":59695},"setIdx":0,"setId":2,"iconIdx":48},{"icon":{"paths":["M273.129 356.679c6.747-28.299 25.696-52.005 51.209-67.153 25.601-15.2 56.323-20.858 84.699-14.531 27.639 6.164 55.116 24.12 74.871 60.34l22.541 41.324 30.135-36.162c27.238-32.687 85.129-39.712 134.716-14.063 23.561 12.187 42.206 30.617 52.062 53.489 9.655 22.406 12.113 51.897-1.434 89.149l-15.616 42.935h45.687c53.606 0 82.419 15.881 97.643 33.749 15.65 18.377 21.897 44.651 18.556 74.718-3.345 30.084-16.055 60.514-33.455 82.889-17.941 23.066-36.651 32.644-50.743 32.644h-543.998c-18.791 0-37.067-10.359-52.194-31.578-15.217-21.342-24.977-51.051-25.818-81.31-0.84-30.229 7.238-57.916 23.734-77.491 15.795-18.748 42.268-33.621 86.278-33.621h59.792l-33.166-49.749c-27.882-41.825-32.118-77.815-25.498-105.58zM518.221 272.084c-26.342-32.073-59.669-51.619-95.255-59.554-45.624-10.173-92.902-0.833-131.301 21.965-38.487 22.85-69.538 60.142-80.791 107.342-8.278 34.72-5.369 72.759 10.731 111.77-35.67 8.465-64.099 26.185-84.825 50.782-29.004 34.423-39.927 78.737-38.767 120.508 1.159 41.741 14.399 84.032 37.682 116.689 23.373 32.785 59.097 58.423 104.305 58.423h543.999c41.907 0 77.201-26.419 101.261-57.353 24.597-31.629 41.886-73.195 46.545-115.115 4.659-41.933-3.098-87.659-33.446-123.285-24.102-28.297-59.503-46.771-105.613-53.453 5.833-35.195 1.685-67.675-10.607-96.206-16.644-38.627-46.997-67.197-81.438-85.009-54.899-28.397-128.734-32.652-182.481 2.495zM512 437.333c17.673 0 32 14.327 32 32v53.329h53.333c17.673 0 32 14.323 32 32 0 17.673-14.327 32-32 32h-53.333v53.338c0 17.673-14.327 32-32 32s-32-14.327-32-32v-53.338h-53.333c-17.672 0-31.999-14.327-31.999-32 0-17.677 14.327-32 31.999-32h53.333v-53.329c0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["cloud-connectivity"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":531,"id":132,"name":"cloud-connectivity","prevSize":32,"code":59696},"setIdx":0,"setId":2,"iconIdx":49},{"icon":{"paths":["M639.172 204.798c16.495 6.344 24.725 24.859 18.381 41.354l-213.333 554.667c-6.345 16.495-24.861 24.725-41.356 18.381s-24.724-24.862-18.38-41.357l213.335-554.666c6.34-16.495 24.858-24.724 41.353-18.38zM330.312 361.371c12.497 12.497 12.497 32.758 0 45.255l-105.373 105.374 105.373 105.37c12.497 12.497 12.497 32.759 0 45.257s-32.758 12.497-45.255 0l-128-128c-12.497-12.497-12.497-32.759 0-45.257l128-127.999c12.497-12.497 32.758-12.497 45.255 0zM711.723 361.371c12.497-12.497 32.759-12.497 45.257 0l128 127.999c12.497 12.497 12.497 32.759 0 45.257l-128 128c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.759 0-45.257l105.374-105.37-105.374-105.374c-12.497-12.497-12.497-32.758 0-45.255z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["code"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":532,"id":131,"name":"code","prevSize":32,"code":59697},"setIdx":0,"setId":2,"iconIdx":50},{"icon":{"paths":["M768 224c17.673 0 32 14.327 32 32v544c0 17.673-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32v-128c17.673 0 32-14.327 32-32s-14.327-32-32-32v-192c17.673 0 32-14.327 32-32s-14.327-32-32-32v-96c0-17.673 14.327-32 32-32h544zM128 608c-17.673 0-32 14.327-32 32s14.327 32 32 32v128c0 53.018 42.981 96 96 96h544c53.018 0 96-42.982 96-96v-544c0-53.019-42.982-96-96-96h-544c-53.019 0-96 42.981-96 96v96c-17.673 0-32 14.327-32 32s14.327 32 32 32v192zM425.205 514.266c13.315-3.567 27.309-3.789 40.732-0.644l31.518 7.39c8.73 2.044 17.83 1.903 26.492-0.418l22.182-5.943c14.289-3.823 29.329-4.036 43.682-0.674 40.431 9.476 69.325 45.585 69.325 87.279v24.337c0 31.812-25.788 57.6-57.6 57.6h-180.736c-31.812 0-57.6-25.788-57.6-57.6v-30.528c0-37.862 25.432-71.002 62.005-80.798zM454.251 563.473c-5.205-1.22-10.633-1.135-15.799 0.247-14.186 3.802-24.051 16.657-24.051 31.343v30.528c0 3.533 2.865 6.4 6.4 6.4h180.736c3.537 0 6.4-2.867 6.4-6.4v-24.337c0-17.749-12.365-33.34-29.807-37.427-6.182-1.451-12.659-1.353-18.752 0.282l-22.187 5.943c-16.811 4.501-34.475 4.779-51.418 0.806l-31.522-7.386zM536.448 420.48c0-13.962-11.315-25.28-25.28-25.28-13.961 0-25.28 11.319-25.28 25.28 0 13.96 11.319 25.28 25.28 25.28 13.965 0 25.28-11.319 25.28-25.28zM587.648 420.48c0 42.24-34.24 76.48-76.48 76.48s-76.48-34.24-76.48-76.48c0-42.239 34.24-76.48 76.48-76.48s76.48 34.242 76.48 76.48z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["contacts"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":533,"id":130,"name":"contacts","prevSize":32,"code":59698},"setIdx":0,"setId":2,"iconIdx":51},{"icon":{"paths":["M464 272c-35.345 0-64 28.654-64 64v480c0 35.345 28.655 64 64 64h352c35.345 0 64-28.655 64-64v-304c0-6.511-1.984-12.864-5.692-18.214l-134.455-194.215c-11.955-17.267-31.616-27.57-52.621-27.57h-223.232zM464 336h144v144c0 35.345 28.655 64 64 64h144v272h-352v-480zM672 480v-144h15.232l99.695 144h-114.927zM144 208c0-35.346 28.654-64 64-64h241.843c18.031 0 35.226 7.607 47.356 20.949l39.138 43.051h-328.337v480h128v64h-128c-35.346 0-64-28.655-64-64v-480z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["copy"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":534,"id":129,"name":"copy","prevSize":32,"code":59699},"setIdx":0,"setId":2,"iconIdx":52},{"icon":{"paths":["M822.242 183.279c-37.141-37.936-98.074-38.532-135.953-1.331l-348.017 341.786c-12.722 12.497-21.706 28.284-25.943 45.594l-23.65 96.623c-6.931 28.318 16.732 54.771 45.685 51.072l90.573-11.563c20.195-2.577 39.050-11.503 53.834-25.485l360.013-340.471c38.933-36.82 40.119-98.352 2.633-136.64l-19.174-19.584zM731.166 227.52c12.625-12.4 32.934-12.201 45.316 0.444l19.174 19.584c12.497 12.762 12.1 33.273-0.879 45.547l-69.871 66.081-63.364-63.28 69.623-68.376zM615.906 340.713l62.511 62.427-243.652 230.426c-4.928 4.659-11.211 7.633-17.943 8.495l-58.231 7.433 15.908-64.99c1.412-5.769 4.407-11.034 8.648-15.198l232.76-228.593zM192.001 265.251c0-17.65 14.327-31.958 32-31.958h280.959c17.673 0 32-14.308 32-31.958s-14.327-31.958-32-31.958h-280.959c-53.019 0-96 42.924-96 95.874v533.695c0 52.949 42.981 95.872 96 95.872h529.065c53.022 0 96-42.923 96-95.872v-279.044c0-17.651-14.327-31.962-32-31.962s-32 14.31-32 31.962v279.044c0 17.651-14.327 31.957-32 31.957h-529.065c-17.673 0-32-14.306-32-31.957v-533.695z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["create"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":535,"id":128,"name":"create","prevSize":32,"code":59700},"setIdx":0,"setId":2,"iconIdx":53},{"icon":{"paths":["M714.667 298.663c0-17.673-14.327-32-32-32s-32 14.327-32 32v426.666c0 17.673 14.327 32 32 32s32-14.327 32-32v-426.666z","M512 394.671c17.673 0 32 14.327 32 32v298.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-298.667c0-17.673 14.327-32 32-32z","M373.333 554.671c0-17.673-14.327-32-32-32s-32 14.327-32 32v170.667c0 17.677 14.327 32 32 32s32-14.323 32-32v-170.667z","M128 199.555v624.888c0 39.518 32.037 71.556 71.555 71.556h624.888c39.518 0 71.556-32.038 71.556-71.556v-624.888c0-39.519-32.038-71.555-71.556-71.555h-624.888c-39.519 0-71.555 32.037-71.555 71.555zM199.555 192h624.888c4.173 0 7.556 3.383 7.556 7.555v624.888c0 4.173-3.383 7.556-7.556 7.556h-624.888c-4.173 0-7.555-3.383-7.555-7.556v-624.888c0-4.173 3.383-7.555 7.555-7.555z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["dashboard"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":536,"id":127,"name":"dashboard","prevSize":32,"code":59701},"setIdx":0,"setId":2,"iconIdx":54},{"icon":{"paths":["M736 864.209v-447.408h-448v447.408h448zM224 864.209v-447.408c-35.346 0-64-28.616-64-63.916v-127.831c0-35.299 28.654-63.916 64-63.916h224c0-35.299 28.655-63.915 64-63.915s64 28.616 64 63.915h224c35.345 0 64 28.616 64 63.916v127.831c0 35.299-28.655 63.916-64 63.916v447.408c0 35.298-28.655 63.915-64 63.915h-448c-35.346 0-64-28.617-64-63.915zM800 225.055h-576v127.831h576v-127.831zM416 544.631v191.748c0 17.647 14.327 31.957 32 31.957s32-14.31 32-31.957v-191.748c0-17.647-14.327-31.957-32-31.957s-32 14.31-32 31.957zM576 512.674c17.673 0 32 14.31 32 31.957v191.748c0 17.647-14.327 31.957-32 31.957s-32-14.31-32-31.957v-191.748c0-17.647 14.327-31.957 32-31.957z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["delete"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":537,"id":126,"name":"delete","prevSize":32,"code":59702},"setIdx":0,"setId":2,"iconIdx":55},{"icon":{"paths":["M341.335 778.671c-17.673 0-32 14.327-32 32s14.327 32 32 32h341.332c17.673 0 32-14.327 32-32s-14.327-32-32-32h-341.332z","M85.335 298.672c0-70.692 57.308-128 128-128h597.332c70.694 0 128 57.308 128 128v298.666c0 70.694-57.306 128-128 128h-597.332c-70.693 0-128-57.306-128-128v-298.666zM213.335 234.672c-35.346 0-64 28.654-64 64v298.666c0 35.349 28.654 64 64 64h597.332c35.349 0 64-28.655 64-64v-298.666c0-35.346-28.651-64-64-64h-597.332z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["desktop"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":538,"id":125,"name":"desktop","prevSize":32,"code":59703},"setIdx":0,"setId":2,"iconIdx":56},{"icon":{"paths":["M320 159.791c0-17.649-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.916c0 17.65 14.327 31.958 32 31.958h64c17.673 0 32-14.308 32-31.958v-63.916zM320 585.894c0-17.651-14.327-31.957-32-31.957h-64c-17.673 0-32 14.306-32 31.957v63.915c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.915zM448 159.791c0-17.649 14.327-31.958 32-31.958h64c17.673 0 32 14.308 32 31.958v63.916c0 17.65-14.327 31.958-32 31.958h-64c-17.673 0-32-14.308-32-31.958v-63.916zM576 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914zM448 585.894c0-17.651 14.327-31.957 32-31.957h64c17.673 0 32 14.306 32 31.957v63.915c0 17.651-14.327 31.957-32 31.957h-64c-17.673 0-32-14.306-32-31.957v-63.915zM576 798.946c0-17.651-14.327-31.957-32-31.957h-64c-17.673 0-32 14.306-32 31.957v63.915c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.915zM704 159.791c0-17.649 14.327-31.958 32-31.958h64c17.673 0 32 14.308 32 31.958v63.916c0 17.65-14.327 31.958-32 31.958h-64c-17.673 0-32-14.308-32-31.958v-63.916zM832 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914zM704 585.894c0-17.651 14.327-31.957 32-31.957h64c17.673 0 32 14.306 32 31.957v63.915c0 17.651-14.327 31.957-32 31.957h-64c-17.673 0-32-14.306-32-31.957v-63.915zM320 372.843c0-17.65-14.327-31.958-32-31.958h-64c-17.673 0-32 14.308-32 31.958v63.914c0 17.651 14.327 31.957 32 31.957h64c17.673 0 32-14.306 32-31.957v-63.914z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["dialpad"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":539,"id":124,"name":"dialpad","prevSize":32,"code":59704},"setIdx":0,"setId":2,"iconIdx":57},{"icon":{"paths":["M512 96c80.094 0 154.901 22.636 218.377 61.859l-46.746 46.746c-24.738-13.843-51.324-24.785-79.287-32.368 12.169 22.394 23.078 49.096 32.405 79.249l-51.998 51.999c-5.658-22.993-12.169-44.004-19.349-62.666-12.821-33.338-26.522-55.927-38.473-69.070-8.397-9.235-13.431-11.29-14.929-11.698-1.498 0.407-6.532 2.463-14.929 11.698-11.951 13.143-25.651 35.731-38.473 69.070-16.299 42.377-29.175 96.867-36.34 159.182h65.976l-64 63.999h-7.114c-0.116 2.47-0.224 4.949-0.322 7.437l-64.536 64.538c-0.174-7.936-0.262-15.927-0.262-23.974 0-16.23 0.358-32.247 1.054-48h-189.809c-2.14 15.697-3.245 31.718-3.245 48 0 33.28 4.619 65.485 13.251 96h106.984l-64 64h-17.852c2.004 3.921 4.079 7.799 6.223 11.631l-46.746 46.746c-39.223-63.475-61.859-138.283-61.859-218.377 0-229.751 186.249-416 416-416zM866.142 293.623l-46.746 46.746c10.56 18.873 19.43 38.819 26.411 59.632h-86.046l-64 63.999h164.992c2.142 15.697 3.247 31.718 3.247 48 0 33.28-4.617 65.485-13.252 96h-183.027c2.799-30.814 4.279-62.959 4.279-96 0-8.047-0.085-16.038-0.26-23.974l-64.538 64.538c-0.751 19.008-2.022 37.517-3.763 55.437h-51.678l-64 64h107.093c-7.369 42.458-17.489 80.081-29.453 111.185-12.821 33.335-26.522 55.923-38.473 69.069-8.397 9.233-13.431 11.29-14.929 11.695-1.498-0.405-6.532-2.462-14.929-11.695-11.951-13.146-25.651-35.733-38.473-69.069-7.177-18.662-13.692-39.676-19.349-62.673l-51.998 52.002c9.327 30.153 20.238 56.853 32.404 79.249-27.964-7.582-54.546-18.526-79.285-32.367l-46.746 46.746c63.347 39.142 137.984 61.769 217.899 61.858h0.956c229.53-0.26 415.522-186.411 415.522-416 0-80.094-22.635-154.903-61.858-218.377zM419.657 172.237c-113.551 30.788-204.311 116.98-241.465 227.764h179.674c10-93.227 32.176-173.254 61.791-227.764zM604.348 851.763c24.393-44.902 43.738-107.119 55.39-179.763h165.879c-44.651 87.347-124.723 153.583-221.269 179.763zM825.374 153.373c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-671.999 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l672.001-672.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["directory-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":540,"id":123,"name":"directory-disabled","prevSize":32,"code":59705},"setIdx":0,"setId":2,"iconIdx":58},{"icon":{"paths":["M864 511.326c0 33.237-4.621 65.399-13.252 95.876h-183.027c2.799-30.78 4.279-62.878 4.279-95.876 0-16.209-0.358-32.205-1.054-47.936h189.807c2.142 15.676 3.247 31.676 3.247 47.936zM606.882 463.39c0.734 15.65 1.118 31.646 1.118 47.936 0 33.353-1.604 65.468-4.561 95.876h-182.876c-2.958-30.409-4.562-62.524-4.562-95.876 0-16.29 0.383-32.286 1.12-47.936h189.762zM666.133 399.476c-9.997-93.104-32.175-173.025-61.79-227.464 113.553 30.747 204.309 116.826 241.463 227.464h-179.674zM419.657 172.012c-29.615 54.438-51.791 134.359-61.791 227.464h-179.674c37.155-110.638 127.914-196.717 241.465-227.464zM422.259 399.476c7.164-62.232 20.041-116.651 36.34-158.972 12.821-33.295 26.522-55.853 38.473-68.979 8.397-9.223 13.431-11.276 14.929-11.683 1.498 0.407 6.532 2.46 14.929 11.683 11.951 13.126 25.651 35.684 38.473 68.979 16.299 42.321 29.175 96.739 36.339 158.972h-179.482zM353.054 463.39c-0.696 15.731-1.054 31.727-1.054 47.936 0 32.998 1.482 65.097 4.282 95.876h-183.031c-8.632-30.477-13.251-62.639-13.251-95.876 0-16.26 1.105-32.26 3.245-47.936h189.809zM364.263 671.117c11.652 72.55 30.999 134.682 55.392 179.524-96.549-26.146-176.621-92.292-221.273-179.524h165.881zM512.478 926.775c229.53-0.256 415.522-186.163 415.522-415.45 0-229.447-186.249-415.451-416-415.451s-416 186.004-416 415.451c0 229.291 185.992 415.194 415.522 415.45 0.158 0.004 0.32 0.004 0.478 0.004s0.32 0 0.478-0.004zM604.348 850.641c24.393-44.843 43.738-106.978 55.39-179.524h165.879c-44.651 87.232-124.723 153.378-221.269 179.524zM594.854 671.117c-7.369 42.398-17.489 79.974-29.453 111.036-12.821 33.293-26.522 55.851-38.473 68.979-8.397 9.22-13.431 11.273-14.929 11.682-1.498-0.41-6.532-2.462-14.929-11.682-11.951-13.129-25.651-35.686-38.473-68.979-11.964-31.061-22.080-68.638-29.453-111.036h165.709z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["directory"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":541,"id":122,"name":"directory","prevSize":32,"code":59706},"setIdx":0,"setId":2,"iconIdx":59},{"icon":{"paths":["M561.668 127.99c-121.698 0-200.973 50.509-248.815 115.801-46.216 63.073-61.638 137.752-61.933 188.615-8.705 98.748 28.601 172.898 89.423 223.471 59.685 49.626 140.202 75.26 217.847 83.738 77.751 8.486 161.715 1.195 227.857-12.548 33.067-6.869 62.775-15.578 85.641-25.331 11.366-4.847 22.029-10.368 30.797-16.67 7.876-5.662 18.543-14.976 23.245-28.834 7.283-21.474-3.136-38.784-11.968-48.102-8.41-8.879-19.426-15.403-28.237-20.015-9.306-4.868-19.379-9.207-27.533-12.71-8.977-3.853-14.861-6.396-18.56-8.431-4.847-2.662-8.845-5.086-12.028-7.202 20.254-47.142 31.548-77.043 37.696-99.191 7.292-26.27 7.313-41.723 7.313-58.907 0-21.523-9.6-88.542-52.809-151.109-44.702-64.73-124.070-122.573-257.937-122.573zM314.916 433.894c0-40.414 12.606-101.841 49.562-152.276 35.783-48.835 95.882-89.628 197.19-89.628 112.085 0 172.087 46.886 205.274 94.94 34.679 50.219 41.472 104.040 41.472 114.741v0.2c0.004 14.918 0.004 23.638-4.979 41.594-5.491 19.776-16.964 50.189-41.54 106.534-5.798 13.295-2.761 25.771 1.156 33.754 3.665 7.479 9.003 13.244 13.329 17.263 8.836 8.213 20.655 15.684 32.226 22.046 6.618 3.635 15.236 7.334 22.562 10.475l1.562 0.67c5.905 2.539 11.52 4.971 16.687 7.42-0.909 0.41-1.856 0.823-2.833 1.237-17.741 7.569-43.081 15.206-73.557 21.542-60.945 12.663-138.065 19.209-207.885 11.584-69.926-7.633-136.986-30.336-183.881-69.325-45.46-37.798-73.674-92.066-66.481-169.822l0.136-1.472v-1.476zM819.162 553.348l-0.073-0.081c0 0 0.009 0.009 0.026 0.034 0.013 0.013 0.026 0.030 0.047 0.047z","M178.552 502.468c7.496-11.255 16.26-22.259 26.436-32.589 0.876 31.744 6.169 61.227 15.216 88.358-15.094 30.878-18.374 59.315-18.374 65.357v0.188c-0 11.379-0 17.532 3.523 30.699 3.993 14.916 12.441 38.212 30.867 82.022 5.256 12.497 2.48 24.098-0.985 31.428-3.249 6.874-7.925 12.058-11.511 15.514-7.319 7.057-16.852 13.257-25.751 18.33-5.008 2.854-11.333 5.705-16.546 8.026 11.182 3.942 24.979 7.817 40.803 11.226 44.976 9.694 101.833 14.673 153.062 8.87 51.281-5.807 99.779-23.014 133.353-51.964l0.606-0.525c14.793 2.782 29.53 4.937 44.053 6.519 10.88 1.19 21.858 2.091 32.875 2.722-10.121 14.797-22.165 28.045-35.742 39.753-46.362 39.974-108.544 60.365-167.945 67.089-59.451 6.729-123.406 0.947-173.745-9.899-25.169-5.427-48.056-12.352-65.902-20.245-8.86-3.917-17.457-8.503-24.686-13.892-6.431-4.791-15.831-13.171-20.001-25.92-6.422-19.631 2.792-35.443 10.458-43.831 7.193-7.872 16.405-13.461 23.247-17.173 7.309-3.968 15.158-7.467 21.235-10.176 6.906-3.076 10.854-4.855 13.183-6.182 1.35-0.768 2.59-1.502 3.727-2.197-13.937-33.886-21.975-56.119-26.479-72.947-5.68-21.222-5.7-33.873-5.7-47.433 0-17.711 7.436-71.138 40.721-121.126zM155.337 797.244c-0.012 0.004 0.084 0.107 0.317 0.303-0.19-0.205-0.306-0.303-0.317-0.303zM179.587 737.084c-0.003 0-0.052 0.051-0.137 0.149l0.112-0.119c0.019-0.021 0.027-0.030 0.025-0.030z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["discussions"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":542,"id":121,"name":"discussions","prevSize":32,"code":59707},"setIdx":0,"setId":2,"iconIdx":60},{"icon":{"paths":["M160 256c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32zM160 421.161c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32.001-32 32.001h-640c-17.673 0-32-14.327-32-32.001zM160 602.837c0-17.673 14.327-32 32-32h640c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32zM160 768c0-17.673 14.327-32 32-32h344.614c17.673 0 32 14.327 32 32s-14.327 32-32 32h-344.614c-17.673 0-32-14.327-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["document"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":543,"id":120,"name":"document","prevSize":32,"code":59708},"setIdx":0,"setId":2,"iconIdx":61},{"icon":{"paths":["M129.353 351.541c0-17.65 14.327-31.958 32-31.958h704c17.673 0 32 14.308 32 31.958s-14.327 31.958-32 31.958h-704c-17.673 0-32-14.308-32-31.958zM214.686 521.984c0-17.651 14.327-31.957 32-31.957h533.333c17.673 0 32 14.306 32 31.957s-14.327 31.957-32 31.957h-533.333c-17.673 0-32-14.306-32-31.957zM332.019 660.467c-17.673 0-32 14.306-32 31.957 0 17.647 14.327 31.957 32 31.957h362.667c17.673 0 32-14.31 32-31.957 0-17.651-14.327-31.957-32-31.957h-362.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["donner"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":544,"id":119,"name":"donner","prevSize":32,"code":59709},"setIdx":0,"setId":2,"iconIdx":62},{"icon":{"paths":["M273.128 356.679c-6.619 27.765-2.384 63.755 25.498 105.58l21.374 32.060v17.69h-48c-44.011 0-70.483 14.874-86.278 33.621-16.496 19.575-24.573 47.262-23.734 77.491 0.841 30.259 10.601 59.968 25.818 81.31 15.127 21.218 33.403 31.578 52.194 31.578h80v64h-80c-45.209 0-80.933-25.638-104.305-58.423-23.283-32.657-36.523-74.948-37.682-116.689-1.161-41.771 9.763-86.084 38.767-120.508 20.726-24.597 49.155-42.317 84.825-50.782-16.1-39.010-19.009-77.050-10.731-111.77 11.253-47.2 42.304-84.492 80.791-107.342 38.399-22.798 85.676-32.138 131.301-21.965 35.587 7.935 68.909 27.48 95.252 59.554 53.751-35.147 127.582-30.892 182.485-2.495 34.436 17.812 64.794 46.382 81.438 85.009 12.292 28.531 16.435 61.011 10.603 96.206 46.114 6.682 81.51 25.156 105.617 53.453 30.349 35.627 38.106 81.353 33.446 123.285-4.659 41.92-21.948 83.486-46.545 115.115-24.060 30.933-59.354 57.353-101.261 57.353h-80v-64h80c14.093 0 32.802-9.579 50.739-32.644 17.404-22.374 30.114-52.804 33.455-82.889 3.341-30.067-2.901-56.341-18.556-74.718-15.219-17.869-44.032-33.749-97.638-33.749h-45.687l15.616-42.935c13.542-37.252 11.089-66.743 1.434-89.149-9.856-22.872-28.501-41.302-52.062-53.489-49.587-25.649-107.477-18.624-134.716 14.063l-30.135 36.162-22.541-41.324c-19.759-36.22-47.232-54.176-74.872-60.34-28.375-6.327-59.098-0.669-84.699 14.531-25.513 15.148-44.462 38.854-51.209 67.153zM630.98 787.221c12.297-12.689 11.981-32.947-0.708-45.248-12.693-12.301-32.951-11.985-45.252 0.708l-41.020 42.321v-273.003c0-17.673-14.327-32-32-32s-32 14.327-32 32v273.003l-41.020-42.321c-12.301-12.693-32.561-13.009-45.251-0.708s-13.007 32.559-0.707 45.248l95.998 99.051c6.029 6.217 14.319 9.728 22.98 9.728s16.951-3.511 22.98-9.728l96-99.051z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["download"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":545,"id":118,"name":"download","prevSize":32,"code":59710},"setIdx":0,"setId":2,"iconIdx":63},{"icon":{"paths":["M794.598 131.828c-24.994-24.994-65.515-24.994-90.509 0l-529.769 529.769c-9.564 9.562-15.852 21.909-17.962 35.273l-19.585 124.019c-1.384 8.764-0.941 17.28 1.005 25.229 7.587 30.997 38.032 53.367 72.195 47.974l124.022-19.584c13.36-2.112 25.708-8.401 35.272-17.963l529.771-529.769c12.497-12.497 18.743-28.877 18.743-45.257 0-10.365-2.505-20.73-7.509-30.111-2.901-5.443-6.647-10.556-11.238-15.144l-104.435-104.437zM629.845 296.585l119.497-119.5 104.439 104.436-119.501 119.5-104.435-104.436zM199.991 830.874l19.585-124.019 365.013-365.015 104.435 104.436-365.012 365.013-124.021 19.584z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":546,"id":117,"name":"edit","prevSize":32,"code":59711},"setIdx":0,"setId":2,"iconIdx":64},{"icon":{"paths":["M332.328 644.885c-21.571-19.618-6.007-52.885 23.151-52.885 8.965 0 17.527 3.529 24.272 9.438 21.605 18.918 42.641 31.957 62.702 40.508 36.565 15.59 71.808 17.109 104.794 9.941 37.632-8.179 72.657-27.81 102.11-51.345 6.724-5.376 14.997-8.542 23.607-8.542 30.217 0 45.615 34.257 22.396 53.594-36.911 30.741-82.79 57.591-134.519 68.834-44.8 9.732-93.67 7.629-143.486-13.606-28.791-12.275-57.234-30.656-85.026-55.936z","M402.423 480c35.345 0 64-28.655 64-64 0-35.346-28.655-64-64-64s-64 28.654-64 64c0 35.345 28.654 64 64 64z","M626.423 480c35.345 0 64-28.655 64-64 0-35.346-28.655-64-64-64s-64 28.654-64 64c0 35.345 28.655 64 64 64z","M930.257 512c0 229.751-186.253 416-416 416-229.752 0-416.002-186.249-416.002-416s186.249-416 416.002-416c229.747 0 416 186.249 416 416zM866.257 512c0-194.404-157.598-352-352-352-194.406 0-352.002 157.596-352.002 352 0 194.402 157.596 352 352.002 352 194.402 0 352-157.598 352-352z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["emoji"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":547,"id":116,"name":"emoji","prevSize":32,"code":59712},"setIdx":0,"setId":2,"iconIdx":65},{"icon":{"paths":["M655.292 512c88.367 0 160-71.633 160-160 0-88.366-71.633-160-160-160s-160 71.634-160 160c0 26.934 6.656 52.313 18.411 74.583l-11.913 11.915 0.128 0.128-299.136 299.136c-3.533 9.924-6.409 20.975-7.738 31.607-1.652 13.218-0.562 22.967 1.874 28.937 1.768 4.335 4.22 7.232 10.772 9.015 8.316 2.261 24.518 2.795 52.822-5.504 8.524-3.806 27.721-16.282 45.133-35.379 18.18-19.938 29.647-41.856 29.647-62.438 0-15.642 11.309-28.992 26.739-31.565l91.751-15.292c3.767-2.658 18.765-15.693 10.129-58.867-2.095-10.492 1.186-21.338 8.751-28.902l88.201-88.196c26.466 19.379 59.11 30.822 94.43 30.822zM439.121 410.916c-5.107-18.773-7.829-38.527-7.829-58.916 0-123.712 100.288-224 224-224s224 100.288 224 224c0 123.712-100.288 224-224 224-29.286 0-57.263-5.619-82.906-15.842l-43.004 43.008c7.155 65.493-23.996 104.533-55.974 115.191l-2.381 0.794-74.855 12.476c-7.083 31.407-25.173 58.125-43.235 77.935-23.122 25.357-50.958 44.629-69.762 52.151l-1.324 0.529-1.365 0.41c-34.714 10.415-64.73 13.188-89.594 6.426-26.782-7.283-44.33-24.785-53.228-46.583-8.23-20.164-8.474-42.283-6.126-61.065 2.402-19.217 7.899-37.956 14.042-53.312 1.609-4.023 4.020-7.68 7.084-10.743l286.458-286.458zM623.292 336c0 26.51 21.491 48 48 48s48-21.49 48-48c0-26.51-21.491-48-48-48s-48 21.49-48 48z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["encrypted"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":548,"id":115,"name":"encrypted","prevSize":32,"code":59713},"setIdx":0,"setId":2,"iconIdx":66},{"icon":{"paths":["M416 586.445v177.233l75.516-62.929c11.866-9.89 29.103-9.89 40.969 0l75.516 62.929v-177.233c-29.094 13.82-61.645 21.555-96 21.555s-66.906-7.735-96-21.555zM352 540.766v291.234c0 12.416 7.183 23.71 18.427 28.979 11.244 5.265 24.521 3.554 34.059-4.395l107.514-89.596 107.516 89.596c9.536 7.949 22.814 9.66 34.057 4.395 11.247-5.269 18.428-16.563 18.428-28.979v-291.234c39.59-40.401 64-95.731 64-156.766 0-123.712-100.288-224-224-224s-224 100.288-224 224c0 61.035 24.41 116.365 64 156.766zM416 512.013l-0.015-0.013c-38.854-29.193-63.985-75.661-63.985-128 0-88.366 71.634-160 160-160 88.367 0 160 71.634 160 160 0 52.34-25.131 98.808-63.983 128l-0.017 0.013c-26.739 20.083-59.981 31.987-96 31.987s-69.261-11.904-96-31.987z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["enterprise-feature"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":549,"id":114,"name":"enterprise-feature","prevSize":32,"code":59714},"setIdx":0,"setId":2,"iconIdx":67},{"icon":{"paths":["M514.338 128c213.35 0 386.334 172.985 386.334 386.338 0 192.866-141.257 352.704-325.978 381.662v-269.961h90.027l17.114-111.701h-107.14v-72.474c0-11.46 2.108-22.822 7.275-32.544 2.475-4.655 5.653-8.932 9.638-12.665 9.865-9.246 24.687-15.152 46.054-15.152h48.734v-95.080c0 0-44.224-7.552-86.49-7.552-84.698 0-141.261 49.314-145.655 138.908-0.183 3.737-0.273 7.545-0.273 11.421v85.137h-98.12v111.701h98.12v269.961c-184.721-29.013-325.978-188.847-325.978-381.662 0-213.353 172.985-386.338 386.338-386.338z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["facebook-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":550,"id":113,"name":"facebook-monochromatic","prevSize":32,"code":59715},"setIdx":0,"setId":2,"iconIdx":68},{"icon":{"paths":["M373.333 448c0-17.673 14.327-32 32-32h213.334c17.673 0 32 14.327 32 32s-14.327 32-32 32h-213.334c-17.673 0-32-14.327-32-32z","M405.333 544c-17.673 0-32 14.327-32 32s14.327 32 32 32h213.334c17.673 0 32-14.327 32-32s-14.327-32-32-32h-213.334z","M256 128c-17.673 0-32 14.327-32 32v704c0 17.673 14.327 32 32 32h512c17.673 0 32-14.327 32-32v-501.745c0-6.67-2.082-13.172-5.961-18.6l-144.469-202.255c-6.003-8.41-15.701-13.4-26.039-13.4h-367.531zM736 372.51v459.49h-448v-640h319.066l128.934 180.51z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-document"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":551,"id":112,"name":"file-document","prevSize":32,"code":59716},"setIdx":0,"setId":2,"iconIdx":69},{"icon":{"paths":["M128 192c-17.673 0-32 14.327-32 32v576.021c0 17.673 14.327 32 32 32h768c17.673 0 32-14.327 32-32v-576.021c0-17.673-14.327-32-32-32h-768zM160 378.302v-122.302h149.333v122.302h-149.333zM160 442.304h149.333v141.722h-149.333v-141.722zM160 648.026h149.333v119.996h-149.333v-119.996zM373.333 768.021v-119.996h490.667v119.996h-490.667zM864 584.026h-490.667v-141.722h490.667v141.722zM864 378.302h-490.667v-122.302h490.667v122.302z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["file-sheet"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":552,"id":111,"name":"file-sheet","prevSize":32,"code":59717},"setIdx":0,"setId":2,"iconIdx":70},{"icon":{"paths":["M202.342 293.308c-30.381-42.346-0.116-101.308 52.001-101.308h517.886c51.11 0 81.6 56.951 53.265 99.484l-180.442 270.85v220.467c0 46.293-47.629 77.274-89.946 58.505l-121.263-53.777c-23.137-10.261-38.054-33.195-38.054-58.505v-166.084l-193.448-269.632zM772.228 256h-517.886l193.449 269.632c7.804 10.876 11.998 23.923 11.998 37.308v166.084l121.263 53.777v-220.467c0-12.629 3.738-24.977 10.739-35.486l180.437-270.848z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["filter"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":553,"id":110,"name":"filter","prevSize":32,"code":59718},"setIdx":0,"setId":2,"iconIdx":71},{"icon":{"paths":["M623.091 896c-2.803 0-5.299-0.623-7.168-0.939-61.999-16.858-102.818-39.646-145.813-80.858-55.147-54.007-85.369-125.815-85.369-202.3 0-65.873 56.705-119.573 126.495-119.573 69.794 0 126.498 53.7 126.498 119.573 0 34.965 31.467 63.373 69.79 63.373s69.794-28.407 69.794-63.373c0-135.808-119.646-246.636-266.705-246.636-104.997 0-200.337 57.132-243.334 145.793-14.332 29.35-21.498 63.066-21.498 100.843 0 28.719 2.493 73.365 24.925 131.746 2.804 7.181 2.493 14.673-0.623 21.854-3.116 6.865-9.036 11.861-16.201 14.357-2.804 1.25-6.231 1.562-9.658 1.562-11.84 0-22.433-7.181-26.484-18.108-19.005-50.261-28.352-99.9-28.352-151.727 0-46.204 9.036-88.35 26.795-125.5 52.343-108.020 167.935-177.64 294.431-177.64 178.53 0 323.721 135.805 323.721 302.828 0 65.873-56.704 119.573-126.81 119.573-70.101 0-126.805-53.7-126.805-119.573 0-34.965-31.471-63.373-69.794-63.373-38.635 0-69.79 28.407-69.79 63.373 0 61.504 24.303 119.262 68.544 162.342 35.209 34.031 68.855 53.073 120.576 67.123 7.168 1.873 13.397 6.554 17.139 13.111 3.738 6.558 4.672 14.362 2.803 21.231-2.803 11.861-14.020 20.915-27.106 20.915zM426.803 888.196c-7.79 0-15.267-3.123-20.253-8.742-33.338-32.781-51.72-54.012-77.892-100.527-26.795-47.142-41.127-105.212-41.127-167.339 0-116.446 100.948-211.354 224.95-211.354 124.006 0 224.956 94.908 224.956 211.354 0 15.612-12.463 28.1-28.356 28.1-15.889 0-28.663-12.177-28.663-28.1 0-85.538-75.401-155.157-168.247-155.157-92.846 0-168.245 69.619-168.245 155.157 0 52.45 11.84 100.527 34.272 139.554 23.368 41.519 38.946 59.004 68.855 88.661 10.906 11.238 10.906 28.723 0 39.65-5.918 5.931-13.086 8.742-20.25 8.742zM699.738 818.889c-47.36 0-88.798-11.866-123.383-34.965-59.196-39.65-94.716-103.962-94.716-172.335 0-15.607 12.463-28.096 28.352-28.096s28.352 12.489 28.352 28.096c0 49.643 26.172 96.781 69.794 125.504 25.237 16.858 55.147 24.977 91.601 24.977 7.787 0 22.43-0.939 38.012-3.746 1.557-0.311 3.426-0.311 4.983-0.311 13.709 0 25.237 9.988 27.729 23.411 1.246 7.181-0.311 14.673-4.361 20.608-4.365 6.242-10.906 10.611-18.697 11.861-23.364 4.685-43.93 4.996-47.667 4.996zM188.765 435.823c-5.608 0-11.216-1.562-16.201-4.992-6.543-4.062-10.593-10.616-12.151-18.109-1.246-7.493 0.311-14.985 4.985-21.229 38.634-53.698 87.862-95.844 146.125-125.502 60.133-30.595 129.612-46.829 200.96-46.829 71.040 0 140.207 15.922 200.030 46.205 58.573 29.658 107.802 71.493 146.125 124.566 4.361 5.932 6.229 13.425 4.983 20.917s-5.606 14.049-11.84 18.42c-4.983 3.435-10.594 4.992-16.512 4.992-9.037 0-17.758-4.369-23.057-11.861-33.335-45.893-75.712-82.107-125.559-107.083-52.343-26.224-112.789-40.273-174.481-40.273-62.31 0-122.756 14.049-175.1 40.585-49.851 25.912-92.535 62.127-126.185 108.644-3.739 6.869-12.463 11.55-22.121 11.55zM733.696 239.142c-4.672 0-9.344-1.249-13.397-3.434-71.347-35.902-133.35-51.512-207.505-51.512-74.462 0-144.254 17.483-207.814 51.825-4.050 2.185-8.724 3.122-13.397 3.122-10.282 0-19.629-5.62-24.925-14.361-3.739-6.556-4.674-14.361-2.493-21.542s7.166-13.424 13.709-16.858c72.596-38.712 151.733-58.38 234.921-58.38 82.569 0 154.85 17.795 233.988 58.068 6.857 3.434 11.84 9.366 14.332 16.858 2.185 7.18 1.246 14.673-2.18 21.229-4.983 9.053-14.643 14.985-25.237 14.985z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["fingerprint"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":554,"id":109,"name":"fingerprint","prevSize":32,"code":59719},"setIdx":0,"setId":2,"iconIdx":72},{"icon":{"paths":["M266.667 170.667c0-17.673 14.327-32 32-32h500.621c12.122 0 23.202 6.848 28.625 17.689 5.419 10.841 4.25 23.814-3.025 33.511l-122.133 162.845 122.133 162.843c7.275 9.698 8.444 22.673 3.025 33.51-5.423 10.842-16.503 17.69-28.625 17.69h-468.621v286.579c0 17.673-14.327 32-32 32s-32-14.327-32-32v-682.667zM330.667 502.754h404.621l-98.133-130.843c-8.533-11.378-8.533-27.023 0-38.4l98.133-130.845h-404.621v300.087z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["flag"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":555,"id":108,"name":"flag","prevSize":32,"code":59720},"setIdx":0,"setId":2,"iconIdx":73},{"icon":{"paths":["M138.667 213.333c0-17.673 14.327-32 32-32h234.057c7.183 0 14.156 2.416 19.798 6.86l88.814 69.94h339.998c17.673 0 32 14.327 32 32v520.533c0 17.673-14.327 32-32 32h-682.667c-17.673 0-32-14.327-32-32v-597.333zM202.667 245.333v533.333h618.667v-456.533h-319.087c-7.181 0-14.153-2.416-19.797-6.86l-88.813-69.94h-190.97z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["folder"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":556,"id":107,"name":"folder","prevSize":32,"code":59721},"setIdx":0,"setId":2,"iconIdx":74},{"icon":{"paths":["M370.254 544c0-17.673-14.327-32-32-32s-32 14.327-32 32v32h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v32c0 17.673 14.327 32 32 32s32-14.327 32-32v-32h32c17.673 0 31.999-14.327 31.999-32s-14.326-32-31.999-32h-32v-32z","M746.253 624c30.929 0 56-25.071 56-56s-25.071-56-56-56c-30.925 0-56 25.071-56 56s25.075 56 56 56z","M674.253 664c0 30.929-25.071 56-56 56-30.925 0-56-25.071-56-56s25.075-56 56-56c30.929 0 56 25.071 56 56z","M706.253 128c0-17.673-14.327-32-32-32s-32 14.327-32 32v96h-128c-17.673 0-32 14.327-32 32v96h-191.999c-106.039 0-192 85.961-192 192v128c0 106.039 85.961 192 192 192h447.999c106.039 0 192-85.961 192-192v-128c0-106.039-85.961-192-192-192h-192v-64h128c17.673 0 32-14.327 32-32v-128zM866.253 544v128c0 70.694-57.306 128-128 128h-447.999c-70.693 0-128-57.306-128-128v-128c0-70.694 57.307-128 128-128h447.999c70.694 0 128 57.306 128 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["game"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":557,"id":106,"name":"game","prevSize":32,"code":59722},"setIdx":0,"setId":2,"iconIdx":75},{"icon":{"paths":["M508.006 170.646l23.036 24.005 23.633-24.005 74.667 0.041v75.855h74.667v75.855h74.667v26.519h0.004v455.125h-0.004v49.293h-522.675v-682.688h252.006zM554.671 246.5h-224.003v530.98h373.34v-379.23h-149.338v-151.75z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["giphy-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":558,"id":105,"name":"giphy-monochromatic","prevSize":32,"code":59723},"setIdx":0,"setId":2,"iconIdx":76},{"icon":{"paths":["M862.455 324.045c-35.2-60.31-82.944-108.058-143.249-143.253-60.314-35.197-126.161-52.792-197.581-52.792-71.415 0-137.28 17.6-197.58 52.792-60.31 35.194-108.054 82.943-143.253 143.253-35.194 60.308-52.792 126.165-52.792 197.568 0 85.773 25.024 162.901 75.086 231.407 50.056 68.51 114.721 115.917 193.99 142.225 9.227 1.711 16.058 0.508 20.499-3.584 4.443-4.096 6.662-9.225 6.662-15.369 0-1.024-0.088-10.249-0.259-27.678-0.176-17.429-0.259-32.631-0.259-45.606l-11.789 2.039c-7.516 1.378-16.998 1.963-28.446 1.796-11.442-0.162-23.321-1.361-35.619-3.588-12.304-2.21-23.748-7.334-34.342-15.364-10.588-8.030-18.104-18.543-22.547-31.518l-5.125-11.793c-3.416-7.851-8.794-16.576-16.142-26.138-7.347-9.57-14.778-16.055-22.294-19.473l-3.589-2.569c-2.391-1.707-4.61-3.767-6.662-6.157-2.050-2.389-3.585-4.779-4.61-7.172-1.027-2.398-0.176-4.365 2.562-5.905 2.737-1.545 7.685-2.291 14.864-2.291l10.247 1.532c6.834 1.37 15.287 5.461 25.371 12.297 10.078 6.835 18.362 15.718 24.856 26.645 7.863 14.012 17.335 24.691 28.446 32.038 11.101 7.347 22.294 11.017 33.568 11.017s21.010-0.858 29.214-2.556c8.195-1.711 15.884-4.279 23.062-7.693 3.075-22.903 11.446-40.495 25.112-52.796-19.473-2.048-36.983-5.129-52.535-9.229-15.542-4.1-31.604-10.761-48.173-19.998-16.578-9.22-30.331-20.672-41.262-34.334-10.932-13.666-19.904-31.612-26.904-53.815-7.003-22.212-10.505-47.838-10.505-76.881 0-41.348 13.5-76.538 40.493-105.584-12.645-31.088-11.451-65.94 3.585-104.55 9.909-3.079 24.604-0.768 44.078 6.917 19.477 7.689 33.738 14.275 42.796 19.736 9.058 5.459 16.316 10.085 21.784 13.837 31.782-8.881 64.58-13.322 98.406-13.322s66.633 4.441 98.415 13.322l19.477-12.295c13.316-8.204 29.043-15.722 47.142-22.556 18.112-6.831 31.957-8.712 41.532-5.633 15.369 38.612 16.738 73.461 4.092 104.55 26.991 29.045 40.495 64.243 40.495 105.587 0 29.039-3.516 54.746-10.509 77.129-6.997 22.387-16.047 40.316-27.149 53.82-11.115 13.5-24.956 24.862-41.523 34.082-16.576 9.225-32.64 15.885-48.183 19.989-15.548 4.105-33.058 7.189-52.531 9.237 17.762 15.373 26.641 39.633 26.641 72.772v108.139c0 6.14 2.138 11.268 6.413 15.369 4.271 4.092 11.017 5.295 20.245 3.58 79.279-26.304 143.945-73.711 193.997-142.221 50.048-68.506 75.081-145.634 75.081-231.407-0.017-71.394-17.621-137.247-52.8-197.555z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["github-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":559,"id":104,"name":"github-monochromatic","prevSize":32,"code":59724},"setIdx":0,"setId":2,"iconIdx":77},{"icon":{"paths":["M133.618 423.61h215.092l-92.537-284.607c-4.739-14.671-25.504-14.671-30.244 0l-92.311 284.607zM86.899 567.168l46.72-143.543h737.135l46.72 143.543c4.288 13.094-0.452 27.537-11.511 35.435l-403.776 293.41-403.776-293.41c-11.060-7.898-15.799-22.34-11.511-35.435zM655.663 423.61h215.091l-92.314-284.607c-4.74-14.671-25.502-14.671-30.242 0l-92.535 284.607z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["gitlab-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":560,"id":103,"name":"gitlab-monochromatic","prevSize":32,"code":59725},"setIdx":0,"setId":2,"iconIdx":78},{"icon":{"paths":["M648.499 170.667h-256.113l-264.387 460.8 124.984 221.867h534.915l124.983-221.867-264.384-460.8zM381.509 631.467l138.935-239.787 138.931 239.787h-277.866z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["google-drive-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":561,"id":102,"name":"google-drive-monochromatic","prevSize":32,"code":59726},"setIdx":0,"setId":2,"iconIdx":79},{"icon":{"paths":["M667.081 338.153c-39.799-38.051-90.415-57.425-146.795-57.425-100.014 0-184.669 67.549-214.865 158.313h-0.002c-7.679 23.036-12.042 47.646-12.042 72.951 0 25.31 4.364 49.92 12.044 72.96l0 0.009c30.196 90.761 114.851 158.31 214.865 158.31 51.665 0 95.65-13.615 130.035-36.655v-0.013c40.67-27.23 67.725-67.9 76.629-115.9h-206.665v-148.535h361.66c4.535 25.135 6.98 51.315 6.98 78.545 0 116.945-41.89 215.39-114.5 282.24v0.013c-63.535 58.65-150.46 93.035-254.14 93.035-150.109 0-279.97-86.054-343.156-211.55v-0.009c-26.007-51.84-40.844-110.485-40.844-172.45 0-61.961 14.836-120.61 40.844-172.45h0.004c63.187-125.494 193.046-211.542 343.152-211.542 103.505 0 190.43 38.051 256.93 100.015l-110.135 110.138z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["google-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":562,"id":101,"name":"google-monochromatic","prevSize":32,"code":59727},"setIdx":0,"setId":2,"iconIdx":80},{"icon":{"paths":["M509.333 728.098c-44.578-0.546-89.015-14.801-126.453-42.765h-38.88v338.667h-5.333v-334.895l-332.514 332.514-3.771-3.772 332.514-332.514h-334.896v-5.333h338.667v-38.882c-27.959-37.44-42.21-81.877-42.755-126.451h-295.912v-5.333h295.912c0.549-44.57 14.8-89.001 42.755-126.433v-38.9h-338.667v-5.333h334.896l-332.514-332.514 3.771-3.771 332.514 332.514v-334.895h5.333v338.667h38.903c37.433-27.951 81.865-42.2 126.43-42.746v-295.92h5.333v295.92c44.565 0.547 88.998 14.796 126.43 42.746h38.903v-338.667h5.333v334.896l332.514-332.515 3.772 3.771-332.514 332.514h334.895v5.333h-338.667v38.9c27.955 37.433 42.206 81.864 42.756 126.433h295.91v5.333h-295.91c-0.546 44.574-14.797 89.011-42.756 126.451v38.882h338.667v5.333h-334.895l332.514 332.514-3.772 3.772-332.514-332.514v334.895h-5.333v-338.667h-38.882c-37.436 27.964-81.873 42.219-126.451 42.765v295.902h-5.333v-295.902zM509.333 722.551v-37.218h-116.952c35.117 24.294 75.975 36.702 116.952 37.218zM385.018 680h124.315v-161.562l-144.327 144.329c6.388 6.229 13.075 11.977 20.012 17.233zM361.084 666.688l-13.312 13.312h28.26c-5.135-4.169-10.124-8.606-14.947-13.312zM361.235 658.995l144.326-144.329h-161.562v124.314c5.258 6.938 11.003 13.623 17.235 20.015zM344 647.966v28.262l13.313-13.312c-4.708-4.826-9.146-9.813-13.313-14.95zM338.667 631.616v-116.949h-37.208c0.514 40.977 12.917 81.83 37.208 116.949zM301.459 509.333h37.208v-116.931c-24.288 35.113-36.69 75.958-37.208 116.931zM344 385.038v124.295h161.562l-144.318-144.319c-6.235 6.391-11.983 13.082-17.243 20.023zM365.015 361.243l144.319 144.318v-161.562h-124.292c-6.943 5.26-13.634 11.008-20.026 17.243zM376.052 344h-28.281l13.321 13.321c4.827-4.711 9.82-9.152 14.96-13.321zM392.406 338.667h116.927v-37.199c-40.969 0.516-81.818 12.916-116.927 37.199zM514.667 301.467v37.199h116.928c-35.11-24.284-75.959-36.684-116.928-37.199zM638.959 344h-124.292v161.562l144.32-144.318c-6.396-6.236-13.086-11.984-20.028-17.244zM662.758 365.015l-144.32 144.319h161.562v-124.295c-5.261-6.941-11.008-13.632-17.242-20.023zM680 376.050v-28.279l-13.321 13.321c4.71 4.826 9.152 9.818 13.321 14.958zM685.333 392.402v116.931h37.21c-0.521-40.973-12.924-81.818-37.21-116.931zM722.543 514.667h-37.21v116.949c24.29-35.119 36.693-75.972 37.21-116.949zM680 638.98v-124.314h-161.562l144.329 144.329c6.229-6.391 11.977-13.077 17.233-20.015zM658.995 662.767l-144.329-144.329v161.562h124.314c6.938-5.257 13.623-11.004 20.015-17.233zM647.97 680h28.258l-13.312-13.312c-4.821 4.706-9.813 9.143-14.946 13.312zM631.62 685.333h-116.954v37.218c40.977-0.516 81.835-12.924 116.954-37.218zM666.688 662.916l13.312 13.312v-28.262c-4.169 5.137-8.606 10.125-13.312 14.95zM662.908 357.321l13.321-13.321h-28.279c5.137 4.169 10.133 8.61 14.959 13.321zM357.321 361.092l-13.321-13.321v28.279c4.169-5.139 8.61-10.131 13.321-14.958z"],"attrs":[{"fill":"rgb(255, 192, 192)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["grid"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":6}]}},"attrs":[{"fill":"rgb(255, 192, 192)"}],"properties":{"order":563,"id":100,"name":"grid","prevSize":32,"code":59728},"setIdx":0,"setId":2,"iconIdx":81},{"icon":{"paths":["M132.506 272c0-17.673 14.327-32 32-32h703.999c17.673 0 32 14.327 32 32s-14.327 32-32 32h-703.999c-17.673 0-32-14.327-32-32zM292.506 432c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-17.673 14.327-32 32-32s32 14.327 32 32zM292.506 752c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-17.673 14.327-32 32-32s32 14.327 32 32zM452.506 624c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-31.999 14.327-31.999 32s14.327 32 31.999 32zM388.506 400c-17.673 0-32 14.327-32 32s14.327 32 32 32h479.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-479.999zM356.506 752c0-17.673 14.327-32 32-32h479.999c17.673 0 32 14.327 32 32s-14.327 32-32 32h-479.999c-17.673 0-32-14.327-32-32zM580.506 560c-17.673 0-32 14.327-32 32s14.327 32 32 32h288c17.673 0 32-14.327 32-32s-14.327-32-32-32h-288z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["group-by-type"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":564,"id":99,"name":"group-by-type","prevSize":32,"code":59729},"setIdx":0,"setId":2,"iconIdx":82},{"icon":{"paths":["M160 512c0-17.673 13.133-32 29.333-32h645.333c16.201 0 29.333 14.327 29.333 32s-13.133 32-29.333 32h-645.333c-16.201 0-29.333-14.327-29.333-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["h-bar"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":565,"id":98,"name":"h-bar","prevSize":32,"code":59730},"setIdx":0,"setId":2,"iconIdx":83},{"icon":{"paths":["M170.667 245.333c0-17.673 14.327-32 32-32h640.845c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640.845c-17.673 0-32-14.327-32-32zM170.667 501.333c0-17.673 14.327-32 32-32h640.845c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640.845c-17.673 0-32-14.327-32-32zM170.667 757.333c0-17.673 14.327-32 32-32h640.845c17.673 0 32 14.327 32 32s-14.327 32-32 32h-640.845c-17.673 0-32-14.327-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["hamburguer"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":566,"id":97,"name":"hamburguer","prevSize":32,"code":59731},"setIdx":0,"setId":2,"iconIdx":84},{"icon":{"paths":["M832 512c0 176.73-143.27 320-320 320-176.731 0-320-143.27-320-320h-64c0 212.079 171.923 384 384 384 212.079 0 384-171.921 384-384 0-212.077-171.921-384-384-384-123.718 0-233.772 58.508-304 149.364v-101.364c0-17.673-14.327-32-32-32s-32 14.327-32 32v192c0 17.673 14.327 32 32 32h176c17.673 0 32-14.327 32-32s-14.327-32-32-32h-107.295c57.239-86.755 155.582-144 267.295-144 176.73 0 320 143.269 320 320z","M544 320c0-17.673-14.327-32-32-32s-32 14.327-32 32v224c0 8.486 3.371 16.627 9.374 22.626l96 96c12.497 12.497 32.755 12.497 45.252 0s12.497-32.755 0-45.252l-86.626-86.63v-210.743z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["history"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":567,"id":96,"name":"history","prevSize":32,"code":59732},"setIdx":0,"setId":2,"iconIdx":85},{"icon":{"paths":["M521.357 195.096l224.171 259.701v365.751c0 5.85-4.749 10.59-10.607 10.59h-136.235v-149.623c0-23.394-18.987-42.355-42.411-42.355h-85.841c-23.424 0-42.415 18.961-42.415 42.355v149.623h-136.229c-5.856 0-10.603-4.74-10.603-10.59v-365.824l224.105-259.628c4.233-4.9 11.836-4.9 16.064 0zM559.834 894.673h175.087c40.994 0 74.223-33.186 74.223-74.125v-243.883h56.154c12.437 0 23.735-7.241 28.919-18.534 5.18-11.294 3.294-24.567-4.826-33.975l-319.846-370.544c-29.611-34.302-82.829-34.302-112.435 0l-319.848 370.544c-8.122 9.408-10.007 22.682-4.825 33.975s16.48 18.534 28.918 18.534h56.214v243.883c0 40.939 33.231 74.125 74.223 74.125h175.085c1.173 0.098 2.359 0.149 3.558 0.149h85.841c1.199 0 2.385-0.051 3.558-0.149z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["home"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":568,"id":95,"name":"home","prevSize":32,"code":59733},"setIdx":0,"setId":2,"iconIdx":86},{"icon":{"paths":["M405.333 405.333c0 47.13-38.205 85.333-85.333 85.333s-85.333-38.204-85.333-85.333c0-47.128 38.205-85.333 85.333-85.333s85.333 38.205 85.333 85.333z","M96 192c0-17.673 14.327-32 32-32h768c17.673 0 32 14.327 32 32v640c0 17.673-14.327 32-32 32h-768c-17.673 0-32-14.327-32-32v-640zM160 764.164l151.704-176.99c9.269-10.816 24.571-14.199 37.538-8.307l153.124 69.602 160.474-204.241c6.007-7.646 15.172-12.147 24.9-12.228 9.728-0.077 18.961 4.271 25.097 11.823l151.164 186.048v-405.871h-704v540.164zM213.575 800h650.425v-68.638l-175.582-216.102-166.78 212.271-176.99-80.448-131.073 152.917z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["image"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":569,"id":94,"name":"image","prevSize":32,"code":59734},"setIdx":0,"setId":2,"iconIdx":87},{"icon":{"paths":["M512 873.024c-199.389 0-361.026-161.634-361.026-361.024s161.637-361.026 361.026-361.026c199.39 0 361.024 161.637 361.024 361.026s-161.634 361.024-361.024 361.024zM512 938.667c235.639 0 426.667-191.027 426.667-426.667 0-235.642-191.027-426.667-426.667-426.667-235.642 0-426.667 191.025-426.667 426.667 0 235.639 191.025 426.667 426.667 426.667zM544.819 347.898c0 18.126-14.694 32.82-32.819 32.82s-32.819-14.694-32.819-32.82c0-18.127 14.694-32.82 32.819-32.82s32.819 14.694 32.819 32.82zM512 413.539c-18.125 0-32.819 14.694-32.819 32.819v229.747c0 18.125 14.694 32.819 32.819 32.819s32.819-14.694 32.819-32.819v-229.747c0-18.125-14.694-32.819-32.819-32.819z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["info"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":570,"id":93,"name":"info","prevSize":32,"code":59735},"setIdx":0,"setId":2,"iconIdx":88},{"icon":{"paths":["M512 864c-194.404 0-352-157.598-352-352 0-194.404 157.596-352 352-352 194.406 0 352 157.596 352 352 0 194.402-157.594 352-352 352zM512 928c229.751 0 416-186.249 416-416s-186.249-416-416-416c-229.751 0-416 186.249-416 416s186.249 416 416 416zM662.626 361.373c12.497 12.497 12.497 32.758 0 45.255l-105.37 105.373 105.37 105.374c12.497 12.497 12.497 32.755 0 45.252s-32.755 12.497-45.252 0l-105.374-105.37-105.373 105.37c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l105.371-105.374-105.371-105.373c-12.497-12.497-12.497-32.758 0-45.255s32.758-12.497 45.255 0l105.373 105.371 105.374-105.371c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["input-clear"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":571,"id":92,"name":"input-clear","prevSize":32,"code":59736},"setIdx":0,"setId":2,"iconIdx":89},{"icon":{"paths":["M546.253 352c17.673 0 32-14.327 32-32s-14.327-32-32-32c-17.673 0-32 14.327-32 32s14.327 32 32 32zM561.865 436.996c2.756-17.457-9.161-33.844-26.62-36.6-17.455-2.756-33.843 9.161-36.599 26.616l-48 304c-2.756 17.459 9.161 33.843 26.62 36.599 17.455 2.756 33.843-9.161 36.599-26.615l48-304z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["italic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":572,"id":91,"name":"italic","prevSize":32,"code":59737},"setIdx":0,"setId":2,"iconIdx":90},{"icon":{"paths":["M497.139 169.373c12.493 12.497 12.493 32.758 0 45.255l-35.964 35.96c1.084-0.017 2.163-0.025 3.247-0.025h191.151c115.11 0 208.427 93.315 208.427 208.424 0 115.11-93.316 208.427-208.427 208.427h-15.573v-64h15.573c79.765 0 144.427-64.661 144.427-144.427 0-79.763-64.661-144.424-144.427-144.424h-191.151c-1.028 0-2.057 0.011-3.081 0.032l35.797 35.797c12.493 12.497 12.493 32.758 0 45.255-12.497 12.497-32.759 12.497-45.257 0l-90.51-90.51c-12.497-12.497-12.497-32.758 0-45.255l90.51-90.51c12.497-12.497 32.759-12.497 45.257 0zM201.318 500.745h56.159v325.82h-59.023v-268.386h-1.909l-76.204 48.678v-54.089l80.977-52.023zM570.411 719.492c0 64.751-48.047 111.526-116.932 111.526-63.637 0-110.887-38.98-112.478-93.069h57.273c2.068 26.726 25.773 45.342 55.206 45.342 34.842 0 59.977-25.933 59.819-62.366 0.158-36.911-25.617-63.317-61.41-63.475-19.567-0.162-40.25 7.953-51.228 20.045l-53.295-8.751 17.023-168h189v49.318h-140.159l-9.386 86.387h1.909c12.091-14.319 35.479-24.819 61.885-24.819 59.183 0 102.775 45.18 102.775 107.861z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump-backward"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":573,"id":90,"name":"jump-backward","prevSize":32,"code":59738},"setIdx":0,"setId":2,"iconIdx":91},{"icon":{"paths":["M494.861 169.373c-12.497 12.497-12.497 32.758 0 45.255l35.959 35.96c-1.079-0.017-2.159-0.025-3.247-0.025h-191.148c-115.11 0-208.425 93.315-208.425 208.424 0 115.11 93.315 208.427 208.425 208.427h15.575v-64h-15.575c-79.764 0-144.425-64.661-144.425-144.427 0-79.763 64.661-144.424 144.425-144.424h191.148c1.033 0 2.061 0.011 3.085 0.032l-35.797 35.797c-12.497 12.497-12.497 32.758 0 45.255s32.759 12.497 45.257 0l90.509-90.51c12.497-12.497 12.497-32.758 0-45.255l-90.509-90.51c-12.497-12.497-32.759-12.497-45.257 0zM521.318 500.745h56.158v325.82h-59.021v-268.386h-1.911l-76.203 48.678v-54.089l80.977-52.023zM890.406 719.492c0 64.751-48.043 111.526-116.928 111.526-63.637 0-110.886-38.98-112.478-93.069h57.271c2.069 26.726 25.771 45.342 55.206 45.342 34.837 0 59.977-25.933 59.814-62.366 0.162-36.911-25.613-63.317-61.406-63.475-19.571-0.162-40.252 7.953-51.23 20.045l-53.295-8.751 17.024-168h189.001v49.318h-140.16l-9.387 86.387h1.907c12.092-14.319 35.477-24.819 61.888-24.819 59.183 0 102.771 45.18 102.771 107.861z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump-forward"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":574,"id":89,"name":"jump-forward","prevSize":32,"code":59739},"setIdx":0,"setId":2,"iconIdx":92},{"icon":{"paths":["M769.293 649.374c12.497 12.497 12.497 32.755 0 45.252l-192 192c-12.497 12.497-32.755 12.497-45.252 0l-192.001-192c-12.497-12.497-12.497-32.755 0-45.252s32.758-12.497 45.255 0l137.373 137.37v-578.743h-192v192c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v610.743l137.374-137.37c12.497-12.497 32.755-12.497 45.252 0z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["jump-to-message"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":575,"id":88,"name":"jump-to-message","prevSize":32,"code":59740},"setIdx":0,"setId":2,"iconIdx":93},{"icon":{"paths":["M577.353 256c0 35.346-28.655 64-64 64s-64-28.654-64-64c0-35.346 28.655-64 64-64s64 28.654 64 64z","M577.353 512c0 35.345-28.655 64-64 64s-64-28.655-64-64c0-35.345 28.655-64 64-64s64 28.655 64 64z","M577.353 768c0 35.345-28.655 64-64 64s-64-28.655-64-64c0-35.345 28.655-64 64-64s64 28.655 64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["kebab"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":576,"id":87,"name":"kebab","prevSize":32,"code":59741},"setIdx":0,"setId":2,"iconIdx":94},{"icon":{"paths":["M160 224c-35.346 0-64 28.654-64 64v448c0 35.345 28.654 64 64 64h704c35.345 0 64-28.655 64-64v-448c0-35.346-28.655-64-64-64h-704zM160 288h704v448h-704v-448zM256 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM256 512c0-17.673 14.327-32 32-32h64c17.673 0 32 14.327 32 32s-14.327 32-32 32h-64c-17.673 0-32-14.327-32-32zM480 480c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM640 512c0-17.673 14.327-32 32-32h64c17.673 0 32 14.327 32 32s-14.327 32-32 32h-64c-17.673 0-32-14.327-32-32zM480 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64zM320 640c0-17.673 14.327-32 32-32h320c17.673 0 32 14.327 32 32s-14.327 32-32 32h-320c-17.673 0-32-14.327-32-32zM704 352c-17.673 0-32 14.327-32 32s14.327 32 32 32h64c17.673 0 32-14.327 32-32s-14.327-32-32-32h-64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["keyboard"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":577,"id":86,"name":"keyboard","prevSize":32,"code":59742},"setIdx":0,"setId":2,"iconIdx":95},{"icon":{"paths":["M680.55 256c0-17.673-14.327-32-32-32s-32 14.327-32 32v56.876l-80.346 13.999c-17.412 3.034-29.069 19.607-26.035 37.018s19.61 29.066 37.022 26.032l69.359-12.085v69.371c-30.383 6.852-60.459 20.075-84.151 43.14-27.925 27.183-44.075 65.242-44.075 113.809 0 25.741 6.263 48.26 18.453 66.62 12.186 18.351 29.065 30.682 47.253 37.999 35.354 14.225 76.719 10.176 108.126-4.578l1.207-0.567c31.125-14.618 62.494-29.35 90.722-57.818 21.325-21.504 39.71-49.536 56.849-88.674 5.653 9.498 9.591 20.966 10.287 34.338 1.63 31.526-14.281 82.812-87.957 153.417-12.762 12.228-13.193 32.486-0.964 45.244 12.228 12.762 32.486 13.193 45.244 0.964 80.196-76.851 110.588-145.033 107.593-202.935-2.987-57.655-38.588-96.021-70.677-113.954-20.774-13.013-50.214-22.827-81.216-28.288-16.623-2.931-34.47-4.749-52.693-4.996v-74.242l101.717-17.723c17.412-3.034 29.069-19.607 26.035-37.018s-19.61-29.066-37.018-26.032l-90.735 15.809v-45.724zM577.041 536.209c10.163-9.894 23.565-17.476 39.509-22.703v138.65c-13.444 2.547-27.486 1.732-38.635-2.752-7.71-3.102-13.658-7.757-17.822-14.029-4.156-6.259-7.77-15.996-7.77-31.215 0-33.348 10.564-54.174 24.717-67.951zM708.642 600.751c-8.683 8.755-17.783 15.706-28.092 22.076v-117.888c14.127 0.239 28.224 1.66 41.587 4.015 13.589 2.394 25.681 5.623 35.763 9.165-16.879 42.039-33.067 66.304-49.259 82.633zM246.351 565.333h98.385l-49.193-184.882-49.193 184.882zM389.497 733.559l-27.732-104.226h-132.443l-27.732 104.226c-4.544 17.079-22.073 27.243-39.152 22.699s-27.241-22.076-22.696-39.151l106.323-399.601c13.494-50.714 85.464-50.713 98.957 0l106.323 399.601c4.544 17.075-5.615 34.607-22.694 39.151s-34.609-5.619-39.153-22.699z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["language"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":578,"id":85,"name":"language","prevSize":32,"code":59743},"setIdx":0,"setId":2,"iconIdx":96},{"icon":{"paths":["M252.138 275.61l-13.972 14.271c-37.092 37.884-36.449 98.665 1.435 135.757l145.62 142.576c37.885 37.090 98.665 36.446 135.756-1.438l13.973-14.268c0.725-0.742 1.434-1.489 2.129-2.244l0.098 0.098c5.807-5.905 13.888-9.566 22.822-9.566 17.673 0 32 14.327 32 32 0 9.6-4.228 18.21-10.923 24.077l-14.37 14.677c-61.82 63.142-163.119 64.213-226.26 2.394l-145.62-142.575c-63.141-61.82-64.212-163.121-2.392-226.262l13.972-14.271c61.82-63.141 163.121-64.212 226.263-2.392l74.692 73.131c0.977 0.847 1.899 1.752 2.769 2.71l0.371 0.366-0.026 0.026c4.937 5.63 7.923 13.005 7.923 21.079 0 17.673-14.327 32-32 32-7.829 0-15.002-2.813-20.565-7.482l-0.107 0.108-77.833-76.207c-37.885-37.092-98.665-36.449-135.757 1.435zM787.861 768l13.973-14.268c37.090-37.884 36.45-98.667-1.434-135.757l-145.621-142.575c-37.888-37.090-98.667-36.45-135.757 1.434l-13.973 14.272c-0.725 0.738-1.434 1.485-2.129 2.244l-0.098-0.102c-5.807 5.905-13.888 9.57-22.822 9.57-17.673 0-32-14.327-32-32 0-9.6 4.228-18.214 10.923-24.077l0.397-0.41 13.973-14.272c61.82-63.139 163.119-64.21 226.261-2.389l145.617 142.575c63.142 61.82 64.213 163.119 2.394 226.261l-13.973 14.268c-61.82 63.142-163.119 64.213-226.261 2.394l-74.692-73.131c-0.977-0.849-1.899-1.754-2.769-2.709l-0.371-0.367 0.026-0.026c-4.937-5.632-7.923-13.005-7.923-21.077 0-17.673 14.327-32 32-32 7.829 0 15.002 2.812 20.565 7.479l0.107-0.107 77.833 76.207c37.884 37.090 98.667 36.45 135.757-1.438z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["link"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":579,"id":84,"name":"link","prevSize":32,"code":59744},"setIdx":0,"setId":2,"iconIdx":97},{"icon":{"paths":["M839.189 128c31.317 0 56.811 24.79 56.811 55.383v657.214c0 30.592-25.493 55.424-56.811 55.424h-654.548c-31.254 0-56.642-24.832-56.642-55.424v-657.214c0-30.593 25.388-55.383 56.642-55.383h654.548zM298.842 233.75c-36.588 0-66.092 29.59-66.092 66.050 0 36.481 29.505 66.071 66.092 66.071 36.438 0 66.007-29.59 66.007-66.071 0-36.46-29.569-66.050-66.007-66.050zM241.795 782.46h114.030v-366.515h-114.030v366.515zM536.461 415.921h-109.188v366.517h113.775v-181.274c0-47.829 9.045-94.148 68.331-94.148 58.458 0 59.183 54.682 59.183 97.178v178.244h113.903v-201.007c0-98.714-21.316-174.598-136.666-174.598-55.407 0-92.57 30.38-107.759 59.202h-1.579v-50.114z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["linkedin-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":580,"id":83,"name":"linkedin-monochromatic","prevSize":32,"code":59745},"setIdx":0,"setId":2,"iconIdx":98},{"icon":{"paths":["M439.275 704.371c0-33.643 26.278-47.676 72.725-47.676s72.73 14.033 72.73 47.676c0 32.96-13.193 111.070-23.915 149.764-4.962 17.805-22.076 25.169-48.815 25.169-26.735 0-43.853-7.364-48.815-25.173-10.714-38.665-23.91-116.689-23.91-149.76zM616.781 870.374l0.021-0.094c6.178-22.289 12.57-53.862 17.387-83.447 4.685-28.791 8.721-60.71 8.721-82.462 0-35.891-15.812-67.802-46.797-87.113-26.035-16.226-57.152-19.925-84.113-19.925-26.957 0-58.078 3.699-84.113 19.925-30.982 19.311-46.795 51.221-46.795 87.113 0 21.807 4.036 53.73 8.726 82.517 4.819 29.577 11.21 61.12 17.381 83.392l0.024 0.085c7.401 26.556 24.983 45.666 46.871 56.469 19.473 9.609 40.427 11.831 57.907 11.831 17.485 0 38.434-2.223 57.907-11.831 21.888-10.799 39.471-29.905 46.874-56.461zM459.635 460.8c0-28.275 23.445-51.2 52.365-51.2s52.365 22.925 52.365 51.2c0 28.275-23.445 51.2-52.365 51.2s-52.365-22.925-52.365-51.2zM512 341.333c-67.477 0-122.182 53.487-122.182 119.467s54.704 119.467 122.182 119.467c67.477 0 122.18-53.487 122.18-119.467s-54.703-119.467-122.18-119.467zM677.803 537.254c-6.665 13.79-3.883 31.108 6.276 42.573 14.084 15.893 39.851 17.937 50.206-0.606 19.639-35.17 30.805-75.52 30.805-118.421 0-136.672-113.314-247.467-253.090-247.467-139.779 0-253.092 110.795-253.092 247.467 0 42.901 11.164 83.251 30.806 118.421 10.355 18.543 36.119 16.499 50.205 0.606 10.16-11.465 12.939-28.783 6.275-42.573-11.203-23.189-17.469-49.105-17.469-76.454 0-98.97 82.054-179.2 183.274-179.2 101.218 0 183.27 80.23 183.27 179.2 0 27.349-6.263 53.265-17.468 76.454zM730.159 699.793c-0.303-10.658 3.546-21.069 10.931-28.762 52.77-54.955 85.090-128.9 85.090-210.231 0-169.662-140.663-307.2-314.18-307.2s-314.182 137.538-314.182 307.2c0 81.331 32.323 155.277 85.094 210.231 7.386 7.693 11.234 18.103 10.928 28.762-0.832 29.018-31.273 47.915-52.121 27.716-70.223-68.041-113.718-162.406-113.718-266.709 0-207.365 171.923-375.467 384-375.467 212.079 0 384 168.102 384 375.467 0 104.303-43.494 198.673-113.719 266.709-20.851 20.203-51.29 1.306-52.122-27.716z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["live-streaming"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":581,"id":82,"name":"live-streaming","prevSize":32,"code":59746},"setIdx":0,"setId":2,"iconIdx":99},{"icon":{"paths":["M700.147 361.892c4.681-24.975 34.701-34.093 49.126-13.174 31.991 46.404 50.726 102.656 50.726 163.281 0 60.629-18.735 116.881-50.726 163.285-14.426 20.919-44.446 11.802-49.126-13.175l-1.707-9.101c-1.583-8.431 0.384-17.084 4.855-24.401 20.749-33.967 32.704-73.89 32.704-116.608 0-42.714-11.955-82.637-32.704-116.604-4.471-7.319-6.438-15.972-4.855-24.402l1.707-9.102z","M320.705 395.396c-20.749 33.967-32.705 73.891-32.705 116.604 0 42.718 11.956 82.641 32.705 116.608 4.471 7.317 6.436 15.97 4.856 24.401l-1.707 9.101c-4.683 24.977-34.703 34.095-49.127 13.175-31.994-46.404-50.728-102.656-50.728-163.285 0-60.625 18.734-116.878 50.728-163.281 14.423-20.919 44.444-11.801 49.127 13.174l1.707 9.102c1.58 8.43-0.385 17.083-4.856 24.402z","M728.764 209.256l-0.512 2.747c-2.236 11.911 2.534 23.968 11.763 31.822 75.861 64.565 123.985 160.751 123.985 268.175 0 107.426-48.124 203.614-123.985 268.177-9.229 7.855-13.999 19.913-11.763 31.821l0.512 2.748c4.194 22.362 29.692 33.195 47.262 18.743 92.796-76.292 151.974-191.979 151.974-321.489 0-129.507-59.179-245.193-151.974-321.489-17.57-14.448-43.068-3.615-47.262 18.745z","M283.986 243.825c9.229-7.854 13.998-19.911 11.764-31.822l-0.515-2.747c-4.192-22.359-29.69-33.193-47.262-18.745-92.793 76.296-151.973 191.982-151.973 321.489 0 129.51 59.18 245.197 151.973 321.489 17.572 14.451 43.070 3.618 47.262-18.743l0.515-2.748c2.233-11.908-2.536-23.966-11.764-31.821-75.863-64.563-123.986-160.751-123.986-268.177 0-107.424 48.122-203.61 123.986-268.175z","M608 512c0 53.022-42.982 96-96 96s-96-42.978-96-96c0-53.018 42.982-95.999 96-95.999s96 42.981 96 95.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["live"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":582,"id":81,"name":"live","prevSize":32,"code":59747},"setIdx":0,"setId":2,"iconIdx":100},{"icon":{"paths":["M410.708 527.33c26.575 0 48.121-21.585 48.121-48.213s-21.547-48.213-48.121-48.213c-26.576 0-48.12 21.585-48.12 48.213s21.544 48.213 48.12 48.213z","M603.187 479.117c0 26.628-21.542 48.213-48.119 48.213s-48.119-21.585-48.119-48.213c0-26.628 21.542-48.213 48.119-48.213s48.119 21.585 48.119 48.213z","M747.55 479.117c0 26.628-21.542 48.213-48.119 48.213s-48.119-21.585-48.119-48.213c0-26.628 21.542-48.213 48.119-48.213s48.119 21.585 48.119 48.213z","M101.734 813.683l119.625 31.279c85.312 22.31 173.449-16.252 238.694-79.71 29.5 4.796 59.874 7.245 90.675 7.245 218.295 0 404.339-122.863 404.339-294.519 0-171.663-186.039-294.519-404.339-294.519-218.313 0-404.35 122.852-404.339 294.524 0 65.071 27.642 125.013 75.655 173.585-2.847 24.836-14.596 44.732-36.049 68.553l-84.261 93.564zM550.729 258.994c183.13 0 331.601 98.043 331.601 218.984 0 120.93-148.471 218.982-331.601 218.982-40.781 0-79.834-4.877-115.913-13.76-36.667 45.598-117.332 109.005-195.694 88.516 25.489-28.305 63.251-76.13 55.168-154.91-46.968-37.781-75.162-86.135-75.162-138.829-0.008-120.949 148.46-218.984 331.599-218.984z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["livechat-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":583,"id":80,"name":"livechat-monochromatic","prevSize":32,"code":59748},"setIdx":0,"setId":2,"iconIdx":101},{"icon":{"paths":["M341.335 778.671c-17.673 0-32 14.327-32 32s14.327 32 32 32h341.332c17.673 0 32-14.327 32-32s-14.327-32-32-32h-341.332zM85.335 298.672c0-70.692 57.308-128 128-128h597.332c70.694 0 128 57.308 128 128v298.666c0 70.694-57.306 128-128 128h-597.332c-70.693 0-128-57.306-128-128v-298.666zM213.335 234.672c-35.346 0-64 28.654-64 64v298.666c0 35.349 28.654 64 64 64h597.332c35.349 0 64-28.655 64-64v-298.666c0-35.346-28.651-64-64-64h-597.332zM256 320c0-11.782 9.551-21.333 21.333-21.333h170.667c11.78 0 21.333 9.551 21.333 21.333s-9.553 21.333-21.333 21.333h-170.667c-11.782 0-21.333-9.551-21.333-21.333zM277.333 554.667c-11.782 0-21.333 9.553-21.333 21.333s9.551 21.333 21.333 21.333h85.333c11.782 0 21.333-9.553 21.333-21.333s-9.551-21.333-21.333-21.333h-85.333zM554.667 320c0-11.782 9.553-21.333 21.333-21.333h170.667c11.78 0 21.333 9.551 21.333 21.333s-9.553 21.333-21.333 21.333h-170.667c-11.78 0-21.333-9.551-21.333-21.333zM448 554.667c-11.78 0-21.333 9.553-21.333 21.333s9.553 21.333 21.333 21.333h298.667c11.78 0 21.333-9.553 21.333-21.333s-9.553-21.333-21.333-21.333h-298.667zM256 448c0-11.78 9.551-21.333 21.333-21.333h213.333c11.78 0 21.333 9.553 21.333 21.333s-9.553 21.333-21.333 21.333h-213.333c-11.782 0-21.333-9.553-21.333-21.333zM618.667 426.667c-11.78 0-21.333 9.553-21.333 21.333s9.553 21.333 21.333 21.333h128c11.78 0 21.333-9.553 21.333-21.333s-9.553-21.333-21.333-21.333h-128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["log-view"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":585,"id":78,"name":"log-view","prevSize":32,"code":59751},"setIdx":0,"setId":2,"iconIdx":103},{"icon":{"paths":["M261.333 96c-17.673 0-32 14.327-32 32v768c0 17.673 14.327 32 32 32h512c17.673 0 32-14.327 32-32v-96c0-17.673-14.327-32-32-32s-32 14.327-32 32v64h-448v-704h448v64c0 17.673 14.327 32 32 32s32-14.327 32-32v-96c0-17.673-14.327-32-32-32h-512zM987.959 489.374l-160-160.001c-12.497-12.497-32.755-12.497-45.252 0s-12.497 32.758 0 45.255l105.37 105.373h-418.743c-17.673 0-32 14.327-32 32s14.327 32 32 32h418.743l-105.37 105.374c-12.497 12.497-12.497 32.755 0 45.252s32.755 12.497 45.252 0l160-160c12.497-12.497 12.497-32.755 0-45.252z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["logout"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":586,"id":77,"name":"logout","prevSize":32,"code":59752},"setIdx":0,"setId":2,"iconIdx":104},{"icon":{"paths":["M192 736h640v-448h-640v448zM128 256c0-17.673 14.327-32 32-32h704c17.673 0 32 14.327 32 32v512c0 17.673-14.327 32-32 32h-704c-17.673 0-32-14.327-32-32v-512zM305.304 389.082c-14.866-9.557-34.665-5.253-44.222 9.613s-5.253 34.665 9.613 44.223l241.304 155.123 241.306-155.123c14.865-9.557 19.17-29.356 9.613-44.223s-29.355-19.17-44.224-9.613l-206.694 132.876-206.696-132.876z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mail"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":587,"id":76,"name":"mail","prevSize":32,"code":59753},"setIdx":0,"setId":2,"iconIdx":105},{"icon":{"paths":["M302.769 192h418.464l23.185 72.727h-0.213l23.795 64c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818c0-18.477-14.327-33.455-32-33.455s-32 14.978-32 33.455c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818c0-18.477-14.327-33.455-32-33.455s-32 14.978-32 33.455c0 41.27-31.305 69.818-64 69.818s-64-28.548-64-69.818l23.793-64h-0.213l23.188-72.727zM212.406 264.727l36.491-114.448c4.231-13.27 16.56-22.279 30.488-22.279h465.23c13.931 0 26.257 9.009 30.49 22.279l56.896 178.448c0 33.937-12.083 64.925-32 88.515v350.775c0 53.022-42.982 96-96 96h-384c-53.019 0-96-42.978-96-96v-350.775c-19.916-23.59-32-54.577-32-88.515l20.406-64zM288 458.33v309.687c0 17.673 14.327 32 32 32h128v-192.017h128v192.017h128c17.673 0 32-14.327 32-32v-309.687c-10.227 2.752-20.949 4.215-32 4.215-38.229 0-72.546-17.523-96-45.303-23.454 27.78-57.771 45.303-96 45.303s-72.546-17.523-96-45.303c-23.454 27.78-57.77 45.303-96 45.303-11.049 0-21.772-1.463-32-4.215z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["marketplace"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":588,"id":75,"name":"marketplace","prevSize":32,"code":59754},"setIdx":0,"setId":2,"iconIdx":106},{"icon":{"paths":["M774.426 576c-35.345 0-64-28.655-64-64s28.655-64 64-64c35.345 0 64 28.655 64 64s-28.655 64-64 64z","M518.426 576c-35.345 0-64-28.655-64-64s28.655-64 64-64c35.345 0 64 28.655 64 64s-28.655 64-64 64z","M262.426 576c-35.346 0-64-28.655-64-64s28.654-64 64-64c35.346 0 64 28.655 64 64s-28.654 64-64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["meatballs"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":589,"id":74,"name":"meatballs","prevSize":32,"code":59755},"setIdx":0,"setId":2,"iconIdx":107},{"icon":{"paths":["M651.358 116.071c-57.242-19.989-105.788-20.069-138.355-20.071l-0.004 64-0.998-64c-92.005 0.342-297.345 47.824-381.242 242.996-17.897 38.968-34.758 103.342-34.758 173.004 0 69.961 17.020 146.283 52.216 207.876 23.229 40.653 92.798 131.375 191.178 173.538 94.863 40.653 206.368 52.343 296.488 16.299 16.41-6.566 24.393-25.19 17.83-41.596-6.566-16.41-25.19-24.393-41.596-17.83-69.879 27.955-163.174 20.446-247.512-15.701-80.82-34.637-141.385-112.448-160.822-146.462-28.804-50.406-43.784-115.418-43.784-176.124 0-60.766 15.020-116.183 29.055-146.59l0.184-0.4 0.174-0.404c69.507-162.182 243.825-204.605 323.586-204.605 31.599 0.002 70.882 0.296 117.261 16.493 46.144 16.113 101.389 48.779 161.822 116.767 43.657 49.114 63.535 114.976 69.389 177.712 5.892 63.121-2.85 118.187-11.541 142.093-6.404 17.6-20.429 45.44-59.392 45.7-18.261-0.806-72.823-14.673-83.123-69.568v-235.063c0-17.673-3.413-34.133-29.013-34.133-19.337 0-26.453 16.46-26.453 34.133v34.133c-35.183-39.86-95.403-68.267-152.747-68.267-106.039 0-192 85.961-192 192s85.961 192 192 192c62.178 0 117.658-29.555 152.747-75.388 25.711 71.078 102.571 93.030 137.011 94.135l0.516 0.017h0.512c82.645 0 111.714-64.806 120.085-87.829 12.642-34.761 21.675-99.695 15.125-169.907-6.592-70.597-29.38-151.402-85.278-214.288-66.906-75.265-131.076-114.598-188.561-134.67zM627.2 512c0 70.694-57.306 128-128 128s-128-57.306-128-128c0-70.694 57.306-128 128-128s128 57.306 128 128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mention"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":590,"id":73,"name":"mention","prevSize":32,"code":59756},"setIdx":0,"setId":2,"iconIdx":108},{"icon":{"paths":["M480.094 128c104.26 0 181.252 29.018 237.495 70.652l-44.318 44.317c-45.521-31.22-107.746-53.057-193.178-53.057-129.725 0-207.828 53.576-254.528 118.696-47.832 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-7.328 80.926 13.761 143.13 51.784 190.498l-43.879 43.883c-50.179-59.904-78.131-139.046-69.44-238.596 0.276-63.411 19.117-157.053 75.696-235.949 58.132-81.062 154.749-144.433 304.572-144.433zM825.105 332.498l-47.010 47.009c16.503 42.634 20.608 78.524 20.608 89.011v0.179c0 19.516 0 31.791-6.622 56.171-7.091 26.108-21.739 65.647-52.553 137.835-5.7 13.355-2.795 25.98 1.263 34.436 3.831 7.987 9.557 14.4 14.592 19.183 10.227 9.711 24.252 18.837 38.545 26.867 7.889 4.429 18.3 8.994 27.614 13.077l1.843 0.806c10.556 4.634 20.727 9.148 29.611 13.897 3.426 1.835 6.302 3.516 8.674 5.030-5.103 3.23-12.036 6.882-20.941 10.765-22.959 10.005-55.33 19.938-93.888 28.126-77.12 16.375-174.729 24.862-263.339 14.976-43.746-4.881-86.83-14.528-126.537-29.231l-47.543 47.543c52.771 23.019 110.493 36.89 167.266 43.221 96.388 10.752 200.708 1.515 282.91-15.936 41.097-8.725 77.734-19.729 105.681-31.906 13.909-6.063 26.615-12.821 36.855-20.343 9.425-6.925 20.74-17.344 25.609-32.017 7.394-22.268-2.953-40.503-12.813-51.136-9.429-10.167-22.106-17.941-32.956-23.74-11.311-6.046-23.625-11.469-33.89-15.979-11.059-4.851-18.931-8.316-24.051-11.191-9.916-5.568-17.442-10.534-22.507-14.571 27.422-64.806 42.214-104.589 50.048-133.423 8.772-32.303 8.794-50.982 8.794-72.64 0-20.105-7.053-75.5-35.264-136.020zM838.293 153.373c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-682.667 682.665c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l682.665-682.668z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":591,"id":72,"name":"message-disabled","prevSize":32,"code":59757},"setIdx":0,"setId":2,"iconIdx":109},{"icon":{"paths":["M223.311 308.609c-47.831 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-9.348 103.236 27.547 175.996 86.848 226.377 60.643 51.516 146.604 80.998 235.291 90.893 88.61 9.886 186.223 1.399 263.339-14.976 38.562-8.188 70.929-18.121 93.888-28.126 8.909-3.883 15.838-7.535 20.945-10.765-2.377-1.515-5.248-3.196-8.678-5.030-8.883-4.749-19.055-9.263-29.611-13.897l-1.843-0.806c-9.314-4.083-19.725-8.649-27.61-13.077-14.293-8.030-28.318-17.156-38.545-26.867-5.035-4.783-10.761-11.196-14.596-19.183-4.058-8.457-6.959-21.082-1.259-34.436 30.814-72.188 45.457-111.727 52.553-137.835 6.622-24.38 6.622-36.655 6.618-56.171v-0.179c0-15.514-8.977-86.595-53.777-152.876-43.371-64.175-121.395-125.729-264.828-125.729-129.727 0-207.831 53.576-254.531 118.696zM173.267 272.433c58.132-81.062 154.749-144.433 304.574-144.433 164.894 0 261.594 72.589 315.857 152.878 52.838 78.182 64.414 161.879 64.414 187.641 0 21.658-0.017 40.337-8.794 72.64-7.834 28.834-22.626 68.617-50.048 133.423 5.069 4.036 12.591 9.003 22.507 14.571 5.12 2.876 12.992 6.34 24.055 11.191 10.261 4.51 22.579 9.933 33.89 15.979 10.846 5.798 23.526 13.572 32.951 23.74 9.86 10.633 20.211 28.868 12.817 51.136-4.873 14.673-16.183 25.092-25.609 32.017-10.24 7.522-22.946 14.281-36.86 20.343-27.947 12.177-64.58 23.181-105.681 31.906-82.197 17.451-186.522 26.688-282.906 15.936-96.31-10.743-195.347-43.174-268.314-105.165-74.031-62.891-119.295-154.778-108.551-277.854 0.276-63.411 19.118-157.053 75.696-235.949z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["message"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":592,"id":71,"name":"message","prevSize":32,"code":59758},"setIdx":0,"setId":2,"iconIdx":110},{"icon":{"paths":["M85.333 85.333l730.795 774.089c0 0 24.896 17.557 43.934-2.927 19.038-20.489 4.395-40.973 4.395-40.973l-779.123-730.189zM316.727 158.5l556.515 599.956c0 0 24.896 17.557 43.938-2.927 19.038-20.489 4.39-40.973 4.39-40.973l-604.844-556.056zM710.682 915.025l-556.516-599.952 604.845 556.057c0 0 14.643 20.484-4.395 40.969-19.038 20.489-43.934 2.927-43.934 2.927zM512.337 221.417l388.804 419.151c0 0 17.395 12.271 30.694-2.044 13.303-14.31 3.072-28.625 3.072-28.625l-422.571-388.482zM596.523 915.674l-388.803-419.153 422.569 388.484c0 0 10.231 14.31-3.068 28.625-13.303 14.31-30.699 2.044-30.699 2.044zM712.145 312.141l176.222 190.549c0 0 8.602 5.751 15.181-0.956s1.519-13.414 1.519-13.414l-192.922-176.179zM481.229 880.226l-176.218-190.549 192.922 176.179c0 0 5.060 6.707-1.519 13.414s-15.185 0.956-15.185 0.956z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["meteor-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":593,"id":70,"name":"meteor-monochromatic","prevSize":32,"code":59759},"setIdx":0,"setId":2,"iconIdx":111},{"icon":{"paths":["M527.782 128c75.546 0 138.859 52.355 155.644 122.761l-59.644 59.644v-22.406c0-53.019-42.982-96-96-96-53.022 0-96 42.981-96 96v128c0 24.068 8.858 46.067 23.488 62.916l-45.322 45.321c-26.182-28.484-42.167-66.496-42.167-108.237v-128c0-88.366 71.634-160 160.001-160zM577.131 568.243l102.895-102.895c-15.765 48.674-54.221 87.13-102.895 102.895zM303.781 416c0 72.154 23.182 123.102 55.327 159.078l-45.303 45.303c-43.427-47.351-74.024-113.993-74.024-204.382 0-17.673 14.327-32 32-32s32 14.327 32 32zM494.438 650.935l-51.806 51.81c18.56 6.374 36.578 10.807 53.15 13.636v115.618h-160.001c-17.673 0-32 14.327-32 32s14.327 32 32 32h384.001c17.673 0 32-14.327 32-32s-14.327-32-32-32h-160v-115.618c44.638-7.62 99.819-26.897 147.721-64.38 60.702-47.501 108.279-123.29 108.279-236.002 0-17.673-14.327-32-32-32s-32 14.327-32 32c0 92.087-37.76 149.632-83.721 185.6-46.468 36.361-102.737 51.58-140.279 54.327-9.971-0.73-21.265-2.338-33.344-4.992zM841.152 153.373c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-672 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l671.998-672.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["microphone-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":594,"id":69,"name":"microphone-disabled","prevSize":32,"code":59760},"setIdx":0,"setId":2,"iconIdx":112},{"icon":{"paths":["M621.53 288c0-53.019-42.982-96-96-96-53.022 0-96 42.981-96 96v128c0 53.018 42.978 96 96 96 53.018 0 96-42.982 96-96v-128zM365.529 288c0-88.366 71.634-160 160.001-160 88.363 0 160 71.634 160 160v128c0 88.367-71.637 160-160 160-88.367 0-160.001-71.633-160.001-160v-128zM269.529 384c17.673 0 32 14.327 32 32 0 92.087 37.758 149.632 83.72 185.6 46.465 36.361 102.734 51.58 140.281 54.327 37.542-2.748 93.811-17.967 140.279-54.327 45.961-35.968 83.721-93.513 83.721-185.6 0-17.673 14.327-32 32-32s32 14.327 32 32c0 112.713-47.578 188.501-108.279 236.002-47.902 37.483-103.083 56.759-147.721 64.38v115.618h160c17.673 0 32 14.327 32 32s-14.327 32-32 32h-384.001c-17.673 0-32-14.327-32-32s14.327-32 32-32h160.001v-115.618c-44.642-7.62-99.819-26.897-147.721-64.38-60.705-47.501-108.28-123.29-108.28-236.002 0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["microphone"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":595,"id":68,"name":"microphone","prevSize":32,"code":59761},"setIdx":0,"setId":2,"iconIdx":113},{"icon":{"paths":["M234.667 170.662c0-47.128 38.205-85.333 85.333-85.333h384c47.13 0 85.333 38.205 85.333 85.333v682.668c0 47.125-38.204 85.333-85.333 85.333h-384c-47.128 0-85.333-38.208-85.333-85.333v-682.668zM298.667 170.662v682.668c0 11.78 9.551 21.333 21.333 21.333h384c11.78 0 21.333-9.553 21.333-21.333v-682.668c0-11.782-9.553-21.333-21.333-21.333h-96.29c-2.654 24.002-23.002 42.672-47.71 42.672h-96c-24.708 0-45.056-18.67-47.707-42.672h-96.293c-11.782 0-21.333 9.551-21.333 21.333z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["mobile"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":596,"id":67,"name":"mobile","prevSize":32,"code":59762},"setIdx":0,"setId":2,"iconIdx":114},{"icon":{"paths":["M317.896 231.569c-62.882 49.504-135.086 136.548-141.94 266.125-5.762 108.924 37.404 187.187 89.916 241.51 53.259 55.1 115.701 84.924 144.248 94.443 41.861 13.952 114.271 29.534 234.646-15.607 34.859-13.069 73.161-42.095 108.301-78.426 14.272-14.754 27.614-30.293 39.599-45.773-28.774 9.293-61.696 17.758-96.589 23.595-56.188 9.404-119.287 12.322-179.409-0.235-2.492-0.521-4.958-1.033-7.411-1.536-22.191-4.578-42.731-8.815-62.583-17.651-23.095-10.274-43.808-25.89-69.3-51.383-42.097-42.095-89.143-107.221-89.372-213.841-1.569-34.084 4.621-81.88 13.763-129.219 4.624-23.946 10.142-48.465 16.132-72.002zM344.209 138.22c32.8-17.15 63.709 15.843 53.877 45.907-12.235 37.414-24.586 85.511-33.483 131.58-9.024 46.731-13.946 88.633-12.643 114.698l0.040 0.798v0.802c0 84.915 36.172 134.916 70.627 169.374 22.505 22.507 36.833 32.277 50.064 38.165 13.227 5.884 26.829 8.717 51.085 13.764 1.924 0.397 3.913 0.815 5.978 1.246 49.911 10.423 104.533 8.337 155.759-0.239 65.434-10.953 122.385-31.979 152.375-47.386 17.562-9.020 34.991-2.466 44.476 5.965 9.664 8.589 19.375 27.153 8.887 46.955-20.638 38.95-53.645 84.42-92.181 124.262-38.217 39.509-84.318 76.036-131.836 93.854-135.625 50.863-223.215 34.441-277.353 16.397-36.516-12.173-108.392-46.912-170.026-110.677-62.381-64.533-114.683-159.428-107.809-289.37 10.79-203.98 157.583-317.1 232.164-356.095z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["moon"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":597,"id":66,"name":"moon","prevSize":32,"code":59763},"setIdx":0,"setId":2,"iconIdx":115},{"icon":{"paths":["M809.003 166.758c7.782 6.063 12.331 15.377 12.331 25.242v448h-0.111c0.073 1.732 0.111 3.477 0.111 5.231 0 69.052-57.306 125.026-128 125.026s-128-55.974-128-125.026c0-69.052 57.306-125.030 128-125.030 23.313 0 45.171 6.089 64 16.725v-303.858l-384 96.797v409.139c0 69.052-57.308 125.035-128 125.035s-128-55.979-128-125.030c0-69.052 57.308-125.026 128-125.026 23.314 0 45.173 6.089 64 16.725v-325.777c0-14.66 9.963-27.446 24.178-31.029l448.001-112.929c9.566-2.412 19.708-0.276 27.49 5.787z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["musical-note"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":598,"id":65,"name":"musical-note","prevSize":32,"code":59764},"setIdx":0,"setId":2,"iconIdx":116},{"icon":{"paths":["M208 176c-35.346 0-64 28.654-64 64v576c0 35.345 28.654 64 64 64h576c35.345 0 64-28.655 64-64v-576c0-35.346-28.655-64-64-64h-576zM208 240h576v576h-576v-576zM698.628 558.153c-0.192 17.673-14.669 31.842-32.341 31.654s-31.846-14.669-31.659-32.341l1.323-123.588-309.344 308.77c-12.508 12.484-32.77 12.467-45.255-0.043-12.485-12.506-12.466-32.768 0.042-45.252l309.199-308.627-123.388 1.318c-17.673 0.189-32.149-13.984-32.337-31.657-0.192-17.672 13.982-32.151 31.654-32.34l201.92-2.156c8.606-0.092 16.887 3.286 22.972 9.371s9.459 14.364 9.37 22.969l-2.155 201.922z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["new-window"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":599,"id":64,"name":"new-window","prevSize":32,"code":59765},"setIdx":0,"setId":2,"iconIdx":117},{"icon":{"paths":["M781.837 402.633c0-5.245-0.154-10.454-0.461-15.622l-63.539 63.541v265.361h-265.361l-66.982 66.982h425.365c12.941 0 24.61-7.795 29.564-19.755s2.214-25.719-6.938-34.871l-51.648-51.652v-273.984zM704.764 217.373l-45.252 45.255c-35.639-35.351-84.706-57.189-138.867-57.189-108.909 0-197.196 88.287-197.196 197.194v196.057l-64 64v-260.057c0-144.253 116.94-261.194 261.196-261.194 71.838 0 136.9 29.001 184.119 75.934zM433.579 808.934c0 48.085 38.98 87.066 87.066 87.066s87.066-38.98 87.066-87.066h-174.131zM854.276 190.982c-11.337-11.334-29.709-11.334-41.045 0l-612.731 612.73c-11.334 11.337-11.334 29.709 0 41.045 11.334 11.332 29.709 11.332 41.043 0l612.733-612.733c11.332-11.334 11.332-29.709 0-41.042z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["notification-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":600,"id":63,"name":"notification-disabled","prevSize":32,"code":59766},"setIdx":0,"setId":2,"iconIdx":118},{"icon":{"paths":["M601.331 808.926h-174.153c0.004 48.090 38.989 87.074 87.078 87.074s87.074-38.985 87.074-87.074zM815.599 731.674c7.27 9.698 8.439 22.673 3.021 33.515-5.423 10.837-16.503 17.685-28.621 17.685h-565.998c-12.943 0-24.611-7.795-29.564-19.75-4.953-11.959-2.215-25.724 6.937-34.876l51.653-51.652v-274.031c0-144.273 116.956-261.229 261.231-261.229 144.273 0 261.227 116.956 261.227 261.229v275.627l40.115 53.483zM711.484 715.895v-313.332c0-108.927-88.303-197.229-197.227-197.229-108.928 0-197.231 88.302-197.231 197.229v313.332h394.457z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["notification"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":601,"id":62,"name":"notification","prevSize":32,"code":59767},"setIdx":0,"setId":2,"iconIdx":119},{"icon":{"paths":["M711.113 512v54.818c-0.196 76.634-43.682 143.091-107.298 176.209-5.525-25.634-28.326-44.847-55.612-44.847h-72.405c-31.42 0-56.888 25.472-56.888 56.892v41.374c0 31.415 25.469 56.887 56.888 56.887h72.405c28.471 0 52.062-20.919 56.235-48.226 69.167-26.795 123.797-82.773 148.77-152.828 4.715 1.267 9.677 1.941 14.793 1.941h28.446c31.415 0 56.887-25.468 56.887-56.887v-85.333c0-31.42-25.472-56.887-56.887-56.887h-28.446v-28.446c0-141.385-114.615-256-256-256s-256 114.615-256 256v23.275h-28.445c-31.419 0-56.889 25.468-56.889 56.887v103.433c0 31.42 25.47 56.892 56.889 56.892h28.445c31.419 0 56.889-25.472 56.889-56.892v-36.203h0.149c-0.099-2.573-0.148-5.158-0.148-7.757v-139.636c0-109.966 89.145-199.111 199.11-199.111s199.113 89.145 199.113 199.111v85.333z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["omnichannel"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":602,"id":61,"name":"omnichannel","prevSize":32,"code":59768},"setIdx":0,"setId":2,"iconIdx":120},{"icon":{"paths":["M697.698 626.56c12.676-12.314 32.934-12.023 45.252 0.649 12.314 12.676 12.023 32.934-0.649 45.252l-208 202.121c-12.42 12.066-32.183 12.066-44.604 0l-207.999-202.121c-12.675-12.318-12.965-32.576-0.649-45.252 12.317-12.672 32.576-12.962 45.25-0.649l185.699 180.45 185.698-180.45zM697.698 416.798c12.676 12.317 32.934 12.027 45.252-0.649 12.314-12.675 12.023-32.934-0.649-45.25l-208-202.119c-12.42-12.067-32.183-12.067-44.604 0l-207.999 202.119c-12.675 12.316-12.965 32.576-0.649 45.25 12.316 12.676 32.576 12.966 45.25 0.649l185.699-180.449 185.698 180.449z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["order"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":603,"id":60,"name":"order","prevSize":32,"code":59769},"setIdx":0,"setId":2,"iconIdx":121},{"icon":{"paths":["M749.756 660.215c16.887-16.435 17.259-43.447 0.823-60.335-16.431-16.887-43.443-17.259-60.335-0.823l59.511 61.158zM512 832.021l-29.756 30.579c16.563 16.115 42.948 16.115 59.511 0l-29.756-30.579zM333.754 599.057c-16.889-16.435-43.901-16.064-60.334 0.823s-16.064 43.9 0.825 60.335l59.509-61.158zM690.244 599.057l-208 202.385 59.511 61.158 208-202.385-59.511-61.158zM541.756 801.442l-208.001-202.385-59.509 61.158 207.999 202.385 59.511-61.158z","M749.756 363.805c16.887 16.433 17.259 43.445 0.823 60.334-16.431 16.889-43.443 17.26-60.335 0.826l59.511-61.16zM512 191.999l-29.756-30.58c16.563-16.116 42.948-16.116 59.511 0l-29.756 30.58zM333.754 424.965c-16.889 16.434-43.901 16.063-60.334-0.825s-16.064-43.901 0.825-60.334l59.509 61.159zM690.244 424.965l-208-202.386 59.511-61.159 208 202.386-59.511 61.16zM541.756 222.579l-208.001 202.386-59.509-61.159 207.999-202.386 59.511 61.159z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"isMulticolor":true,"isMulticolor2":true,"grid":0,"tags":["ordering-ascending"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":5}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"properties":{"order":604,"id":59,"name":"ordering-ascending","prevSize":32,"code":59770,"codes":[59770,59771]},"setIdx":0,"setId":2,"iconIdx":122},{"icon":{"paths":["M274.246 363.785c-16.889 16.433-17.258 43.445-0.825 60.334s43.445 17.259 60.334 0.825l-59.509-61.159zM512 191.978l29.756-30.58c-16.563-16.116-42.948-16.116-59.511 0l29.756 30.58zM690.244 424.944c16.892 16.434 43.904 16.063 60.335-0.825 16.435-16.889 16.064-43.901-0.823-60.334l-59.511 61.159zM333.754 424.944l208.001-202.386-59.511-61.16-207.999 202.386 59.509 61.159zM482.244 222.558l208 202.386 59.511-61.159-208-202.386-59.511 61.16z","M274.246 660.194c-16.889-16.431-17.258-43.443-0.825-60.335 16.433-16.887 43.445-17.259 60.334-0.823l-59.509 61.158zM512 832l29.756 30.579c-16.563 16.115-42.948 16.115-59.511 0l29.756-30.579zM690.244 599.036c16.892-16.435 43.904-16.064 60.335 0.823 16.435 16.892 16.064 43.904-0.823 60.335l-59.511-61.158zM333.754 599.036l208.001 202.385-59.511 61.158-207.999-202.385 59.509-61.158zM482.244 801.421l208-202.385 59.511 61.158-208 202.385-59.511-61.158z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"isMulticolor":true,"isMulticolor2":true,"grid":0,"tags":["ordering-descending"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":5}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(158, 162, 168)","opacity":0.7}],"properties":{"order":605,"id":58,"name":"ordering-descending","prevSize":32,"code":59772,"codes":[59772,59773]},"setIdx":0,"setId":2,"iconIdx":123},{"icon":{"paths":["M512.674 928c229.751 0 416-186.249 416-416s-186.249-416-416-416c-229.749 0-415.998 186.249-415.998 416s186.249 416 415.998 416zM400.675 383.995c0-17.673 14.327-32 32-32s32 14.327 32 32v256.001c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256.001zM560.674 383.995c0-17.673 14.327-32 32-32s32 14.327 32 32v256.001c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256.001z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause-filled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":606,"id":57,"name":"pause-filled","prevSize":32,"code":59774},"setIdx":0,"setId":2,"iconIdx":124},{"icon":{"paths":["M905.088 512c0-194.404-157.598-352-352-352-194.405 0-352.001 157.596-352.001 352 0 194.402 157.596 352 352.001 352 194.402 0 352-157.598 352-352zM969.088 512c0 229.751-186.249 416-416 416s-416.001-186.249-416.001-416c0-229.751 186.25-416 416.001-416s416 186.249 416 416zM441.088 383.995v256.001c0 17.673 14.323 32 32 32 17.673 0 32-14.327 32-32v-256.001c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32zM601.084 383.995v256.001c0 17.673 14.327 32 32 32s32-14.327 32-32v-256.001c0-17.673-14.327-32-32-32s-32 14.327-32 32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pause"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":607,"id":56,"name":"pause","prevSize":32,"code":59775},"setIdx":0,"setId":2,"iconIdx":125},{"icon":{"paths":["M825.374 153.373c12.497-12.497 32.755-12.497 45.252 0s12.497 32.758 0 45.255l-671.999 671.999c-12.497 12.497-32.758 12.497-45.255 0s-12.497-32.755 0-45.252l672.001-672.001zM575.612 569.067l-44.706 44.71c7.846 5.905 15.846 11.452 23.535 16.013 9.199 5.461 22.835 12.578 36.971 13.222 16.226 0.738 31.445-5.299 42.47-10.974 11.742-6.042 22.793-13.824 31.744-20.945 1.054-0.836 2.133-1.229 2.884-1.34 0.401-0.060 0.61-0.030 0.687-0.013l121.097 59.076c-5.581 29.329-20.382 66.769-42.394 92.621-11.23 13.184-23.198 22.037-35.388 26.317-11.597 4.075-25.404 4.766-42.65-1.673-48.427-18.074-103.407-53.154-151.693-89.263-14.029-10.492-27.298-20.919-39.467-30.844l-44.39 44.39c14.187 11.686 29.841 24.064 46.447 36.48 49.805 37.248 110.447 76.561 167.258 97.762 30.276 11.298 59.149 11.238 85.197 2.086 25.455-8.939 46.093-25.783 62.246-44.757 31.868-37.423 50.637-88.141 57.062-126.246 4.719-27.985-11.776-51.767-33.139-62.191l-122.807-59.908c-24.41-11.908-51.46-6.003-69.837 8.61-7.006 5.572-14.541 10.739-21.44 14.289-5.577 2.867-8.93 3.742-10.313 4.006l-0.094-0.038c-0.99-0.401-3.729-1.515-8.567-4.386-3.277-1.946-6.874-4.301-10.714-7.006zM313.636 589.683l44.39-44.39c-9.92-12.164-20.35-25.438-30.839-39.462-36.108-48.286-71.191-103.267-89.262-151.691-6.437-17.249-5.748-31.055-1.675-42.651 4.282-12.192 13.134-24.16 26.319-35.388 25.85-22.012 63.29-36.814 92.623-42.392l59.075 121.096c0.018 0.076 0.047 0.285-0.012 0.687-0.111 0.75-0.504 1.83-1.343 2.884-7.118 8.951-14.9 20.001-20.943 31.741-5.673 11.025-11.714 26.246-10.976 42.472 0.643 14.135 7.761 27.772 13.222 36.971 4.562 7.689 10.109 15.684 16.014 23.535l44.705-44.71c-2.701-3.836-5.056-7.433-7.002-10.709-2.867-4.838-3.981-7.578-4.386-8.567l-0.038-0.094c0.265-1.382 1.139-4.738 4.006-10.312 3.55-6.9 8.717-14.437 14.289-21.443 14.613-18.374 20.523-45.425 8.614-69.835l-59.913-122.809c-10.421-21.361-34.203-37.856-62.191-33.137-38.103 6.425-88.824 25.194-126.246 57.060-18.972 16.155-35.817 36.795-44.758 62.246-9.15 26.048-9.211 54.92 2.088 85.196 21.201 56.813 60.513 117.455 97.761 167.26 12.417 16.606 24.795 32.256 36.478 46.443z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":608,"id":55,"name":"phone-disabled","prevSize":32,"code":59776},"setIdx":0,"setId":2,"iconIdx":126},{"icon":{"paths":["M801.122 657.591c22.477 7.735 50.957 2.581 67.409-20.544 22.4-31.488 44.992-80.623 48.922-129.617 1.993-24.841-0.691-51.345-12.365-75.665-11.951-24.887-32.324-45.347-61.722-58.766-55.164-25.181-125.841-40.262-187.396-49.144-61.948-8.937-117.495-11.981-143.966-11.981v62.47c23.198 0 75.755 2.787 135.044 11.341 59.674 8.61 123.358 22.681 170.377 44.143 16.751 7.646 26.027 17.894 31.347 28.975 5.589 11.648 7.795 26.372 6.409 43.635-2.714 33.839-18.718 70.784-35.516 95.467l-127.398-43.853c-0.068-0.043-0.239-0.171-0.478-0.495-0.452-0.61-0.939-1.651-1.092-2.991-1.297-11.362-3.605-24.678-7.633-37.252-3.785-11.806-10.278-26.842-22.272-37.794-10.449-9.54-25.126-14.148-35.494-16.794-11.917-3.038-25.566-5.18-38.946-6.716-26.859-3.085-55.77-4.092-74.351-4.092v62.468c16.9 0 43.341 0.939 67.217 3.686 11.994 1.374 22.554 3.123 30.643 5.184 5.449 1.391 8.175 2.543 9.161 2.957l0.094 0.043c0.789 1.161 2.543 4.151 4.459 10.121 2.368 7.39 4.041 16.375 5.056 25.267 2.662 23.326 17.613 46.63 43.294 55.471l129.199 44.476zM222.876 657.587c-22.474 7.735-50.954 2.581-67.407-20.544-22.4-31.488-44.993-80.623-48.922-129.617-1.992-24.841 0.691-51.345 12.367-75.665 11.948-24.887 32.321-45.347 61.719-58.765 55.165-25.181 125.841-40.263 187.399-49.144 61.946-8.937 117.494-11.981 143.964-11.981v62.47c-23.198 0-75.755 2.787-135.043 11.341-59.675 8.61-123.36 22.681-170.38 44.143-16.748 7.646-26.025 17.894-31.343 28.975-5.593 11.648-7.797 26.372-6.412 43.635 2.714 33.843 18.721 70.784 35.518 95.467l127.4-43.853c0.067-0.043 0.235-0.171 0.478-0.495 0.452-0.61 0.937-1.651 1.090-2.991 1.296-11.362 3.606-24.678 7.636-37.252 3.784-11.806 10.276-26.842 22.27-37.794 10.45-9.54 25.127-14.148 35.493-16.794 11.916-3.038 25.568-5.18 38.947-6.716 26.859-3.085 55.77-4.092 74.351-4.092v62.468c-16.9 0-43.341 0.939-67.221 3.686-11.989 1.378-22.551 3.123-30.638 5.184-5.452 1.391-8.175 2.543-9.162 2.957l-0.092 0.043c-0.791 1.161-2.546 4.151-4.459 10.121-2.368 7.39-4.044 16.375-5.058 25.267-2.66 23.326-17.611 46.63-43.292 55.471l-129.202 44.476z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone-end"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":609,"id":54,"name":"phone-end","prevSize":32,"code":59777},"setIdx":0,"setId":2,"iconIdx":127},{"icon":{"paths":["M410.504 204.615c-10.421-21.361-34.203-37.856-62.191-33.137-38.103 6.425-88.824 25.194-126.246 57.060-18.972 16.155-35.817 36.795-44.758 62.246-9.15 26.048-9.211 54.92 2.088 85.196 21.201 56.813 60.513 117.455 97.761 167.26 37.482 50.125 74.609 91.554 93.327 110.272l44.173-44.173c-16.405-16.405-51.598-55.539-87.472-103.509-36.108-48.286-71.191-103.267-89.262-151.691-6.437-17.249-5.748-31.055-1.675-42.651 4.282-12.192 13.134-24.16 26.319-35.388 25.85-22.012 63.29-36.814 92.623-42.392l59.075 121.096c0.018 0.075 0.047 0.285-0.012 0.687-0.111 0.75-0.504 1.83-1.343 2.884-7.118 8.951-14.9 20.001-20.943 31.741-5.673 11.025-11.714 26.246-10.976 42.472 0.643 14.135 7.761 27.772 13.222 36.971 6.277 10.577 14.417 21.743 22.79 32.29 16.809 21.175 36.542 42.33 49.679 55.467l44.173-44.173c-11.951-11.951-29.978-31.309-44.924-50.133-7.505-9.455-13.739-18.159-18.001-25.335-2.867-4.838-3.981-7.578-4.386-8.567l-0.038-0.094c0.265-1.382 1.139-4.738 4.006-10.312 3.55-6.9 8.717-14.437 14.289-21.443 14.613-18.374 20.523-45.425 8.614-69.835l-59.913-122.809zM819.383 613.5c21.363 10.423 37.858 34.206 33.139 62.191-6.426 38.106-25.195 88.823-57.062 126.246-16.154 18.974-36.791 35.819-62.246 44.757-26.048 9.152-54.921 9.212-85.197-2.086-56.811-21.201-117.453-60.514-167.258-97.762-50.125-37.483-91.555-74.607-110.273-93.325l44.173-44.173c16.407 16.405 55.54 51.597 103.511 87.471 48.286 36.109 103.266 71.189 151.693 89.263 17.246 6.434 31.053 5.747 42.65 1.673 12.19-4.279 24.158-13.133 35.388-26.317 22.012-25.852 36.813-63.292 42.394-92.621l-121.097-59.076c-0.077-0.017-0.286-0.047-0.687 0.013-0.751 0.111-1.83 0.503-2.884 1.34-8.951 7.121-20.002 14.903-31.744 20.945-11.025 5.675-26.244 11.712-42.47 10.974-14.135-0.644-27.772-7.761-36.971-13.222-10.577-6.276-21.743-14.417-32.29-22.788-21.175-16.811-42.33-36.544-55.467-49.681l44.173-44.173c11.951 11.951 31.309 29.982 50.133 44.928 9.455 7.501 18.159 13.734 25.335 17.997 4.838 2.871 7.578 3.985 8.567 4.386l0.094 0.038c1.382-0.265 4.736-1.139 10.313-4.006 6.899-3.55 14.434-8.717 21.44-14.289 18.377-14.613 45.427-20.518 69.837-8.61l122.807 59.908z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":610,"id":53,"name":"phone","prevSize":32,"code":59778},"setIdx":0,"setId":2,"iconIdx":128},{"icon":{"paths":["M512.606 128.018c-72.375-1.375-144.904 25.78-199.305 80.524-54.654 54.999-89.301 136.056-89.301 239.466 0 80.23 44.5 174.869 97.546 252.804 53.065 77.965 120.83 148.16 176.139 175.821l15.194 7.603 14.81-8.333c217.156-122.15 272.311-333.879 272.311-427.895 0-101.732-27.925-181.775-80.179-236.931-52.343-55.247-125.295-81.503-207.215-83.060zM288 448.009c0-88.591 29.353-152.747 70.699-194.353 41.599-41.862 97.071-62.705 152.691-61.648 68.992 1.311 124.041 23.055 161.971 63.089 38.016 40.126 62.639 102.647 62.639 192.913 0 74.551-44.689 253.679-224.175 363.034-39.684-25.613-92.187-79.855-137.371-146.24-50.954-74.863-86.454-156.22-86.454-216.794zM544 416c0-17.673-14.327-32-32-32s-32 14.327-32 32c0 17.673 14.327 32 32 32s32-14.327 32-32zM608 416c0 53.018-42.982 96-96 96s-96-42.982-96-96c0-53.019 42.982-96 96-96s96 42.981 96 96z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin-map"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":611,"id":52,"name":"pin-map","prevSize":32,"code":59779},"setIdx":0,"setId":2,"iconIdx":129},{"icon":{"paths":["M403.11 199.401c17.543-17.543 45.987-17.543 63.531 0 17.54 17.543 17.54 45.987 0 63.53l-43.66 43.659 177.83 177.827 60.339-60.338c24.994-24.994 65.515-24.994 90.509 0l45.257 45.256-331.87 331.87-45.255-45.257c-24.994-24.994-24.994-65.515 0-90.509l60.338-60.339-177.827-177.83-43.66 43.661c-17.543 17.545-45.987 17.545-63.53 0s-17.543-45.986 0-63.529l207.999-207.999zM149.856 362.145c-42.537 42.537-42.537 111.502 0 154.041 42.001 42.001 109.771 42.53 152.421 1.583l87.335 87.339-15.077 15.074c-49.987 49.988-49.987 131.034 0 181.022l67.884 67.878c12.497 12.497 32.755 12.497 45.252 0l167.936-167.932 88.013 92.002c12.215 12.77 32.474 13.222 45.244 1.003 12.77-12.215 13.218-32.474 1.003-45.244l-88.994-93.026 163.925-163.925c12.493-12.497 12.493-32.759 0-45.257l-67.883-67.88c-49.988-49.987-131.034-49.987-181.022 0l-15.906 15.907-87.322-87.322c41.762-42.596 41.506-110.984-0.772-153.262-42.534-42.537-111.502-42.537-154.038 0l-207.999 207.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pin"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":612,"id":51,"name":"pin","prevSize":32,"code":59780},"setIdx":0,"setId":2,"iconIdx":130},{"icon":{"paths":["M533.333 128c200.294 0 362.667 162.371 362.667 362.667h-362.667v-362.667z","M128 533.333c0-200.295 162.371-362.667 362.667-362.667v362.667h362.667c0 200.294-162.372 362.667-362.667 362.667s-362.667-162.372-362.667-362.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["pizza-graph"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":613,"id":50,"name":"pizza-graph","prevSize":32,"code":59781},"setIdx":0,"setId":2,"iconIdx":131},{"icon":{"paths":["M512.678 928.004c229.747 0 416-186.249 416-416 0-229.749-186.253-415.999-416-415.999-229.752 0-416.001 186.25-416.001 415.999 0 229.751 186.249 416 416.001 416zM452.523 357.193l195.255 136.768c14.208 9.95 18.496 30.874 9.583 46.729-2.432 4.322-5.709 7.979-9.583 10.692l-195.255 136.768c-14.204 9.95-32.95 5.163-41.866-10.692-3.036-5.397-4.646-11.644-4.646-18.018v-273.536c0-18.72 13.597-33.897 30.372-33.897 5.709 0 11.307 1.798 16.141 5.186z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play-filled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":614,"id":49,"name":"play-filled","prevSize":32,"code":59782},"setIdx":0,"setId":2,"iconIdx":132},{"icon":{"paths":["M864 512c0-194.404-157.598-352-352-352-194.404 0-352 157.596-352 352 0 194.402 157.596 352 352 352 194.402 0 352-157.598 352-352zM928 512c0 229.751-186.249 416-416 416s-416-186.249-416-416c0-229.751 186.249-416 416-416s416 186.249 416 416zM451.849 357.186l195.255 136.766c14.208 9.954 18.496 30.874 9.579 46.733-2.432 4.322-5.705 7.979-9.579 10.692l-195.255 136.768c-14.208 9.95-32.952 5.163-41.868-10.692-3.036-5.402-4.646-11.648-4.646-18.022v-273.534c0-18.72 13.597-33.897 30.369-33.897 5.713 0 11.307 1.798 16.145 5.186z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["play"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":615,"id":48,"name":"play","prevSize":32,"code":59783},"setIdx":0,"setId":2,"iconIdx":133},{"icon":{"paths":["M321.626 430.882l224.49 224.491-126.12 126.118-224.49-224.491 126.12-126.118zM366.881 385.626l192.116-192.116 224.491 224.49-192.115 192.116-224.492-224.49zM581.623 125.628c-12.497-12.497-32.759-12.497-45.257 0l-408.743 408.747c-12.497 12.497-12.497 32.755 0 45.252l269.745 269.747c5.23 5.231 11.82 8.269 18.631 9.122v0.068h0.571c2.276 0.243 4.573 0.243 6.85 0h440.579c17.673 0 32-14.327 32-32s-14.327-32-32-32h-366.566l353.937-353.937c12.497-12.497 12.497-32.757 0-45.254l-269.747-269.745z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["prune"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":619,"id":44,"name":"prune","prevSize":32,"code":59789},"setIdx":0,"setId":2,"iconIdx":137},{"icon":{"paths":["M230.487 636.693c-14.92-9.472-34.694-5.056-44.167 9.865-9.473 14.916-5.058 34.692 9.862 44.164l298.665 189.632c10.47 6.647 23.838 6.647 34.304 0l298.667-189.632c14.921-9.472 19.337-29.248 9.865-44.164-9.472-14.921-29.248-19.337-44.169-9.865l-281.515 178.739-281.513-178.739zM186.32 494.852c9.473-14.921 29.247-19.337 44.167-9.86l281.513 178.739 281.515-178.739c14.921-9.476 34.697-5.060 44.169 9.86s5.056 34.697-9.865 44.169l-298.667 189.628c-10.466 6.647-23.834 6.647-34.304 0l-298.665-189.628c-14.92-9.472-19.336-29.248-9.862-44.169zM529.152 143.663l298.667 189.63c9.25 5.871 14.848 16.062 14.848 27.015s-5.598 21.144-14.848 27.015l-298.667 189.629c-10.466 6.647-23.834 6.647-34.304 0l-298.665-189.629c-9.246-5.871-14.848-16.062-14.848-27.015s5.602-21.144 14.848-27.015l298.665-189.63c10.47-6.647 23.838-6.647 34.304 0zM273.035 360.307l238.965 151.723 238.967-151.723-238.967-151.725-238.965 151.725z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["queue"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":620,"id":43,"name":"queue","prevSize":32,"code":59790},"setIdx":0,"setId":2,"iconIdx":138},{"icon":{"paths":["M160 800c-11.706 0-22.478-6.391-28.087-16.666s-5.161-22.793 1.169-32.64l112.304-174.694h-69.387c-17.673 0-32-14.327-32-32v-288c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v288c0 6.135-1.762 12.143-5.082 17.306l-144.001 224c-5.888 9.156-16.029 14.694-26.918 14.694h-128zM330.918 561.306l-112.304 174.694h51.916l129.47-201.399v-246.601h-192v224h96c11.706 0 22.478 6.391 28.087 16.666s5.161 22.793-1.169 32.64zM576 800c-11.708 0-22.477-6.391-28.087-16.666s-5.158-22.793 1.169-32.64l112.307-174.694h-69.389c-17.673 0-32-14.327-32-32v-288c0-17.673 14.327-32 32-32h256c17.673 0 32 14.327 32 32v288c0 6.135-1.762 12.143-5.082 17.306l-144 224c-5.888 9.156-16.030 14.694-26.918 14.694h-128zM746.918 561.306l-112.307 174.694h51.917l129.472-201.399v-246.601h-192v224h96c11.708 0 22.477 6.391 28.087 16.666s5.158 22.793-1.169 32.64z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["quote"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":621,"id":42,"name":"quote","prevSize":32,"code":59791},"setIdx":0,"setId":2,"iconIdx":139},{"icon":{"paths":["M802.257 544c0 176.73-143.27 320-320 320-176.733 0-320.002-143.27-320.002-320 0-176.731 143.269-320 320.002-320v-64c-212.079 0-384.002 171.923-384.002 384 0 212.079 171.922 384 384.002 384 212.075 0 384-171.921 384-384 0-10.778-0.444-21.449-1.314-32h-64.269c1.045 10.526 1.583 21.201 1.583 32zM802.257 112c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32v112h-112c-17.677 0-32 14.327-32 32s14.323 32 32 32h112v112c0 17.673 14.323 32 32 32 17.673 0 32-14.327 32-32v-112h112c17.673 0 32-14.327 32-32s-14.327-32-32-32h-112v-112zM386.255 512c35.346 0 64.002-28.655 64.002-64 0-35.346-28.656-64-64.002-64s-64 28.654-64 64c0 35.345 28.654 64 64 64zM642.257 448c0 35.345-28.655 64-64 64-35.349 0-64-28.655-64-64 0-35.346 28.651-64 64-64 35.345 0 64 28.654 64 64zM332.060 589.077c-10.451-14.251-30.477-17.335-44.729-6.882-14.252 10.449-17.332 30.477-6.881 44.727 37.658 51.354 77.753 84.625 119.178 102.665 41.737 18.18 82.796 19.989 120.364 11.652 73.468-16.307 132.211-70.874 164.070-114.317 10.449-14.251 7.369-34.278-6.882-44.727-14.251-10.453-34.278-7.369-44.727 6.882-26.812 36.557-73.668 77.99-126.332 89.681-25.502 5.662-52.642 4.476-80.938-7.846-28.608-12.459-60.38-37.188-93.122-81.835z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["reaction-add"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":622,"id":41,"name":"reaction-add","prevSize":32,"code":59792},"setIdx":0,"setId":2,"iconIdx":140},{"icon":{"paths":["M832.678 512c0-176.731-143.27-320-320-320-176.733 0-320.001 143.269-320.001 320 0 176.73 143.269 320 320.001 320 176.73 0 320-143.27 320-320zM896.678 512c0 212.079-171.925 384-384 384-212.079 0-384.001-171.921-384.001-384 0-212.077 171.923-384 384.001-384 212.075 0 384 171.923 384 384zM512.678 704c-106.040 0-192.001-85.961-192.001-192s85.961-192 192.001-192c106.039 0 192 85.961 192 192s-85.961 192-192 192z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["record"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":623,"id":40,"name":"record","prevSize":32,"code":59793},"setIdx":0,"setId":2,"iconIdx":141},{"icon":{"paths":["M896 512h-63.987c0-175.415-145.749-320-328.516-320-115.27 0-215.817 57.513-274.36 144h110.768c17.673 0 32 14.327 32 32s-14.327 32-32 32h-179.904c-17.673 0-32-14.327-32-32v-192c0-17.673 14.327-32 32-32s32 14.327 32 32v102.32c71.751-91.401 184.591-150.32 311.497-150.32 216.781 0 392.503 171.923 392.503 384 0 1.114 0.009 2.227 0 3.337v-3.337z","M127.997 512h63.986c0 175.415 145.751 320 328.52 320 115.268 0 215.817-57.515 274.359-144h-110.767c-17.677 0-32-14.327-32-32s14.323-32 32-32h179.904c17.673 0 32 14.327 32 32v192c0 17.673-14.327 32-32 32s-32-14.327-32-32v-102.319c-71.753 91.401-184.593 150.319-311.497 150.319-216.783 0-392.506-171.921-392.506-384 0-1.084-0.009-2.163 0-3.247v3.247z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["refresh"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":624,"id":39,"name":"refresh","prevSize":32,"code":59794},"setIdx":0,"setId":2,"iconIdx":142},{"icon":{"paths":["M245.333 480h533.333c17.673 0 32 14.327 32 32s-14.327 32-32 32h-533.333c-17.673 0-32-14.327-32-32s14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["Remove"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":625,"id":38,"name":"Remove","prevSize":32,"code":59795},"setIdx":0,"setId":2,"iconIdx":143},{"icon":{"paths":["M708.723 449.771c0-142.502-115.674-258.022-258.364-258.022-142.687 0-258.359 115.52-258.359 258.022s115.673 258.022 258.359 258.022c142.69 0 258.364-115.52 258.364-258.022zM654.379 699.042c-55.573 45.419-126.609 72.666-204.019 72.666-178.033 0-322.359-144.137-322.359-321.937s144.326-321.937 322.359-321.937c178.035 0 322.364 144.137 322.364 321.937 0 77.303-27.281 148.245-72.755 203.742l186.59 186.347c12.591 12.574 12.591 32.956 0 45.53-12.587 12.574-32.998 12.574-45.589 0l-186.59-186.347z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["search"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":626,"id":37,"name":"search","prevSize":32,"code":59796},"setIdx":0,"setId":2,"iconIdx":144},{"icon":{"paths":["M891.494 238.961l-297.476 637.446c-16.853 36.113-69.619 31.458-79.889-7.049l-66.001-247.501 151.249-189.056-219.993 109.995-226.834-113.417c-35.43-17.715-29.696-69.947 8.733-79.555l681.2-170.3c34.837-8.71 64.196 26.892 49.011 59.436z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send-filled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":627,"id":36,"name":"send-filled","prevSize":32,"code":59797},"setIdx":0,"setId":2,"iconIdx":145},{"icon":{"paths":["M878.020 192.976c7.851 9.521 9.527 22.708 4.309 33.891l-298.667 640c-5.692 12.194-18.402 19.529-31.808 18.342-13.406-1.182-24.64-10.628-28.109-23.629l-81.617-306.074-285.774-142.884c-11.977-5.989-18.959-18.802-17.498-32.113s11.056-24.305 24.048-27.553l682.667-170.667c11.972-2.993 24.597 1.165 32.448 10.686zM505.822 545.967l57.079 214.054 233.028-499.351-533.581 133.396 203.605 101.802 69.513-52.134c14.14-10.603 34.197-7.735 44.8 6.4 10.603 14.14 7.74 34.197-6.4 44.8l-68.045 51.034z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["send"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":628,"id":35,"name":"send","prevSize":32,"code":59798},"setIdx":0,"setId":2,"iconIdx":146},{"icon":{"paths":["M421.334 256c0-44.183-35.817-80-80-80s-80 35.817-80 80c0 44.183 35.817 80 80 80s80-35.817 80-80zM465.301 288c-14.208 55.207-64.324 96-123.967 96s-109.758-40.793-123.968-96h-89.367c-17.673 0-32-14.327-32-32s14.327-32 32-32h89.367c14.209-55.207 64.325-96 123.968-96s109.759 40.793 123.967 96h430.699c17.673 0 32 14.327 32 32s-14.327 32-32 32h-430.699zM96 768c0-17.673 14.327-32 32-32h89.367c14.209-55.206 64.325-96 123.968-96 60.782 0 111.671 42.368 124.744 99.179 4.207-2.035 8.93-3.179 13.922-3.179h416c17.673 0 32 14.327 32 32s-14.327 32-32 32h-416c-4.992 0-9.715-1.143-13.922-3.179-13.073 56.811-63.962 99.179-124.744 99.179-59.643 0-109.758-40.794-123.968-96h-89.367c-17.673 0-32-14.327-32-32zM341.334 848c44.183 0 80-35.819 80-80s-35.817-80-80-80c-44.183 0-80 35.819-80 80s35.817 80 80 80zM796.032 543.757c-14.127 55.33-64.303 96.243-124.032 96.243s-109.905-40.913-124.028-96.243c-1.301 0.162-2.628 0.243-3.972 0.243h-416c-17.673 0-32-14.327-32-32s14.327-32 32-32h416c1.344 0 2.671 0.081 3.972 0.243 14.123-55.331 64.299-96.243 124.028-96.243s109.905 40.913 124.032 96.243c1.297-0.162 2.624-0.243 3.968-0.243h96c17.673 0 32 14.327 32 32s-14.327 32-32 32h-96c-1.344 0-2.671-0.081-3.968-0.243zM752 512c0-44.181-35.819-80-80-80s-80 35.819-80 80c0 44.181 35.819 80 80 80s80-35.819 80-80z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["settings"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":629,"id":34,"name":"settings","prevSize":32,"code":59799},"setIdx":0,"setId":2,"iconIdx":147},{"icon":{"paths":["M694.626 300.76c12.497-12.854 12.497-33.694 0-46.548l-160-164.571c-12.497-12.854-32.755-12.854-45.252 0l-160.001 164.571c-12.497 12.854-12.497 33.694 0 46.548s32.758 12.854 45.255 0l105.373-108.383v430.711c0 18.176 14.327 32.913 32 32.913s32-14.737 32-32.913v-430.711l105.374 108.384c12.497 12.854 32.755 12.854 45.252 0z","M192 384h160v64h-128v416h576v-416h-128v-64h160c17.673 0 32 14.327 32 32v480c0 17.673-14.327 32-32 32h-640c-17.673 0-32-14.327-32-32v-480c0-17.673 14.327-32 32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["share"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":630,"id":33,"name":"share","prevSize":32,"code":59800},"setIdx":0,"setId":2,"iconIdx":148},{"icon":{"paths":["M664.081 405.067c11.639-13.3 10.291-33.517-3.008-45.155s-33.519-10.29-45.154 3.011l-125.252 143.142-39.919-45.619c-11.635-13.299-31.852-14.647-45.153-3.008-13.3 11.635-14.648 31.851-3.011 45.154l64.001 73.143c6.076 6.942 14.852 10.927 24.081 10.927s18.005-3.985 24.081-10.927l149.333-170.669z","M541.99 143.045c-12.855-3.633-26.449-3.753-39.364-0.345l-264.722 69.849c-29.793 7.861-52.548 33.743-54.753 65.632-23.59 341.206 187.75 520.291 276.51 579.794 33.135 22.212 75.362 22.144 108.416-0.26 88.218-59.789 297.118-239.070 272.614-580.213-2.253-31.321-24.341-56.895-53.508-65.14l-245.193-69.318zM518.955 204.582c1.847-0.487 3.789-0.47 5.623 0.049l245.197 69.318c4.292 1.214 6.839 4.781 7.078 8.139 22.153 308.364-165.303 468.85-244.685 522.653-11.435 7.753-25.365 7.787-36.873 0.073-80.083-53.683-269.603-214.050-248.298-522.217 0.234-3.389 2.833-7.004 7.234-8.165l264.723-69.849z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield-check"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":631,"id":32,"name":"shield-check","prevSize":32,"code":59801},"setIdx":0,"setId":2,"iconIdx":149},{"icon":{"paths":["M515.558 142.7c12.915-3.408 26.505-3.288 39.36 0.345l245.197 69.318c29.163 8.245 51.255 33.818 53.504 65.14 24.503 341.143-184.397 520.424-272.614 580.213-33.050 22.404-75.277 22.473-108.416 0.26-88.759-59.503-300.099-238.588-276.509-579.794 2.205-31.89 24.96-57.772 54.753-65.632l264.726-69.849zM537.506 204.631c-1.835-0.519-3.776-0.536-5.623-0.049l-264.722 69.849c-4.4 1.161-6.999 4.776-7.233 8.165-21.305 308.167 168.215 468.534 248.301 522.217 11.507 7.714 25.434 7.68 36.873-0.073 79.377-53.803 266.833-214.289 244.681-522.653-0.239-3.358-2.786-6.925-7.078-8.139l-245.197-69.318z","M492.928 337.331c-19.759 4.672-47.249 12.307-81.151 24.553 20.812 108.773 53.221 187.015 81.151 238.829v-263.382zM495.125 271.311c33.766-6.897 61.803 19.742 61.803 51.124v360.966c0 17.024-10.334 31.748-25.643 37.521-15.671 5.909-33.779 1.327-44.86-12.676-32.657-41.267-106.324-152.951-141.192-354.565-3.241-18.744 7.065-37.694 25.329-44.725 56.185-21.63 99.095-32.442 124.562-37.644z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shield"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":632,"id":31,"name":"shield","prevSize":32,"code":59802},"setIdx":0,"setId":2,"iconIdx":150},{"icon":{"paths":["M778.641 213.333c-17.673 0-32 14.327-32 32v533.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-533.333c0-17.673-14.327-32-32-32z","M600.87 341.333c-17.673 0-32 14.327-32 32v405.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-405.333c0-17.673-14.327-32-32-32z","M423.103 810.667c-17.673 0-32-14.327-32-32v-277.333c0-17.673 14.327-32 32-32s32.001 14.327 32.001 32v277.333c0 17.673-14.327 32-32.001 32z","M245.333 597.333c-17.673 0-32 14.327-32 32v149.333c0 17.673 14.327 32 32 32s32-14.327 32-32v-149.333c0-17.673-14.327-32-32-32z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["signal"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":633,"id":30,"name":"signal","prevSize":32,"code":59803},"setIdx":0,"setId":2,"iconIdx":151},{"icon":{"paths":["M292.515 192c13.364 0 25.321 8.305 29.987 20.829l96 257.699c6.17 16.559-2.255 34.987-18.816 41.156s-34.988-2.253-41.158-18.816l-20.476-54.963h-91.075l-20.476 54.963c-6.17 16.563-24.596 24.986-41.158 18.816s-24.985-24.597-18.816-41.156l96-257.699c4.666-12.524 16.623-20.829 29.987-20.829zM314.211 373.904l-21.695-58.238-21.696 58.238h43.391z","M525.286 626.436c-12.156 12.826-11.61 33.079 1.22 45.235l160 151.586c12.343 11.695 31.676 11.695 44.019 0l160-151.586c12.826-12.156 13.376-32.41 1.22-45.235-12.156-12.83-32.41-13.38-45.239-1.225l-105.993 100.42v-501.632c0-17.673-14.327-32-32-32s-32 14.327-32 32v501.632l-105.988-100.42c-12.83-12.156-33.084-11.605-45.239 1.225z","M196.515 570.944c-17.673 0-32 14.327-32 32 0 17.677 14.327 32 32 32h116.145l-139.065 142.733c-8.979 9.216-11.565 22.916-6.564 34.769 5.001 11.857 16.617 19.563 29.484 19.563h192c17.673 0 32-14.327 32-32s-14.327-32-32-32h-116.145l139.065-142.733c8.979-9.216 11.565-22.912 6.564-34.769-5.001-11.853-16.617-19.563-29.484-19.563h-192z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-az"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":634,"id":29,"name":"sort-az","prevSize":32,"code":59804},"setIdx":0,"setId":2,"iconIdx":152},{"icon":{"paths":["M922.551 666.351c12.587-12.237 12.86-32.35 0.606-44.919-12.254-12.574-32.393-12.843-44.983-0.606l-105.971 103.019v-500.139c0-17.545-14.242-31.767-31.808-31.767-17.57 0-31.812 14.223-31.812 31.767v500.139l-105.971-103.019c-12.591-12.237-32.73-11.968-44.983 0.606-12.254 12.57-11.981 32.683 0.606 44.919l159.97 155.511c12.352 12.006 32.026 12.006 44.378 0l159.97-155.511zM564.425 333.229c17.57 0 31.808-14.223 31.808-31.767s-14.238-31.767-31.808-31.767h-431.919c-17.568 0-31.809 14.223-31.809 31.767s14.242 31.767 31.809 31.767h431.919zM468.442 535.394c17.57 0 31.812-14.221 31.812-31.765s-14.242-31.77-31.812-31.77h-335.936c-17.568 0-31.809 14.225-31.809 31.77s14.242 31.765 31.809 31.765h335.936zM388.458 722.010c17.568 0 31.809-14.221 31.809-31.765s-14.241-31.77-31.809-31.77h-255.952c-17.568 0-31.809 14.225-31.809 31.77s14.241 31.765 31.809 31.765h255.952z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":635,"id":28,"name":"sort","prevSize":32,"code":59805},"setIdx":0,"setId":2,"iconIdx":153},{"icon":{"paths":["M192 405.333h213.333v-213.333h-213.333v213.333zM128 170.667c0-23.564 19.103-42.667 42.667-42.667h256c23.565 0 42.667 19.103 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.564 0-42.667-19.102-42.667-42.667v-256zM618.667 405.333h213.333v-213.333h-213.333v213.333zM554.667 170.667c0-23.564 19.102-42.667 42.667-42.667h256c23.565 0 42.667 19.103 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.565 0-42.667-19.102-42.667-42.667v-256zM618.667 618.667h213.333v213.333h-213.333v-213.333zM597.333 554.667c-23.565 0-42.667 19.102-42.667 42.667v256c0 23.565 19.102 42.667 42.667 42.667h256c23.565 0 42.667-19.102 42.667-42.667v-256c0-23.565-19.102-42.667-42.667-42.667h-256zM192 832h213.333v-213.333h-213.333v213.333zM128 597.333c0-23.565 19.103-42.667 42.667-42.667h256c23.565 0 42.667 19.102 42.667 42.667v256c0 23.565-19.102 42.667-42.667 42.667h-256c-23.564 0-42.667-19.102-42.667-42.667v-256z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["squares"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":636,"id":27,"name":"squares","prevSize":32,"code":59806},"setIdx":0,"setId":2,"iconIdx":154},{"icon":{"paths":["M593.937 170.244c-20.864-57.726-103.155-55.816-121.318 2.816l-58.978 190.35h-221.512c-58.947 0-86.549 72.943-42.37 111.965l157.591 139.204-53.209 216.93c-14.17 57.771 51.25 101.935 99.535 67.2l177.739-127.881 177.741 127.881c48.286 34.735 113.707-9.429 99.537-67.2l-53.389-217.651 147.891-139.968c42.031-39.778 13.879-110.481-43.994-110.481h-195.439l-69.824-193.165z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star-filled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":637,"id":26,"name":"star-filled","prevSize":32,"code":59807},"setIdx":0,"setId":2,"iconIdx":155},{"icon":{"paths":["M472.619 172.834c18.163-58.554 100.454-60.462 121.318-2.812l69.824 192.91h195.439c57.873 0 86.025 70.608 43.994 110.335l-147.891 139.785 53.389 217.361c14.17 57.694-51.251 101.803-99.537 67.11l-177.741-127.706-177.739 127.706c-48.284 34.692-113.705-9.417-99.535-67.11l53.209-216.64-157.591-139.021c-44.179-38.976-16.577-111.82 42.37-111.82h221.512l58.978-190.098zM603.571 384.66l-69.82-192.91-58.978 190.098c-8.303 26.758-33.084 44.998-61.133 44.998h-221.512l157.591 139.025c17.834 15.731 25.456 40.047 19.787 63.125l-53.209 216.644 177.74-127.706c22.332-16.047 52.425-16.047 74.756 0l177.741 127.706-53.389-217.361c-5.478-22.319 1.455-45.854 18.167-61.649l147.887-139.785h-195.439c-26.957 0-51.025-16.868-60.19-42.186z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":638,"id":25,"name":"star","prevSize":32,"code":59808},"setIdx":0,"setId":2,"iconIdx":156},{"icon":{"paths":["M520.533 827.733c169.66 0 307.2-137.54 307.2-307.2 0-169.662-137.54-307.2-307.2-307.2-169.662 0-307.2 137.538-307.2 307.2 0 169.66 137.538 307.2 307.2 307.2zM418.133 477.867c-23.564 0-42.667 19.102-42.667 42.667 0 23.561 19.103 42.667 42.667 42.667h204.8c23.565 0 42.667-19.106 42.667-42.667 0-23.565-19.102-42.667-42.667-42.667h-204.8z"],"attrs":[{"fill":"rgb(245, 69, 92)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-busy"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":4}]}},"attrs":[{"fill":"rgb(245, 69, 92)"}],"properties":{"order":640,"id":23,"name":"status-busy","prevSize":32,"code":59810},"setIdx":0,"setId":2,"iconIdx":158},{"icon":{"paths":["M520.533 742.4c-122.534 0-221.867-99.332-221.867-221.867s99.333-221.867 221.867-221.867c122.534 0 221.867 99.333 221.867 221.867s-99.332 221.867-221.867 221.867zM520.533 827.733c169.66 0 307.2-137.54 307.2-307.2 0-169.662-137.54-307.2-307.2-307.2-169.662 0-307.2 137.538-307.2 307.2 0 169.66 137.538 307.2 307.2 307.2z"],"attrs":[{"fill":"rgb(158, 162, 168)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-invisible"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":5}]}},"attrs":[{"fill":"rgb(158, 162, 168)"}],"properties":{"order":641,"id":22,"name":"status-invisible","prevSize":32,"code":59811},"setIdx":0,"setId":2,"iconIdx":159},{"icon":{"paths":["M825.135 560.657l-84.617-11.038c1.237-9.481 1.882-19.187 1.882-29.086s-0.644-19.605-1.882-29.086l84.617-11.038c1.715 13.133 2.598 26.526 2.598 40.124s-0.883 26.991-2.598 40.124zM804.425 402.941l-78.827 32.686c-7.458-17.989-17.242-34.822-28.996-50.116l67.665-51.996c16.243 21.139 29.798 44.451 40.158 69.426zM707.55 276.801l-51.994 67.663c-15.296-11.753-32.128-21.534-50.116-28.995l32.687-78.825c24.973 10.356 48.286 23.913 69.423 40.157zM560.657 215.93l-11.038 84.617c-9.481-1.237-19.187-1.88-29.086-1.88s-19.605 0.643-29.086 1.88l-11.038-84.617c13.133-1.713 26.526-2.597 40.124-2.597s26.991 0.884 40.124 2.597zM402.941 236.644l32.686 78.825c-17.989 7.461-34.822 17.242-50.116 28.995l-51.996-67.663c21.139-16.244 44.451-29.801 69.426-40.157zM276.801 333.515l67.663 51.996c-11.753 15.294-21.534 32.126-28.995 50.116l-78.825-32.686c10.356-24.975 23.913-48.287 40.157-69.426zM215.93 480.41c-1.713 13.133-2.597 26.526-2.597 40.124s0.884 26.991 2.597 40.124l84.617-11.038c-1.237-9.481-1.88-19.187-1.88-29.086s0.643-19.605 1.88-29.086l-84.617-11.038zM236.644 638.127l78.825-32.687c7.461 17.988 17.242 34.82 28.995 50.116l-67.663 51.994c-16.244-21.137-29.801-44.45-40.157-69.423zM333.515 764.267l51.996-67.665c15.294 11.755 32.126 21.538 50.116 28.996l-32.686 78.827c-24.975-10.359-48.287-23.915-69.426-40.158zM480.41 825.135l11.038-84.617c9.481 1.237 19.187 1.882 29.086 1.882s19.605-0.644 29.086-1.882l11.038 84.617c-13.133 1.715-26.526 2.598-40.124 2.598s-26.991-0.883-40.124-2.598zM638.127 804.425l-32.687-78.827c17.988-7.458 34.82-17.242 50.116-28.996l51.994 67.665c-21.137 16.243-44.45 29.798-69.423 40.158zM764.267 707.55l-67.665-51.994c11.755-15.296 21.538-32.128 28.996-50.116l78.827 32.687c-10.359 24.973-23.915 48.286-40.158 69.423z"],"attrs":[{"fill":"rgb(158, 162, 168)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-loading"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":5}]}},"attrs":[{"fill":"rgb(158, 162, 168)"}],"properties":{"order":642,"id":21,"name":"status-loading","prevSize":32,"code":59812},"setIdx":0,"setId":2,"iconIdx":160},{"icon":{"paths":["M819.2 512c0 169.662-137.538 307.2-307.2 307.2s-307.2-137.538-307.2-307.2c0-169.662 137.538-307.2 307.2-307.2s307.2 137.538 307.2 307.2z"],"attrs":[{"fill":"rgb(45, 224, 165)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["status-online"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":3}]}},"attrs":[{"fill":"rgb(45, 224, 165)"}],"properties":{"order":643,"id":20,"name":"status-online","prevSize":32,"code":59813},"setIdx":0,"setId":2,"iconIdx":161},{"icon":{"paths":["M536.845 548.215h-255.502c-20.173 0-36.527-16.354-36.527-36.527s16.354-36.527 36.527-36.527h102.99c-21.546-22.75-32.32-50.118-32.32-82.102 0-39.587 15.766-72.27 47.297-98.048 31.764-26.008 72.959-39.012 123.596-39.012 34.526 0 65.25 6.675 92.177 20.024 27.162 13.349 48.107 31.762 62.835 55.238 8.849 13.888 15.083 28.54 18.697 43.958 4.198 17.914-11.17 33.030-29.572 33.030-18.398 0-32.499-15.279-37.828-32.89-4.617-15.263-12.779-28.121-24.486-38.574-19.332-17.492-46.609-26.238-81.822-26.238-32.683 0-58.231 7.25-76.642 21.75-18.185 14.27-27.275 34.179-27.275 59.726 0 20.484 8.629 37.86 25.892 52.132 13.764 11.046 35.012 21.385 63.744 31.006h247.556c20.173 0 36.527 16.354 36.527 36.527s-16.354 36.527-36.527 36.527h-90.027c7.138 7.215 13.133 14.878 17.975 22.989 11.511 18.871 17.263 41.079 17.263 66.628 0 40.738-15.881 73.421-47.642 98.048-31.761 24.397-74.227 36.595-127.394 36.595-34.522 0-66.743-6.558-96.666-19.678-29.921-13.35-53.052-31.531-69.393-54.549-9.777-13.965-16.588-29.077-20.432-45.338-4.233-17.903 11.182-33.028 29.581-33.028s32.466 15.364 38.446 32.764c5.318 15.475 14.771 28.604 28.356 39.39 22.558 17.493 52.591 26.236 90.108 26.236 34.987 0 61.798-7.134 80.439-21.402 18.645-14.272 27.968-33.719 27.968-58.347s-8.631-43.614-25.894-56.964c-14.332-11.273-38.34-22.387-72.026-33.344z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["strike"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":644,"id":19,"name":"strike","prevSize":32,"code":59814},"setIdx":0,"setId":2,"iconIdx":162},{"icon":{"paths":["M480 117.333c0-17.673 14.327-32 32-32s32 14.327 32 32v106.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-106.667zM770.039 214.629c12.497-12.497 32.759-12.497 45.257 0s12.497 32.758 0 45.255l-75.413 75.412c-12.497 12.497-32.759 12.497-45.257 0s-12.497-32.758 0-45.255l75.413-75.412zM335.377 694.63c-12.497-12.497-32.758-12.497-45.255 0l-75.495 75.494c-12.497 12.497-12.497 32.755 0 45.252s32.758 12.497 45.255 0l75.495-75.494c12.497-12.497 12.497-32.755 0-45.252zM480 800c0-17.673 14.327-32 32-32s32 14.327 32 32v106.667c0 17.673-14.327 32-32 32s-32-14.327-32-32v-106.667zM212 211.999c-12.497 12.497-12.497 32.758 0 45.255l75.349 75.349c12.497 12.497 32.758 12.497 45.255 0s12.497-32.758 0-45.255l-75.349-75.349c-12.497-12.497-32.758-12.497-45.255 0zM694.626 739.883c-12.497-12.497-12.497-32.759 0-45.257s32.759-12.497 45.257 0l75.328 75.328c12.497 12.497 12.497 32.759 0 45.257s-32.759 12.497-45.257 0l-75.328-75.328zM86.4 512c0 17.673 14.327 32 32 32h106.667c17.673 0 32-14.327 32-32s-14.327-32-32-32h-106.667c-17.673 0-32 14.327-32 32zM800 544c-17.673 0-32-14.327-32-32s14.327-32 32-32h106.667c17.673 0 32 14.327 32 32s-14.327 32-32 32h-106.667zM666.816 512c0-85.505-69.316-154.82-154.82-154.82-85.503 0-154.817 69.315-154.817 154.82 0 85.504 69.315 154.816 154.817 154.816 85.504 0 154.82-69.312 154.82-154.816zM725.329 512c0 117.82-95.509 213.333-213.333 213.333-117.819 0-213.332-95.514-213.332-213.333 0-117.822 95.512-213.335 213.332-213.335 117.824 0 213.333 95.513 213.333 213.335z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sun"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":645,"id":18,"name":"sun","prevSize":32,"code":59815},"setIdx":0,"setId":2,"iconIdx":163},{"icon":{"paths":["M930.253 512c0 229.751-186.249 416-416 416-229.749 0-415.999-186.249-415.999-416s186.249-416 415.999-416c229.751 0 416 186.249 416 416zM570.039 859.605l-41.83-156.105c-4.608 0.333-9.263 0.499-13.956 0.499-4.57 0-9.101-0.158-13.594-0.474l-41.835 156.134c18.057 2.854 36.57 4.339 55.428 4.339 18.982 0 37.615-1.502 55.787-4.395zM438.443 688.452c-47.551-20.459-85.181-59.571-103.675-108.126l-155.028 41.54c33.967 103.484 114.617 185.805 217.044 222.054l41.659-155.469zM162.254 512c0 16.090 1.079 31.927 3.17 47.445l156.904-42.044c-0.049-1.796-0.074-3.597-0.074-5.402 0-4.911 0.184-9.779 0.546-14.596l-156.051-41.813c-2.958 18.368-4.495 37.21-4.495 56.41zM395.858 180.407c-99.159 35.408-177.785 114.033-213.195 213.191l155.535 41.675c19.356-44.352 54.982-79.977 99.336-99.331l-41.676-155.535zM514.253 160c-19.196 0-38.037 1.537-56.401 4.495l41.813 156.051c4.817-0.361 9.681-0.546 14.588-0.546 5.030 0 10.018 0.194 14.95 0.573l41.805-156.022c-18.475-2.995-37.431-4.552-56.755-4.552zM632.064 843.802c102.656-36.45 183.39-119.194 217.092-223.121l-154.974-41.523c-18.291 48.981-56.009 88.491-103.787 109.15l41.668 155.494zM863.249 558.199c1.984-15.121 3.004-30.541 3.004-46.199 0-18.769-1.468-37.197-4.297-55.172l-156.156 41.843c0.299 4.403 0.452 8.849 0.452 13.329 0 1.378-0.013 2.752-0.043 4.122l157.039 42.078zM846.263 394.775c-35.166-99.601-113.886-178.641-213.278-214.248l-41.681 155.559c44.587 19.556 80.311 55.564 99.507 100.343l155.452-41.653zM642.253 512c0-70.694-57.306-128-128-128-70.69 0-127.999 57.306-127.999 128s57.309 128 127.999 128c70.694 0 128-57.306 128-128z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["support"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":646,"id":17,"name":"support","prevSize":32,"code":59816},"setIdx":0,"setId":2,"iconIdx":164},{"icon":{"paths":["M368 412.435c-53.592 0-96-42.819-96-94.367s42.408-94.366 96-94.366c53.592 0 96 42.819 96 94.366s-42.408 94.367-96 94.367zM368 476.352c88.367 0 160-70.867 160-158.284s-71.633-158.282-160-158.282c-88.366 0-160 70.865-160 158.282s71.634 158.284 160 158.284zM713.6 396.606c-35.921 0-64-28.646-64-62.71s28.079-62.71 64-62.71c35.921 0 64 28.646 64 62.71s-28.079 62.71-64 62.71zM713.6 460.523c70.69 0 128-56.693 128-126.626s-57.31-126.626-128-126.626c-70.694 0-128 56.692-128 126.626s57.306 126.626 128 126.626zM197.459 526.571c27.344-8.695 56.671-9.229 84.32-1.536l48.489 13.491c24.207 6.733 49.884 6.268 73.824-1.348l30.101-9.57c29.474-9.37 61.086-9.946 90.889-1.655 67.964 18.91 114.918 80.226 114.918 150.071v91.866c0 52.45-42.982 94.967-96 94.967h-352c-53.019 0-96-42.517-96-94.967v-103.633c0-62.822 40.999-118.46 101.459-137.685zM264.451 585.98c-15.545-4.322-32.034-4.023-47.407 0.866-33.993 10.807-57.044 42.091-57.044 77.41v103.633c0 17.485 14.327 31.654 32 31.654h352c17.673 0 32-14.17 32-31.654v-91.866c0-41.476-27.887-77.892-68.245-89.122-17.702-4.924-36.476-4.582-53.978 0.981l-30.1 9.57c-35.91 11.422-74.426 12.122-110.737 2.018l-48.489-13.491zM691.2 777.685h140.8c53.018 0 96-42.923 96-95.872v-30.571c0-56.802-38.613-106.351-93.747-120.294-21.397-5.414-43.853-5.035-65.058 1.092l-16.397 4.745c-21.845 6.31-44.975 6.703-67.017 1.126l-29.807-7.539c-19.908-5.035-40.798-4.685-60.523 1.015-1.062 0.311-2.121 0.631-3.174 0.969 10.714 4.045 20.898 9.527 30.255 16.354l2.428 1.771 15.040 13.193c9.937 8.713 18.142 19.221 24.183 30.972l2.206 4.292 3.678 0.934c33.062 8.363 67.759 7.778 100.523-1.694l16.401-4.74c10.283-2.974 21.171-3.157 31.548-0.529 26.735 6.763 45.461 30.788 45.461 58.334v30.571c0 17.651-14.327 31.957-32 31.957h-140.8v63.915z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["team"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":647,"id":16,"name":"team","prevSize":32,"code":59817},"setIdx":0,"setId":2,"iconIdx":165},{"icon":{"paths":["M223.311 308.609c-47.831 66.699-64.077 147.668-64.077 201.185v1.404l-0.127 1.399c-9.348 103.236 27.547 175.996 86.848 226.377 60.643 51.516 146.604 80.998 235.291 90.893 88.61 9.886 186.223 1.399 263.339-14.976 38.562-8.188 70.929-18.121 93.888-28.126 8.909-3.883 15.838-7.535 20.945-10.765-2.377-1.515-5.248-3.196-8.678-5.030-8.883-4.749-19.055-9.263-29.611-13.897l-1.843-0.806c-9.314-4.083-19.725-8.649-27.61-13.077-14.293-8.030-28.318-17.156-38.545-26.867-5.035-4.783-10.761-11.196-14.596-19.183-4.058-8.457-6.959-21.082-1.259-34.436 30.814-72.188 45.457-111.727 52.548-137.835 6.622-24.38 6.622-36.655 6.622-56.171v-0.179c0-15.514-8.977-86.595-53.777-152.876-43.371-64.175-121.395-125.729-264.828-125.729-129.727 0-207.831 53.576-254.531 118.696zM173.267 272.433c58.132-81.062 154.749-144.433 304.574-144.433 164.894 0 261.594 72.589 315.857 152.878 52.838 78.182 64.414 161.879 64.414 187.641 0 21.658-0.017 40.337-8.794 72.64-7.834 28.834-22.626 68.617-50.048 133.423 5.069 4.036 12.591 9.003 22.507 14.571 5.12 2.876 12.992 6.34 24.055 11.191 10.261 4.51 22.579 9.933 33.89 15.979 10.846 5.798 23.526 13.572 32.951 23.74 9.86 10.633 20.211 28.868 12.817 51.136-4.873 14.673-16.183 25.092-25.609 32.017-10.24 7.522-22.946 14.281-36.86 20.343-27.947 12.177-64.585 23.181-105.681 31.906-82.197 17.451-186.522 26.688-282.906 15.936-96.311-10.743-195.347-43.174-268.314-105.165-74.031-62.891-119.295-154.778-108.551-277.854 0.276-63.411 19.118-157.053 75.696-235.949zM333.953 422.052c0-17.648 14.25-31.955 31.828-31.955h183.011c17.574 0 31.825 14.307 31.825 31.955s-14.251 31.955-31.825 31.955h-183.011c-17.578 0-31.828-14.306-31.828-31.955zM365.781 539.708c-17.578 0-31.828 14.306-31.828 31.957 0 17.647 14.25 31.953 31.828 31.953h224.12c17.579 0 31.825-14.306 31.825-31.953 0-17.651-14.246-31.957-31.825-31.957h-224.12z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["threads"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":648,"id":15,"name":"threads","prevSize":32,"code":59818},"setIdx":0,"setId":2,"iconIdx":166},{"icon":{"paths":["M392.557 853.333c317.455 0 491.091-262.665 491.091-490.443 0-7.46 0-14.887-0.503-22.281 33.779-24.401 62.938-54.613 86.11-89.223-31.501 13.94-64.917 23.081-99.136 27.12 36.032-21.542 62.997-55.424 75.883-95.34-33.882 20.078-70.946 34.228-109.598 41.839-53.5-56.814-138.513-70.72-207.369-33.919-68.851 36.8-104.427 115.155-86.767 191.127-138.778-6.948-268.076-72.41-355.717-180.093-45.81 78.76-22.411 179.517 53.437 230.097-27.467-0.811-54.336-8.211-78.337-21.573 0 0.706 0 1.445 0 2.185 0.022 82.053 57.937 152.722 138.47 168.969-25.41 6.921-52.071 7.932-77.934 2.957 22.611 70.217 87.409 118.319 161.251 119.706-61.117 47.97-136.617 74.010-214.351 73.933-13.732-0.026-27.452-0.858-41.087-2.487 78.931 50.586 170.772 77.419 264.557 77.295z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["twitter-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":649,"id":14,"name":"twitter-monochromatic","prevSize":32,"code":59819},"setIdx":0,"setId":2,"iconIdx":167},{"icon":{"paths":["M388.508 320c0-17.673-14.327-32-32-32s-32 14.327-32 32v256c0 106.039 85.961 192 192.002 192 106.035 0 192-85.961 192-192v-256c0-17.673-14.327-32-32-32-17.677 0-32 14.327-32 32v256c0 70.694-57.31 128-128 128-70.694 0-128.002-57.306-128.002-128v-256zM356.508 848c-17.673 0-32 14.327-32 32s14.327 32 32 32h320.002c17.673 0 32-14.327 32-32s-14.327-32-32-32h-320.002z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["underline"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":650,"id":13,"name":"underline","prevSize":32,"code":59820},"setIdx":0,"setId":2,"iconIdx":168},{"icon":{"paths":["M512 832c176.73 0 320-143.27 320-320 0-176.731-143.27-320-320-320-111.713 0-210.056 57.245-267.295 144h107.295c17.673 0 32 14.327 32 32s-14.327 32-32 32h-176c-17.673 0-32-14.327-32-32v-192c0-17.673 14.327-32 32-32s32 14.327 32 32v101.364c70.228-90.856 180.282-149.364 304-149.364 212.079 0 384 171.923 384 384 0 212.079-171.921 384-384 384-212.077 0-384-171.921-384-384h64c0 176.73 143.269 320 320 320z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["undo"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":651,"id":12,"name":"undo","prevSize":32,"code":59821},"setIdx":0,"setId":2,"iconIdx":169},{"icon":{"paths":["M825.374 153.173c12.497-12.48 32.755-12.48 45.252 0s12.497 32.715 0 45.195l-671.999 671.115c-12.497 12.48-32.758 12.48-45.255 0s-12.497-32.717 0-45.197l672.002-671.113zM107.307 479.424c101.801-153.5 303.397-327.248 541.397-225.142l-49.51 49.446c-56.452-19.184-107.934-19.478-153.323-9.713-114.755 24.688-215.37 118.236-280.422 213.585 38.582 47.94 75.869 86.711 111.715 117.73l-45.303 45.244c-39.632-34.692-80.083-77.333-121.157-129.007-14.229-17.903-16.035-43.089-3.398-62.144zM794.227 349.994l-45.269 45.21c35.494 31.386 72.239 70.857 110.037 119.924-61.461 95.061-158.443 188.433-271.761 213.342-46.694 10.266-100.493 9.719-160.384-11.588l-49.266 49.203c244.342 107.191 442.802-69.069 540.065-223.957 11.657-18.564 9.882-42.598-3.392-60.049-40.439-53.163-80.533-96.801-120.030-132.084zM511.599 346.974c13.692 0 26.999 1.57 39.753 4.534l-203.471 203.201c-3.895-13.82-5.974-28.365-5.974-43.379 0-90.772 75.975-164.356 169.693-164.356zM675.494 468.57l-202.995 202.726c12.553 2.867 25.643 4.386 39.1 4.386 93.722 0 169.694-73.583 169.694-164.352 0-14.793-2.018-29.129-5.798-42.761z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["unread-on-top-disabled"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":652,"id":11,"name":"unread-on-top-disabled","prevSize":32,"code":59822},"setIdx":0,"setId":2,"iconIdx":170},{"icon":{"paths":["M587.234 728.474c-107.23 23.573-251.957-9.865-421.785-220.868 65.051-95.352 165.667-188.9 280.422-213.588 108.169-23.272 250.953 10.59 413.124 221.111-61.461 95.061-158.443 188.433-271.761 213.346zM914.257 482.082c-347.132-456.361-668.857-210.879-806.95-2.658-12.638 19.055-10.832 44.241 3.398 62.144 362.307 455.817 676.142 208.862 806.945 0.563 11.657-18.569 9.882-42.598-3.392-60.049zM617.293 511.33c0-53.641-45.457-100.439-105.694-100.439s-105.693 46.799-105.693 100.439c0 53.636 45.456 100.437 105.693 100.437s105.694-46.801 105.694-100.437zM681.293 511.33c0 90.769-75.972 164.352-169.694 164.352-93.718 0-169.693-73.583-169.693-164.352 0-90.771 75.975-164.355 169.693-164.355 93.722 0 169.694 73.584 169.694 164.355z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["unread-on-top"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":653,"id":10,"name":"unread-on-top","prevSize":32,"code":59823},"setIdx":0,"setId":2,"iconIdx":171},{"icon":{"paths":["M273.128 356.679c-6.619 27.765-2.384 63.755 25.498 105.58l33.166 49.749h-59.792c-44.011 0-70.483 14.874-86.278 33.621-16.496 19.575-24.573 47.262-23.734 77.491 0.841 30.259 10.601 59.968 25.818 81.31 15.127 21.218 33.403 31.578 52.194 31.578h144v64h-144c-45.209 0-80.933-25.638-104.305-58.423-23.283-32.657-36.523-74.948-37.682-116.689-1.161-41.771 9.763-86.084 38.767-120.508 20.726-24.597 49.155-42.317 84.825-50.782-16.1-39.010-19.009-77.050-10.731-111.77 11.253-47.2 42.304-84.492 80.791-107.342 38.399-22.798 85.676-32.138 131.301-21.965 35.587 7.935 68.909 27.48 95.252 59.554 53.751-35.147 127.582-30.892 182.485-2.495 34.436 17.812 64.794 46.382 81.438 85.009 12.292 28.531 16.435 61.011 10.603 96.206 46.114 6.682 81.51 25.156 105.617 53.453 30.349 35.627 38.106 81.353 33.446 123.285-4.659 41.92-21.948 83.486-46.545 115.115-24.060 30.933-59.354 57.353-101.261 57.353h-144v-64h144c14.093 0 32.802-9.579 50.739-32.644 17.404-22.374 30.114-52.804 33.455-82.889 3.341-30.067-2.901-56.341-18.556-74.718-15.219-17.869-44.032-33.749-97.638-33.749h-45.687l15.616-42.935c13.547-37.252 11.089-66.743 1.434-89.149-9.856-22.872-28.501-41.302-52.062-53.489-49.587-25.649-107.477-18.624-134.716 14.063l-30.135 36.162-22.541-41.324c-19.759-36.22-47.232-54.176-74.872-60.34-28.375-6.327-59.098-0.669-84.699 14.531-25.513 15.148-44.462 38.854-51.209 67.153zM630.98 588.779l-96-99.051c-6.029-6.217-14.319-9.728-22.98-9.728s-16.951 3.511-22.98 9.728l-95.998 99.051c-12.3 12.689-11.983 32.947 0.707 45.248s32.951 11.985 45.251-0.708l41.020-42.321v273.003c0 17.673 14.327 32 32 32s32-14.327 32-32v-273.003l41.020 42.321c12.301 12.693 32.559 13.009 45.252 0.708 12.689-12.301 13.005-32.559 0.708-45.248z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["upload"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":654,"id":9,"name":"upload","prevSize":32,"code":59824},"setIdx":0,"setId":2,"iconIdx":172},{"icon":{"paths":["M553.412 325.606c0 91.58-75.849 165.821-169.412 165.821s-169.412-74.241-169.412-165.821c0-91.579 75.848-165.819 169.412-165.819s169.412 74.24 169.412 165.819zM485.649 325.606c0-54.947-45.513-99.491-101.649-99.491-56.138 0-101.647 44.544-101.647 99.491s45.509 99.492 101.647 99.492c56.137 0 101.649-44.544 101.649-99.492z","M203.427 510.558c28.952-9.109 60.004-9.668 89.279-1.613l51.342 14.135c25.632 7.057 52.819 6.566 78.167-1.408l31.873-10.027c31.206-9.822 64.678-10.423 96.23-1.737 71.962 19.81 121.681 84.049 121.681 157.218v96.239c0 54.946-45.508 99.49-101.649 99.49h-372.704c-56.138 0-101.647-44.544-101.647-99.49v-108.565c0-65.818 43.411-124.105 107.427-144.243zM274.359 572.796c-16.46-4.531-33.918-4.215-50.196 0.905-35.992 11.324-60.399 44.096-60.399 81.101v108.565c0 18.317 15.169 33.165 33.882 33.165h372.704c18.714 0 33.886-14.848 33.886-33.165v-96.239c0-43.452-29.53-81.604-72.265-93.367-18.739-5.158-38.618-4.8-57.148 1.028l-31.872 10.031c-38.024 11.959-78.804 12.698-117.251 2.112l-51.342-14.135z","M797.090 188.646c0-15.913-13.022-28.812-29.090-28.812s-29.090 12.9-29.090 28.812v100.844h-101.82c-16.064 0-29.090 12.9-29.090 28.812s13.026 28.813 29.090 28.813h101.82v100.842c0 15.915 13.022 28.813 29.090 28.813s29.090-12.898 29.090-28.813v-100.842h101.82c16.064 0 29.090-12.9 29.090-28.813s-13.026-28.812-29.090-28.812h-101.82v-100.844z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-add"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":655,"id":8,"name":"user-add","prevSize":32,"code":59825},"setIdx":0,"setId":2,"iconIdx":173},{"icon":{"paths":["M464 318.068c0-51.548-42.408-94.366-96-94.366s-96 42.819-96 94.366c0 51.548 42.408 94.367 96 94.367s96-42.819 96-94.367zM528 318.068c0 87.417-71.633 158.284-160 158.284-88.366 0-160-70.867-160-158.284s71.634-158.282 160-158.282c88.367 0 160 70.865 160 158.282zM281.778 525.035c-27.649-7.693-56.976-7.159-84.32 1.536-60.46 19.226-101.459 74.863-101.459 137.685v103.633c0 52.45 42.981 94.967 96 94.967h352c3.605 0 7.164-0.196 10.667-0.576v-64.538c-3.337 1.169-6.925 1.801-10.667 1.801h-352c-17.673 0-32-14.17-32-31.654v-103.633c0-35.319 23.051-66.603 57.043-77.41 15.374-4.89 31.862-5.188 47.407-0.866l48.489 13.491c36.311 10.103 74.826 9.404 110.737-2.018l30.1-9.57c17.502-5.564 36.275-5.905 53.978-0.981 18.901 5.257 35.063 16.038 46.912 30.281v-79.714c-9.31-4.749-19.204-8.627-29.585-11.516-29.803-8.29-61.414-7.714-90.889 1.655l-30.101 9.57c-23.941 7.616-49.617 8.081-73.825 1.348l-48.489-13.491zM763.366 489.033c-12.326-12.651-32.585-12.919-45.248-0.61s-12.937 32.542-0.61 45.193l102.839 105.506h-324.348c-17.673 0-32 14.31-32 31.962 0 17.647 14.327 31.957 32 31.957h324.348l-102.839 105.51c-12.326 12.646-12.053 32.879 0.61 45.188 12.663 12.314 32.922 12.041 45.248-0.606l155.721-159.761c12.092-12.407 12.092-32.175 0-44.582l-155.721-159.757z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user-forward"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":656,"id":7,"name":"user-forward","prevSize":32,"code":59826},"setIdx":0,"setId":2,"iconIdx":174},{"icon":{"paths":["M608 336c0-53.019-42.982-96-96-96s-96 42.981-96 96c0 53.019 42.982 96 96 96s96-42.981 96-96zM672 336c0 88.366-71.633 160-160 160-88.366 0-160-71.634-160-160s71.634-160 160-160c88.367 0 160 71.634 160 160zM412.451 551.799c-24.621-5.769-50.286-5.363-74.713 1.178-67.087 17.971-113.738 78.763-113.738 148.215v66.807c0 53.018 42.981 96 96 96h384c53.018 0 96-42.982 96-96v-58.53c0-74.3-51.149-138.825-123.49-155.78l-6.455-1.51c-25.673-6.020-52.437-5.598-77.909 1.225l-49.626 13.295c-20.378 5.457-41.788 5.794-62.327 0.981l-67.741-15.881zM354.297 614.797c14.238-3.81 29.199-4.049 43.55-0.683l67.744 15.876c30.805 7.219 62.921 6.716 93.487-1.472l49.626-13.295c15.283-4.092 31.343-4.343 46.746-0.734l6.455 1.51c43.405 10.176 74.095 48.887 74.095 93.47v58.53c0 17.673-14.327 32-32 32h-384c-17.673 0-32-14.327-32-32v-66.807c0-40.486 27.193-75.921 66.297-86.396z"],"attrs":[{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["user"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"}],"properties":{"order":657,"id":6,"name":"user","prevSize":32,"code":59827},"setIdx":0,"setId":2,"iconIdx":175},{"icon":{"paths":["M206.090 672c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M334.090 576c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 480c35.346 0 64-28.655 64-64 0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.345 28.654 64 64 64z","M334.090 384c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 288c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M334.090 192c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M206.090 864c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M334.090 768c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-condensed"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1},{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":658,"id":5,"name":"view-condensed","prevSize":32,"code":59828},"setIdx":0,"setId":2,"iconIdx":176},{"icon":{"paths":["M288 192c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 288c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 304c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M288 464c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 560c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 576c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M288 736c0-17.673 14.327-32 32-32h544c17.673 0 32 14.327 32 32s-14.327 32-32 32h-544c-17.673 0-32-14.327-32-32zM288 832c0-17.673 14.327-32 32-32h448c17.673 0 32 14.327 32 32s-14.327 32-32 32h-448c-17.673 0-32-14.327-32-32zM192 848c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-extended"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":659,"id":4,"name":"view-extended","prevSize":32,"code":59829},"setIdx":0,"setId":2,"iconIdx":177},{"icon":{"paths":["M194.254 320c35.346 0 64-28.654 64-64s-28.654-64-64-64c-35.346 0-64 28.654-64 64s28.654 64 64 64z","M322.254 224c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M194.254 576c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M322.254 480c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z","M194.254 832c35.346 0 64-28.655 64-64s-28.654-64-64-64c-35.346 0-64 28.655-64 64s28.654 64 64 64z","M322.254 736c-17.673 0-32 14.327-32 32s14.327 32 32 32h543.999c17.673 0 32-14.327 32-32s-14.327-32-32-32h-543.999z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["view-medium"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":660,"id":3,"name":"view-medium","prevSize":32,"code":59830},"setIdx":0,"setId":2,"iconIdx":178},{"icon":{"paths":["M512 352.004c17.673 0 32 14.327 32 32v224c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224c0-17.673 14.327-32 32-32z","M512 672.004c17.673 0 32 14.327 32 32s-14.327 32-32 32c-17.673 0-32-14.327-32-32s14.327-32 32-32z","M567.1 158.348c-24.772-41.922-85.427-41.922-110.199 0l-359.921 609.098c-25.21 42.662 5.544 96.559 55.099 96.559h719.845c49.553 0 80.307-53.897 55.095-96.559l-359.919-609.098zM512 190.906l359.923 609.098h-719.845l359.922-609.098z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["warning"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":661,"id":2,"name":"warning","prevSize":32,"code":59831},"setIdx":0,"setId":2,"iconIdx":179},{"icon":{"paths":["M367.701 399.123l14.031 0.627-0.541-16.725-152.878 1.576c49.916-116.49 165.641-198.132 300.366-198.132 99.644 0 188.894 44.66 248.811 115.065-3.712 0.171-7.492 0.492-11.524 1.068 0 0-63.765 14.898-13.308 118.709 0 0 31.087 84.541-19.302 198.773l-28.407 73.306-99.098-264.725c0 0-5.982-28.15 23.607-28.15l23.761-1.475 0.341-16.034h-228.368v14.456c0 0 39.828-0.288 56.153 27.151l40.759 103.78-73.015 174.4-108.608-272.397c0 0-3.39-31.781 27.22-31.273z","M855.3 513.169c0-33.865-5.235-66.645-14.729-97.356l-145.946 378.756c96.132-56.883 160.674-161.574 160.674-281.399z","M425.173 823.010c32.491 10.914 67.268 16.777 103.492 16.777 37.845 0 74.099-6.404 107.895-18.253l-99.712-258.505-111.676 259.981z","M201.975 513.148c0-32.081 4.712-63.066 13.339-92.335l1 2.051 152.657 375.229c-99.607-55.881-166.995-162.539-166.995-284.945z","M144.401 512.094c0-211.776 172.317-384.094 384.077-384.094 211.725 0 383.923 172.318 383.923 384.094 0 211.772-172.203 384.009-383.923 384.009-211.759 0-384.077-172.233-384.077-384.009zM528.478 879.253c202.385 0 367.108-164.74 367.108-367.177 0-202.487-164.723-367.145-367.108-367.161-202.49 0-367.214 164.673-367.214 367.161 0 202.436 164.724 367.177 367.214 367.177z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"width":1067,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["wordpress-monochromatic"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":662,"id":1,"name":"wordpress-monochromatic","prevSize":32,"code":59832},"setIdx":0,"setId":2,"iconIdx":180},{"icon":{"paths":["M384 149.138c0-11.766 9.551-21.305 21.333-21.305h85.333c11.78 0 21.333 9.539 21.333 21.305v106.526h-106.667c-11.782 0-21.333-9.539-21.333-21.305v-85.221z","M512 255.665h106.667c11.78 0 21.333 9.539 21.333 21.305v85.221c0 11.767-9.553 21.305-21.333 21.305h-106.667v-127.831z","M384 404.801c0-11.767 9.551-21.305 21.333-21.305h106.667v127.83h-106.667c-11.782 0-21.333-9.536-21.333-21.303v-85.222z","M512 511.326h106.667c11.78 0 21.333 9.54 21.333 21.308v106.526h-128v-127.834z","M426.667 639.159c-23.564 0-42.667 19.076-42.667 42.607v170.445c0 23.531 19.103 42.611 42.667 42.611h170.667c23.565 0 42.667-19.081 42.667-42.611v-213.052h-213.333zM490.667 724.378c-11.78 0-21.333 9.54-21.333 21.308v42.607c0 11.767 9.553 21.308 21.333 21.308h42.667c11.78 0 21.333-9.54 21.333-21.308v-42.607c0-11.767-9.553-21.308-21.333-21.308h-42.667z"],"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["zip"],"colorPermutations":{"108114122115816216812282312341243190812456992125519219214522416519902101":[{"f":1},{"f":1},{"f":1},{"f":1},{"f":1}]}},"attrs":[{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"},{"fill":"rgb(108, 114, 122)"}],"properties":{"order":663,"id":0,"name":"zip","prevSize":32,"code":59833},"setIdx":0,"setId":2,"iconIdx":181}],"height":1024,"metadata":{"name":"custom"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"custom","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"noie8":true,"ie7":false,"autoHost":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16,"quickUsageToken":{"RC":"OGIzMzFiYmJjOThlMGY0OTQwMDc5MzgzOWRkY2MxZGQjMSMxNTk3MDkyNDU2IyMj"}}}
\ No newline at end of file
diff --git a/app/lib/userPreferences.js b/app/lib/userPreferences.js
new file mode 100644
index 000000000..633a5198d
--- /dev/null
+++ b/app/lib/userPreferences.js
@@ -0,0 +1,80 @@
+import MMKVStorage from 'react-native-mmkv-storage';
+
+import log from '../utils/log';
+
+const MMKV = new MMKVStorage.Loader()
+ // MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS)
+ .setProcessingMode(MMKVStorage.MODES.MULTI_PROCESS)
+ .withEncryption()
+ .initialize();
+
+class UserPreferences {
+ constructor() {
+ this.mmkv = MMKV;
+
+ this.encryptMigratedData();
+ }
+
+ // It should run only once
+ async encryptMigratedData() {
+ try {
+ const encryptMigration = await this.getBoolAsync('encryptMigration');
+
+ if (!encryptMigration) {
+ // Encrypt the migrated data
+ await this.mmkv.encryption.encrypt();
+
+ // Mark as completed
+ await this.setBoolAsync('encryptMigration', true);
+ }
+ } catch (e) {
+ log(e);
+ }
+ }
+
+ async getStringAsync(key) {
+ try {
+ const value = await this.mmkv.getStringAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setStringAsync(key, value) {
+ return this.mmkv.setStringAsync(key, value);
+ }
+
+ async getBoolAsync(key) {
+ try {
+ const value = await this.mmkv.getBoolAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setBoolAsync(key, value) {
+ return this.mmkv.setBoolAsync(key, value);
+ }
+
+ async getMapAsync(key) {
+ try {
+ const value = await this.mmkv.getMapAsync(key);
+ return value;
+ } catch {
+ return null;
+ }
+ }
+
+ setMapAsync(key, value) {
+ return this.mmkv.setMapAsync(key, value);
+ }
+
+ removeItem(key) {
+ return this.mmkv.removeItem(key);
+ }
+}
+
+const userPreferences = new UserPreferences();
+export default userPreferences;
diff --git a/app/notifications/push/index.js b/app/notifications/push/index.js
index 40ccbea16..df4ac152d 100644
--- a/app/notifications/push/index.js
+++ b/app/notifications/push/index.js
@@ -1,8 +1,8 @@
import EJSON from 'ejson';
-
import PushNotification from './push';
import store from '../../lib/createStore';
import { deepLinkingOpen } from '../../actions/deepLinking';
+import { isFDroidBuild } from '../../constants/environment';
export const onNotification = (notification) => {
if (notification) {
@@ -38,8 +38,10 @@ export const onNotification = (notification) => {
export const getDeviceToken = () => PushNotification.getDeviceToken();
export const setBadgeCount = count => PushNotification.setBadgeCount(count);
export const initializePushNotifications = () => {
- setBadgeCount();
- return PushNotification.configure({
- onNotification
- });
+ if (!isFDroidBuild) {
+ setBadgeCount();
+ return PushNotification.configure({
+ onNotification
+ });
+ }
};
diff --git a/app/presentation/RoomItem/LastMessage.js b/app/presentation/RoomItem/LastMessage.js
index 54ee09f11..f89881644 100644
--- a/app/presentation/RoomItem/LastMessage.js
+++ b/app/presentation/RoomItem/LastMessage.js
@@ -6,6 +6,7 @@ import I18n from '../../i18n';
import styles from './styles';
import Markdown from '../../containers/markdown';
import { themes } from '../../constants/colors';
+import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
const formatMsg = ({
lastMessage, type, showLastMessage, username, useRealName
@@ -29,6 +30,11 @@ const formatMsg = ({
return I18n.t('User_sent_an_attachment', { user });
}
+ // Encrypted message pending decrypt
+ if (lastMessage.t === E2E_MESSAGE_TYPE && lastMessage.e2e !== E2E_STATUS.DONE) {
+ lastMessage.msg = I18n.t('Encrypted_message');
+ }
+
if (isLastMessageSentByMe) {
prefix = I18n.t('You_colon');
} else if (type !== 'd') {
diff --git a/app/reducers/crashReport.js b/app/reducers/crashReport.js
index e59848b54..8465897e3 100644
--- a/app/reducers/crashReport.js
+++ b/app/reducers/crashReport.js
@@ -1,7 +1,8 @@
-import { TOGGLE_CRASH_REPORT } from '../actions/actionsTypes';
+import { TOGGLE_CRASH_REPORT, TOGGLE_ANALYTICS_EVENTS } from '../actions/actionsTypes';
const initialState = {
- allowCrashReport: false
+ allowCrashReport: false,
+ allowAnalyticsEvents: false
};
@@ -9,8 +10,15 @@ export default (state = initialState, action) => {
switch (action.type) {
case TOGGLE_CRASH_REPORT:
return {
+ ...state,
allowCrashReport: action.payload
};
+
+ case TOGGLE_ANALYTICS_EVENTS:
+ return {
+ ...state,
+ allowAnalyticsEvents: action.payload
+ };
default:
return state;
}
diff --git a/app/reducers/encryption.js b/app/reducers/encryption.js
new file mode 100644
index 000000000..729c8be41
--- /dev/null
+++ b/app/reducers/encryption.js
@@ -0,0 +1,19 @@
+import { ENCRYPTION } from '../actions/actionsTypes';
+
+const initialState = {
+ banner: null
+};
+
+export default function encryption(state = initialState, action) {
+ switch (action.type) {
+ case ENCRYPTION.SET_BANNER:
+ return {
+ ...state,
+ banner: action.banner
+ };
+ case ENCRYPTION.INIT:
+ return initialState;
+ default:
+ return state;
+ }
+}
diff --git a/app/reducers/enterpriseModules.js b/app/reducers/enterpriseModules.js
new file mode 100644
index 000000000..2f1a7ac9a
--- /dev/null
+++ b/app/reducers/enterpriseModules.js
@@ -0,0 +1,14 @@
+import { ENTERPRISE_MODULES } from '../actions/actionsTypes';
+
+const initialState = [];
+
+export default (state = initialState, action) => {
+ switch (action.type) {
+ case ENTERPRISE_MODULES.SET:
+ return action.payload;
+ case ENTERPRISE_MODULES.CLEAR:
+ return initialState;
+ default:
+ return state;
+ }
+};
diff --git a/app/reducers/index.js b/app/reducers/index.js
index 968254ffd..6211ccb6f 100644
--- a/app/reducers/index.js
+++ b/app/reducers/index.js
@@ -16,7 +16,10 @@ import activeUsers from './activeUsers';
import usersTyping from './usersTyping';
import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
-import inquiry from './inquiry';
+import enterpriseModules from './enterpriseModules';
+import encryption from './encryption';
+
+import inquiry from '../ee/omnichannel/reducers/inquiry';
export default combineReducers({
settings,
@@ -36,5 +39,7 @@ export default combineReducers({
usersTyping,
inviteLinks,
createDiscussion,
- inquiry
+ inquiry,
+ enterpriseModules,
+ encryption
});
diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js
index 2c642006c..ca4bcb9fe 100644
--- a/app/sagas/createChannel.js
+++ b/app/sagas/createChannel.js
@@ -36,8 +36,18 @@ const handleRequest = function* handleRequest({ data }) {
({ room: sub } = result);
}
} else {
- const { type, readOnly, broadcast } = data;
- logEvent(events.CREATE_CHANNEL_CREATE, { type: type ? 'private' : 'public', readOnly, broadcast });
+ const {
+ type,
+ readOnly,
+ broadcast,
+ encrypted
+ } = data;
+ logEvent(events.CREATE_CHANNEL_CREATE, {
+ type: type ? 'private' : 'public',
+ readOnly,
+ broadcast,
+ encrypted
+ });
sub = yield call(createChannel, data);
}
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index b650a06a9..4e30ddfea 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -1,8 +1,8 @@
import {
takeLatest, take, select, put, all, delay
} from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
+import UserPreferences from '../lib/userPreferences';
import Navigation from '../lib/Navigation';
import * as types from '../actions/actionsTypes';
import { selectServerRequest, serverInitAdd } from '../actions/server';
@@ -105,8 +105,8 @@ const handleOpen = function* handleOpen({ params }) {
}
const [server, user] = yield all([
- RNUserDefaults.get('currentServer'),
- RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ host }`)
+ UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER),
+ UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ host }`)
]);
// TODO: needs better test
diff --git a/app/sagas/encryption.js b/app/sagas/encryption.js
new file mode 100644
index 000000000..8c74ede28
--- /dev/null
+++ b/app/sagas/encryption.js
@@ -0,0 +1,129 @@
+import EJSON from 'ejson';
+import { takeLatest, select, put } from 'redux-saga/effects';
+
+import { ENCRYPTION } from '../actions/actionsTypes';
+import { encryptionSetBanner } from '../actions/encryption';
+import { Encryption } from '../lib/encryption';
+import Navigation from '../lib/Navigation';
+import {
+ E2E_PUBLIC_KEY,
+ E2E_PRIVATE_KEY,
+ E2E_BANNER_TYPE,
+ E2E_RANDOM_PASSWORD_KEY
+} from '../lib/encryption/constants';
+import database from '../lib/database';
+import RocketChat from '../lib/rocketchat';
+import UserPreferences from '../lib/userPreferences';
+import { getUserSelector } from '../selectors/login';
+import { showErrorAlert } from '../utils/info';
+import I18n from '../i18n';
+import log from '../utils/log';
+
+const getServer = state => state.share.server || state.server.server;
+const getE2eEnable = state => state.settings.E2E_Enable;
+
+const handleEncryptionInit = function* handleEncryptionInit() {
+ try {
+ const server = yield select(getServer);
+ const user = yield select(getUserSelector);
+ const E2E_Enable = yield select(getE2eEnable);
+
+ // Fetch server info to check E2E enable
+ const serversDB = database.servers;
+ const serversCollection = serversDB.collections.get('servers');
+ let serverInfo;
+ try {
+ serverInfo = yield serversCollection.find(server);
+ } catch {
+ // Server not found
+ }
+
+ // If E2E is disabled on server, skip
+ if (!serverInfo?.E2E_Enable && !E2E_Enable) {
+ return;
+ }
+
+ // Fetch stored private e2e key for this server
+ const storedPrivateKey = yield UserPreferences.getStringAsync(`${ server }-${ E2E_PRIVATE_KEY }`);
+
+ // Fetch server stored e2e keys
+ const keys = yield RocketChat.e2eFetchMyKeys();
+
+ // A private key was received from the server, but it's not saved locally yet
+ // Show the banner asking for the password
+ if (!storedPrivateKey && keys?.privateKey) {
+ yield put(encryptionSetBanner(E2E_BANNER_TYPE.REQUEST_PASSWORD));
+ return;
+ }
+
+ // If the user has a private key stored, but never entered the password
+ const storedRandomPassword = yield UserPreferences.getStringAsync(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`);
+ if (storedRandomPassword) {
+ yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD));
+ }
+
+ // Fetch stored public e2e key for this server
+ let storedPublicKey = yield UserPreferences.getStringAsync(`${ server }-${ E2E_PUBLIC_KEY }`);
+ // Prevent parse undefined
+ if (storedPublicKey) {
+ storedPublicKey = EJSON.parse(storedPublicKey);
+ }
+
+ if (storedPublicKey && storedPrivateKey) {
+ // Persist these keys
+ yield Encryption.persistKeys(server, storedPublicKey, storedPrivateKey);
+ } else {
+ // Create new keys since the user doesn't have any
+ yield Encryption.createKeys(user.id, server);
+ yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD));
+ }
+
+ // Decrypt all pending messages/subscriptions
+ Encryption.initialize(user.id);
+ } catch (e) {
+ log(e);
+ }
+};
+
+const handleEncryptionStop = function* handleEncryptionStop() {
+ // Hide encryption banner
+ yield put(encryptionSetBanner());
+ // Stop Encryption client
+ Encryption.stop();
+};
+
+const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password }) {
+ try {
+ const server = yield select(getServer);
+ const user = yield select(getUserSelector);
+
+ // Fetch server stored e2e keys
+ const keys = yield RocketChat.e2eFetchMyKeys();
+
+ const publicKey = EJSON.parse(keys?.publicKey);
+
+ // Decode the current server key
+ const privateKey = yield Encryption.decodePrivateKey(keys?.privateKey, password, user.id);
+
+ // Persist these decrypted keys
+ yield Encryption.persistKeys(server, publicKey, privateKey);
+
+ // Decrypt all pending messages/subscriptions
+ Encryption.initialize(user.id);
+
+ // Hide encryption banner
+ yield put(encryptionSetBanner());
+
+ Navigation.back();
+ } catch {
+ // Can't decrypt user private key
+ showErrorAlert(I18n.t('Encryption_error_desc'), I18n.t('Encryption_error_title'));
+ }
+};
+
+const root = function* root() {
+ yield takeLatest(ENCRYPTION.INIT, handleEncryptionInit);
+ yield takeLatest(ENCRYPTION.STOP, handleEncryptionStop);
+ yield takeLatest(ENCRYPTION.DECODE_KEY, handleEncryptionDecodeKey);
+};
+export default root;
diff --git a/app/sagas/index.js b/app/sagas/index.js
index 772df5717..e499d74ec 100644
--- a/app/sagas/index.js
+++ b/app/sagas/index.js
@@ -10,7 +10,9 @@ import state from './state';
import deepLinking from './deepLinking';
import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
-import inquiry from './inquiry';
+import encryption from './encryption';
+
+import inquiry from '../ee/omnichannel/sagas/inquiry';
const root = function* root() {
yield all([
@@ -25,7 +27,8 @@ const root = function* root() {
deepLinking(),
inviteLinks(),
createDiscussion(),
- inquiry()
+ inquiry(),
+ encryption()
]);
};
diff --git a/app/sagas/init.js b/app/sagas/init.js
index 25c77da04..5c0cb7286 100644
--- a/app/sagas/init.js
+++ b/app/sagas/init.js
@@ -1,21 +1,14 @@
import { put, takeLatest, all } from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
-import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import RNBootSplash from 'react-native-bootsplash';
-import AsyncStorage from '@react-native-community/async-storage';
+import UserPreferences from '../lib/userPreferences';
import { selectServerRequest } from '../actions/server';
import { setAllPreferences } from '../actions/sortPreferences';
-import { toggleCrashReport } from '../actions/crashReport';
+import { toggleCrashReport, toggleAnalyticsEvents } from '../actions/crashReport';
import { APP } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
-import {
- SERVERS, SERVER_ICON, SERVER_NAME, SERVER_URL, TOKEN, USER_ID
-} from '../constants/userDefaults';
-import { isIOS } from '../utils/deviceInfo';
import database from '../lib/database';
-import protectedFunction from '../lib/methods/helpers/protectedFunction';
import { localAuthenticate } from '../utils/localAuthentication';
import { appStart, ROOT_OUTSIDE, appReady } from '../actions/app';
@@ -25,83 +18,35 @@ export const initLocalSettings = function* initLocalSettings() {
const allowCrashReport = yield RocketChat.getAllowCrashReport();
yield put(toggleCrashReport(allowCrashReport));
+
+ const allowAnalyticsEvents = yield RocketChat.getAllowAnalyticsEvents();
+ yield put(toggleAnalyticsEvents(allowAnalyticsEvents));
};
const restore = function* restore() {
try {
- let hasMigration;
- if (isIOS) {
- hasMigration = yield AsyncStorage.getItem('hasMigration');
- }
-
- let { token, server } = yield all({
- token: RNUserDefaults.get(RocketChat.TOKEN_KEY),
- server: RNUserDefaults.get('currentServer')
+ const { token, server } = yield all({
+ token: UserPreferences.getStringAsync(RocketChat.TOKEN_KEY),
+ server: UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER)
});
- if (!hasMigration && isIOS) {
- let servers = yield RNUserDefaults.objectForKey(SERVERS);
- // if not have current
- if (servers && servers.length !== 0 && (!token || !server)) {
- server = servers[0][SERVER_URL];
- token = servers[0][TOKEN];
- }
-
- // get native credentials
- if (servers) {
- try {
- // parse servers
- servers = yield Promise.all(servers.map(async(s) => {
- await RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ s[SERVER_URL] }`, s[USER_ID]);
- return ({ id: s[SERVER_URL], name: s[SERVER_NAME], iconURL: s[SERVER_ICON] });
- }));
- const serversDB = database.servers;
- yield serversDB.action(async() => {
- const serversCollection = serversDB.collections.get('servers');
- const allServerRecords = await serversCollection.query().fetch();
-
- // filter servers
- let serversToCreate = servers.filter(i1 => !allServerRecords.find(i2 => i1.id === i2.id));
-
- // Create
- serversToCreate = serversToCreate.map(record => serversCollection.prepareCreate(protectedFunction((s) => {
- s._raw = sanitizedRaw({ id: record.id }, serversCollection.schema);
- Object.assign(s, record);
- })));
-
- const allRecords = serversToCreate;
-
- try {
- await serversDB.batch(...allRecords);
- } catch (e) {
- log(e);
- }
- return allRecords.length;
- });
- } catch (e) {
- log(e);
- }
- }
-
- try {
- yield AsyncStorage.setItem('hasMigration', '1');
- } catch (e) {
- log(e);
- }
- }
-
if (!token || !server) {
yield all([
- RNUserDefaults.clear(RocketChat.TOKEN_KEY),
- RNUserDefaults.clear('currentServer')
+ UserPreferences.removeItem(RocketChat.TOKEN_KEY),
+ UserPreferences.removeItem(RocketChat.CURRENT_SERVER)
]);
yield put(appStart({ root: ROOT_OUTSIDE }));
} else {
const serversDB = database.servers;
const serverCollections = serversDB.collections.get('servers');
- yield localAuthenticate(server);
- const serverObj = yield serverCollections.find(server);
+ let serverObj;
+ try {
+ yield localAuthenticate(server);
+ serverObj = yield serverCollections.find(server);
+ } catch {
+ // Server not found
+ }
yield put(selectServerRequest(server, serverObj && serverObj.version));
}
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 01ab28453..bd561cf6f 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -1,10 +1,10 @@
import {
put, call, takeLatest, select, take, fork, cancel, race, delay
} from 'redux-saga/effects';
-import RNUserDefaults from 'rn-user-defaults';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import moment from 'moment';
import 'moment/min/locales';
+import { Q } from '@nozbe/watermelondb';
import * as types from '../actions/actionsTypes';
import {
@@ -15,17 +15,22 @@ import {
loginFailure, loginSuccess, setUser, logout
} from '../actions/login';
import { roomsRequest } from '../actions/rooms';
-import { inquiryRequest } from '../actions/inquiry';
import { toMomentLocale } from '../utils/moment';
import RocketChat from '../lib/rocketchat';
import log, { logEvent, events } from '../utils/log';
-import I18n from '../i18n';
+import I18n, { LANGUAGES } from '../i18n';
import database from '../lib/database';
import EventEmitter from '../utils/events';
import { inviteLinksRequest } from '../actions/inviteLinks';
import { showErrorAlert } from '../utils/info';
import { localAuthenticate } from '../utils/localAuthentication';
import { setActiveUsers } from '../actions/activeUsers';
+import { encryptionInit, encryptionStop } from '../actions/encryption';
+import UserPreferences from '../lib/userPreferences';
+
+import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
+import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
+import { E2E_REFRESH_MESSAGES_KEY } from '../lib/encryption/constants';
const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
@@ -48,6 +53,26 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
} else {
const server = yield select(getServer);
yield localAuthenticate(server);
+
+ // Saves username on server history
+ const serversDB = database.servers;
+ const serversHistoryCollection = serversDB.collections.get('servers_history');
+ yield serversDB.action(async() => {
+ try {
+ const serversHistory = await serversHistoryCollection.query(Q.where('url', server)).fetch();
+ if (serversHistory?.length) {
+ const serverHistoryRecord = serversHistory[0];
+ // this is updating on every login just to save `updated_at`
+ // keeping this server as the most recent on autocomplete order
+ await serverHistoryRecord.update((s) => {
+ s.username = result.username;
+ });
+ }
+ } catch (e) {
+ log(e);
+ }
+ });
+
yield put(loginSuccess(result));
}
} catch (e) {
@@ -85,22 +110,60 @@ const fetchUsersPresence = function* fetchUserPresence() {
RocketChat.subscribeUsersPresence();
};
+const fetchEnterpriseModules = function* fetchEnterpriseModules({ user }) {
+ yield RocketChat.getEnterpriseModules();
+
+ if (isOmnichannelStatusAvailable(user) && RocketChat.isOmnichannelModuleAvailable()) {
+ yield put(inquiryRequest());
+ }
+};
+
+const fetchRooms = function* fetchRooms({ server }) {
+ try {
+ // Read the flag to check if refresh was already done
+ const refreshed = yield UserPreferences.getBoolAsync(E2E_REFRESH_MESSAGES_KEY);
+ if (!refreshed) {
+ const serversDB = database.servers;
+ const serversCollection = serversDB.collections.get('servers');
+
+ const serverRecord = yield serversCollection.find(server);
+
+ // We need to reset roomsUpdatedAt to request all rooms again
+ // and save their respective E2EKeys to decrypt all pending messages and lastMessage
+ // that are already inserted on local database by other app version
+ yield serversDB.action(async() => {
+ await serverRecord.update((s) => {
+ s.roomsUpdatedAt = null;
+ });
+ });
+
+ // Set the flag to indicate that already refreshed
+ yield UserPreferences.setBoolAsync(E2E_REFRESH_MESSAGES_KEY, true);
+ }
+ } catch (e) {
+ log(e);
+ }
+
+ yield put(roomsRequest());
+};
+
const handleLoginSuccess = function* handleLoginSuccess({ user }) {
try {
const adding = yield select(state => state.server.adding);
- yield RNUserDefaults.set(RocketChat.TOKEN_KEY, user.token);
+ yield UserPreferences.setStringAsync(RocketChat.TOKEN_KEY, user.token);
RocketChat.getUserPresence(user.id);
const server = yield select(getServer);
- yield put(roomsRequest());
- yield put(inquiryRequest());
+ yield fork(fetchRooms, { server });
yield fork(fetchPermissions);
yield fork(fetchCustomEmojis);
yield fork(fetchRoles);
yield fork(fetchSlashCommands);
yield fork(registerPushToken);
yield fork(fetchUsersPresence);
+ yield fork(fetchEnterpriseModules, { user });
+ yield put(encryptionInit());
I18n.locale = user.language;
moment.locale(toMomentLocale(user.language));
@@ -114,11 +177,13 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
language: user.language,
status: user.status,
statusText: user.statusText,
- roles: user.roles
+ roles: user.roles,
+ loginEmailPassword: user.loginEmailPassword
};
yield serversDB.action(async() => {
try {
const userRecord = await usersCollection.find(user.id);
+ u.loginEmailPassword = userRecord?.loginEmailPassword;
await userRecord.update((record) => {
record._raw = sanitizedRaw({ id: user.id, ...record._raw }, usersCollection.schema);
Object.assign(record, u);
@@ -131,8 +196,8 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
}
});
- yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ server }`, user.id);
- yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ user.id }`, user.token);
+ yield UserPreferences.setStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`, user.id);
+ yield UserPreferences.setStringAsync(`${ RocketChat.TOKEN_KEY }-${ user.id }`, user.token);
yield put(setUser(user));
EventEmitter.emit('connected');
@@ -161,6 +226,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
};
const handleLogout = function* handleLogout({ forcedByServer }) {
+ yield put(encryptionStop());
yield put(appStart({ root: ROOT_LOADING, text: I18n.t('Logging_out') }));
const server = yield select(getServer);
if (server) {
@@ -182,9 +248,10 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
if (servers.length > 0) {
for (let i = 0; i < servers.length; i += 1) {
const newServer = servers[i].id;
- const token = yield RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ newServer }`);
+ const token = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ newServer }`);
if (token) {
- return yield put(selectServerRequest(newServer));
+ yield put(selectServerRequest(newServer));
+ return;
}
}
}
@@ -200,8 +267,9 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
const handleSetUser = function* handleSetUser({ user }) {
if (user && user.language) {
- I18n.locale = user.language;
- moment.locale(toMomentLocale(user.language));
+ const locale = LANGUAGES.find(l => l.value.toLowerCase() === user.language)?.value || user.language;
+ I18n.locale = locale;
+ moment.locale(toMomentLocale(locale));
}
if (user && user.status) {
@@ -209,8 +277,12 @@ const handleSetUser = function* handleSetUser({ user }) {
yield put(setActiveUsers({ [userId]: user }));
}
- if (user && user.statusLivechat) {
- yield put(inquiryRequest());
+ if (user?.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) {
+ if (isOmnichannelStatusAvailable(user)) {
+ yield put(inquiryRequest());
+ } else {
+ yield put(inquiryReset());
+ }
}
};
diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js
index 7204a33a9..3f240f729 100644
--- a/app/sagas/rooms.js
+++ b/app/sagas/rooms.js
@@ -16,13 +16,17 @@ import protectedFunction from '../lib/methods/helpers/protectedFunction';
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers');
- const serverRecord = yield serversCollection.find(server);
+ try {
+ const serverRecord = yield serversCollection.find(server);
- return serversDB.action(async() => {
- await serverRecord.update((record) => {
- record.roomsUpdatedAt = newRoomsUpdatedAt;
+ return serversDB.action(async() => {
+ await serverRecord.update((record) => {
+ record.roomsUpdatedAt = newRoomsUpdatedAt;
+ });
});
- });
+ } catch {
+ // Server not found
+ }
};
const handleRoomsRequest = function* handleRoomsRequest({ params }) {
@@ -36,8 +40,12 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
yield put(roomsRefresh());
} else {
const serversCollection = serversDB.collections.get('servers');
- const serverRecord = yield serversCollection.find(server);
- ({ roomsUpdatedAt } = serverRecord);
+ try {
+ const serverRecord = yield serversCollection.find(server);
+ ({ roomsUpdatedAt } = serverRecord);
+ } catch {
+ // Server not found
+ }
}
const [subscriptionsResult, roomsResult] = yield RocketChat.getRooms(roomsUpdatedAt);
const { subscriptions } = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult);
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 51728b8a8..6afb8fd1d 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -1,7 +1,7 @@
import { put, takeLatest } from 'redux-saga/effects';
import { Alert } from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
+import { Q } from '@nozbe/watermelondb';
import semver from 'semver';
import Navigation from '../lib/Navigation';
@@ -16,10 +16,12 @@ import database from '../lib/database';
import log, { logServerVersion } from '../utils/log';
import { extractHostname } from '../utils/server';
import I18n from '../i18n';
-import { SERVERS, TOKEN, SERVER_URL } from '../constants/userDefaults';
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';
import { appStart, ROOT_INSIDE, ROOT_OUTSIDE } from '../actions/app';
-import { inquiryReset } from '../actions/inquiry';
+import UserPreferences from '../lib/userPreferences';
+import { encryptionStop } from '../actions/encryption';
+
+import { inquiryReset } from '../ee/omnichannel/actions/inquiry';
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
try {
@@ -31,7 +33,7 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
if (!serverInfo.success || !websocketInfo.success) {
if (raiseError) {
const info = serverInfo.success ? websocketInfo : serverInfo;
- Alert.alert(I18n.t('Oops'), I18n.t(info.message, info.messageOptions));
+ Alert.alert(I18n.t('Oops'), info.message);
}
yield put(serverFailure());
return;
@@ -67,13 +69,15 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
const handleSelectServer = function* handleSelectServer({ server, version, fetchVersion }) {
try {
yield put(inquiryReset());
+ yield put(encryptionStop());
const serversDB = database.servers;
- yield RNUserDefaults.set('currentServer', server);
- const userId = yield RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
+ const userId = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users');
let user = null;
if (userId) {
try {
+ // search credentials on database
const userRecord = yield userCollections.find(userId);
user = {
id: userRecord.id,
@@ -85,17 +89,16 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
statusText: userRecord.statusText,
roles: userRecord.roles
};
- } catch (e) {
- // We only run it if not has user on DB
- const servers = yield RNUserDefaults.objectForKey(SERVERS);
- const userCredentials = servers && servers.find(srv => srv[SERVER_URL] === server);
- user = userCredentials && {
- token: userCredentials[TOKEN]
- };
+ } catch {
+ // search credentials on shared credentials (Experimental/Official)
+ const token = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ userId }`);
+ if (token) {
+ user = { token };
+ }
}
}
- const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`);
+ const basicAuth = yield UserPreferences.getStringAsync(`${ BASIC_AUTH_KEY }-${ server }`);
setBasicAuth(basicAuth);
// Check for running requests and abort them before connecting to the server
@@ -115,6 +118,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
// and block the selectServerSuccess raising multiples errors
RocketChat.setSettings();
RocketChat.setCustomEmojis();
+ RocketChat.setEnterpriseModules();
let serverInfo;
if (fetchVersion) {
@@ -133,18 +137,39 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
}
};
-const handleServerRequest = function* handleServerRequest({ server, certificate }) {
+const handleServerRequest = function* handleServerRequest({
+ server, certificate, username, fromServerHistory
+}) {
try {
if (certificate) {
- yield RNUserDefaults.setObjectForKey(extractHostname(server), certificate);
+ yield UserPreferences.setMapAsync(extractHostname(server), certificate);
}
const serverInfo = yield getServerInfo({ server });
+ const serversDB = database.servers;
+ const serversHistoryCollection = serversDB.collections.get('servers_history');
if (serverInfo) {
yield RocketChat.getLoginServices(server);
yield RocketChat.getLoginSettings({ server });
Navigation.navigate('WorkspaceView');
+
+ if (fromServerHistory) {
+ Navigation.navigate('LoginView', { username });
+ }
+
+ yield serversDB.action(async() => {
+ try {
+ const serversHistory = await serversHistoryCollection.query(Q.where('url', server)).fetch();
+ if (!serversHistory?.length) {
+ await serversHistoryCollection.create((s) => {
+ s.url = server;
+ });
+ }
+ } catch (e) {
+ log(e);
+ }
+ });
yield put(selectServerRequest(server, serverInfo.version, false));
}
} catch (e) {
diff --git a/app/share.js b/app/share.js
index 93840b828..d1f83fd88 100644
--- a/app/share.js
+++ b/app/share.js
@@ -5,7 +5,6 @@ import { NavigationContainer } from '@react-navigation/native';
import { AppearanceProvider } from 'react-native-appearance';
import { createStackNavigator } from '@react-navigation/stack';
import { Provider } from 'react-redux';
-import RNUserDefaults from 'rn-user-defaults';
import {
defaultTheme,
@@ -13,6 +12,7 @@ import {
subscribeTheme,
unsubscribeTheme
} from './utils/theme';
+import UserPreferences from './lib/userPreferences';
import Navigation from './lib/ShareNavigation';
import store from './lib/createStore';
import { supportSystemTheme } from './utils/deviceInfo';
@@ -138,9 +138,9 @@ class Root extends React.Component {
}
init = async() => {
- RNUserDefaults.objectForKey(THEME_PREFERENCES_KEY).then(this.setTheme);
- const currentServer = await RNUserDefaults.get('currentServer');
- const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY);
+ UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
+
+ const [currentServer, token] = await Promise.all([UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER), UserPreferences.getStringAsync(RocketChat.TOKEN_KEY)]);
if (currentServer && token) {
await localAuthenticate(currentServer);
diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js
index e9a4a7a74..a5a664986 100644
--- a/app/stacks/InsideStack.js
+++ b/app/stacks/InsideStack.js
@@ -30,10 +30,11 @@ import PickerView from '../views/PickerView';
import ThreadMessagesView from '../views/ThreadMessagesView';
import MarkdownTableView from '../views/MarkdownTableView';
import ReadReceiptsView from '../views/ReadReceiptView';
-import QueueListView from '../views/QueueListView';
// Profile Stack
import ProfileView from '../views/ProfileView';
+import UserPreferencesView from '../views/UserPreferencesView';
+import UserNotificationPrefView from '../views/UserNotificationPreferencesView';
// Settings Stack
import SettingsView from '../views/SettingsView';
@@ -49,6 +50,13 @@ import AdminPanelView from '../views/AdminPanelView';
import NewMessageView from '../views/NewMessageView';
import CreateChannelView from '../views/CreateChannelView';
+// E2ESaveYourPassword Stack
+import E2ESaveYourPasswordView from '../views/E2ESaveYourPasswordView';
+import E2EHowItWorksView from '../views/E2EHowItWorksView';
+
+// E2EEnterYourPassword Stack
+import E2EEnterYourPasswordView from '../views/E2EEnterYourPasswordView';
+
// InsideStackNavigator
import AttachmentView from '../views/AttachmentView';
import ModalBlockView from '../views/ModalBlockView';
@@ -57,6 +65,8 @@ import StatusView from '../views/StatusView';
import ShareView from '../views/ShareView';
import CreateDiscussionView from '../views/CreateDiscussionView';
+import QueueListView from '../ee/omnichannel/views/QueueListView';
+
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
const ChatsStackNavigator = () => {
@@ -184,6 +194,21 @@ const ProfileStackNavigator = () => {
component={ProfileView}
options={ProfileView.navigationOptions}
/>
+
+
+
);
};
@@ -284,6 +309,43 @@ const NewMessageStackNavigator = () => {
);
};
+// E2ESaveYourPasswordStackNavigator
+const E2ESaveYourPasswordStack = createStackNavigator();
+const E2ESaveYourPasswordStackNavigator = () => {
+ const { theme } = React.useContext(ThemeContext);
+
+ return (
+
+
+
+
+ );
+};
+
+// E2EEnterYourPasswordStackNavigator
+const E2EEnterYourPasswordStack = createStackNavigator();
+const E2EEnterYourPasswordStackNavigator = () => {
+ const { theme } = React.useContext(ThemeContext);
+
+ return (
+
+
+
+ );
+};
+
// InsideStackNavigator
const InsideStack = createStackNavigator();
const InsideStackNavigator = () => {
@@ -301,6 +363,16 @@ const InsideStackNavigator = () => {
component={NewMessageStackNavigator}
options={{ headerShown: false }}
/>
+
+
{
@@ -253,6 +259,31 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
name='CreateDiscussionView'
component={CreateDiscussionView}
/>
+
+
+
+
+
);
diff --git a/app/utils/appGroup.js b/app/utils/appGroup.js
new file mode 100644
index 000000000..63fb428aa
--- /dev/null
+++ b/app/utils/appGroup.js
@@ -0,0 +1,11 @@
+import { NativeModules } from 'react-native';
+
+import { isIOS } from './deviceInfo';
+
+const { AppGroup } = NativeModules;
+
+const appGroup = {
+ path: isIOS ? AppGroup.path : ''
+};
+
+export default appGroup;
diff --git a/app/utils/base64-js/base64-js.test.js b/app/utils/base64-js/base64-js.test.js
new file mode 100644
index 000000000..83e3de762
--- /dev/null
+++ b/app/utils/base64-js/base64-js.test.js
@@ -0,0 +1,144 @@
+/* eslint-disable no-undef */
+/* eslint-disable no-bitwise */
+// https://github.com/beatgammit/base64-js/tree/master/test
+
+import {
+ byteLength,
+ toByteArray,
+ fromByteArray
+} from './index';
+
+const map = (arr, callback) => {
+ const res = [];
+ let kValue;
+ let mappedValue;
+
+ for (let k = 0, len = arr.length; k < len; k += 1) {
+ if ((typeof arr === 'string' && !!arr.charAt(k))) {
+ kValue = arr.charAt(k);
+ mappedValue = callback(kValue, k, arr);
+ res[k] = mappedValue;
+ } else if (typeof arr !== 'string' && k in arr) {
+ kValue = arr[k];
+ mappedValue = callback(kValue, k, arr);
+ res[k] = mappedValue;
+ }
+ }
+ return res;
+};
+
+expect.extend({
+ toBeEqual(a, b) {
+ let i;
+ const { length } = a;
+ if (length !== b.length) {
+ return {
+ pass: false
+ };
+ }
+ for (i = 0; i < length; i += 1) {
+ if ((a[i] & 0xFF) !== (b[i] & 0xFF)) {
+ return {
+ pass: false
+ };
+ }
+ }
+ return {
+ pass: true
+ };
+ }
+});
+
+test('decode url-safe style base64 strings', () => {
+ const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff];
+
+ let str = '//++/++/++//';
+ let actual = toByteArray(str);
+ for (let i = 0; i < actual.length; i += 1) {
+ expect(actual[i]).toBe(expected[i]);
+ }
+
+ expect(byteLength(str)).toBe(actual.length);
+
+ str = '__--_--_--__';
+ actual = toByteArray(str);
+ for (let i = 0; i < actual.length; i += 1) {
+ expect(actual[i]).toBe(expected[i]);
+ }
+
+ expect(byteLength(str)).toBe(actual.length);
+});
+
+test('padding bytes found inside base64 string', () => {
+ // See https://github.com/beatgammit/base64-js/issues/42
+ const str = 'SQ==QU0=';
+ expect(toByteArray(str)).toEqual(new Uint8Array([73]));
+ expect(byteLength(str)).toBe(1);
+});
+
+const checks = [
+ 'a',
+ 'aa',
+ 'aaa',
+ 'hi',
+ 'hi!',
+ 'hi!!',
+ 'sup',
+ 'sup?',
+ 'sup?!'
+];
+
+test('convert to base64 and back', () => {
+ for (let i = 0; i < checks.length; i += 1) {
+ const check = checks[i];
+
+ const b64Str = fromByteArray(map(check, char => char.charCodeAt(0)));
+
+ const arr = toByteArray(b64Str);
+ const str = map(arr, byte => String.fromCharCode(byte)).join('');
+
+ expect(check).toBe(str);
+ expect(byteLength(b64Str)).toBe(arr.length);
+ }
+});
+
+const data = [
+ [[0, 0, 0], 'AAAA'],
+ [[0, 0, 1], 'AAAB'],
+ [[0, 1, -1], 'AAH/'],
+ [[1, 1, 1], 'AQEB'],
+ [[0, -73, 23], 'ALcX']
+];
+
+test('convert known data to string', () => {
+ for (let i = 0; i < data.length; i += 1) {
+ const bytes = data[i][0];
+ const expected = data[i][1];
+ const actual = fromByteArray(bytes);
+ expect(actual).toBe(expected);
+ }
+});
+
+test('convert known data from string', () => {
+ for (let i = 0; i < data.length; i += 1) {
+ const expected = data[i][0];
+ const string = data[i][1];
+ const actual = toByteArray(string);
+ expect(actual).toBeEqual(expected);
+ const length = byteLength(string);
+ expect(length).toBe(expected.length);
+ }
+});
+
+test('convert big data to base64', () => {
+ let i;
+ let length;
+ const big = new Uint8Array(64 * 1024 * 1024);
+ for (i = 0, length = big.length; i < length; i += 1) {
+ big[i] = i % 256;
+ }
+ const b64str = fromByteArray(big);
+ const arr = toByteArray(b64str);
+ expect(arr).toBeEqual(big);
+ expect(byteLength(b64str)).toBe(arr.length);
+});
diff --git a/app/utils/base64-js/index.js b/app/utils/base64-js/index.js
new file mode 100644
index 000000000..ef4bcd4ce
--- /dev/null
+++ b/app/utils/base64-js/index.js
@@ -0,0 +1,141 @@
+/* eslint-disable no-bitwise */
+// https://github.com/beatgammit/base64-js/blob/master/index.js
+
+const lookup = [];
+const revLookup = [];
+const Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
+
+const code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+for (let i = 0, len = code.length; i < len; i += 1) {
+ lookup[i] = code[i];
+ revLookup[code.charCodeAt(i)] = i;
+}
+
+// Support decoding URL-safe base64 strings, as Node.js does.
+// See: https://en.wikipedia.org/wiki/Base64#URL_applications
+revLookup['-'.charCodeAt(0)] = 62;
+revLookup['_'.charCodeAt(0)] = 63;
+
+const getLens = (b64) => {
+ const len = b64.length;
+
+ // We're encoding some strings not multiple of 4, so, disable this check
+ // if (len % 4 > 0) {
+ // throw new Error('Invalid string. Length must be a multiple of 4');
+ // }
+
+ // Trim off extra bytes after placeholder bytes are found
+ // See: https://github.com/beatgammit/base64-js/issues/42
+ let validLen = b64.indexOf('=');
+ if (validLen === -1) { validLen = len; }
+
+ const placeHoldersLen = validLen === len
+ ? 0
+ : 4 - (validLen % 4);
+
+ return [validLen, placeHoldersLen];
+};
+
+// base64 is 4/3 + up to two characters of the original data
+export const byteLength = (b64) => {
+ const lens = getLens(b64);
+ const validLen = lens[0];
+ const placeHoldersLen = lens[1];
+ return (((validLen + placeHoldersLen) * 3) / 4) - placeHoldersLen;
+};
+
+const _byteLength = (b64, validLen, placeHoldersLen) => (((validLen + placeHoldersLen) * 3) / 4) - placeHoldersLen;
+
+export const toByteArray = (b64) => {
+ let tmp;
+ const lens = getLens(b64);
+ const validLen = lens[0];
+ const placeHoldersLen = lens[1];
+
+ const arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));
+
+ let curByte = 0;
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ const len = placeHoldersLen > 0
+ ? validLen - 4
+ : validLen;
+
+ let i;
+ for (i = 0; i < len; i += 4) {
+ tmp = (revLookup[b64.charCodeAt(i)] << 18)
+ | (revLookup[b64.charCodeAt(i + 1)] << 12)
+ | (revLookup[b64.charCodeAt(i + 2)] << 6)
+ | revLookup[b64.charCodeAt(i + 3)];
+ arr[curByte] = (tmp >> 16) & 0xFF;
+ curByte += 1;
+ arr[curByte] = (tmp >> 8) & 0xFF;
+ curByte += 1;
+ arr[curByte] = tmp & 0xFF;
+ curByte += 1;
+ }
+
+ if (placeHoldersLen === 2) {
+ tmp = (revLookup[b64.charCodeAt(i)] << 2)
+ | (revLookup[b64.charCodeAt(i + 1)] >> 4);
+ arr[curByte] = tmp & 0xFF;
+ curByte += 1;
+ }
+
+ if (placeHoldersLen === 1) {
+ tmp = (revLookup[b64.charCodeAt(i)] << 10)
+ | (revLookup[b64.charCodeAt(i + 1)] << 4)
+ | (revLookup[b64.charCodeAt(i + 2)] >> 2);
+ arr[curByte] = (tmp >> 8) & 0xFF;
+ curByte += 1;
+ arr[curByte] = tmp & 0xFF;
+ curByte += 1;
+ }
+
+ return arr;
+};
+
+const tripletToBase64 = num => lookup[(num >> 18) & 0x3F]
+ + lookup[(num >> 12) & 0x3F]
+ + lookup[(num >> 6) & 0x3F]
+ + lookup[num & 0x3F];
+
+const encodeChunk = (uint8, start, end) => {
+ let tmp;
+ const output = [];
+ for (let i = start; i < end; i += 3) {
+ tmp = ((uint8[i] << 16) & 0xFF0000) + ((uint8[i + 1] << 8) & 0xFF00) + (uint8[i + 2] & 0xFF);
+ output.push(tripletToBase64(tmp));
+ }
+ return output.join('');
+};
+
+export const fromByteArray = (uint8) => {
+ let tmp;
+ const len = uint8.length;
+ const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
+ const parts = [];
+ const maxChunkLength = 16383; // must be multiple of 3
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
+ parts.push(encodeChunk(
+ uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
+ ));
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ if (extraBytes === 1) {
+ tmp = uint8[len - 1];
+ parts.push(
+ `${ lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3F] }==`
+ );
+ } else if (extraBytes === 2) {
+ tmp = (uint8[len - 2] << 8) + uint8[len - 1];
+ parts.push(
+ `${ lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3F] + lookup[(tmp << 2) & 0x3F] }=`
+ );
+ }
+
+ return parts.join('');
+};
diff --git a/app/utils/deferred.js b/app/utils/deferred.js
new file mode 100644
index 000000000..0c0046e55
--- /dev/null
+++ b/app/utils/deferred.js
@@ -0,0 +1,14 @@
+// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
+export default class Deferred {
+ constructor() {
+ const promise = new Promise((resolve, reject) => {
+ this.resolve = resolve;
+ this.reject = reject;
+ });
+
+ promise.resolve = this.resolve;
+ promise.reject = this.reject;
+
+ return promise;
+ }
+}
diff --git a/app/utils/info.js b/app/utils/info.js
index 8114ef2aa..685b2f948 100644
--- a/app/utils/info.js
+++ b/app/utils/info.js
@@ -3,17 +3,20 @@ import I18n from '../i18n';
export const showErrorAlert = (message, title, onPress = () => {}) => Alert.alert(title, message, [{ text: 'OK', onPress }], { cancelable: true });
-export const showConfirmationAlert = ({ message, callToAction, onPress }) => (
+export const showConfirmationAlert = ({
+ title, message, confirmationText, dismissText = I18n.t('Cancel'), onPress, onCancel
+}) => (
Alert.alert(
- I18n.t('Are_you_sure_question_mark'),
+ title || I18n.t('Are_you_sure_question_mark'),
message,
[
{
- text: I18n.t('Cancel'),
+ text: dismissText,
+ onPress: onCancel,
style: 'cancel'
},
{
- text: callToAction,
+ text: confirmationText,
style: 'destructive',
onPress
}
diff --git a/app/utils/localAuthentication.js b/app/utils/localAuthentication.js
index 37c03fe37..2eb9c7d29 100644
--- a/app/utils/localAuthentication.js
+++ b/app/utils/localAuthentication.js
@@ -2,9 +2,9 @@ import * as LocalAuthentication from 'expo-local-authentication';
import moment from 'moment';
import RNBootSplash from 'react-native-bootsplash';
import AsyncStorage from '@react-native-community/async-storage';
-import RNUserDefaults from 'rn-user-defaults';
import { sha256 } from 'js-sha256';
+import UserPreferences from '../lib/userPreferences';
import store from '../lib/createStore';
import database from '../lib/database';
import { isIOS } from './deviceInfo';
@@ -51,7 +51,7 @@ const openChangePasscodeModal = ({ force }) => new Promise((resolve, reject) =>
export const changePasscode = async({ force = false }) => {
const passcode = await openChangePasscodeModal({ force });
- await RNUserDefaults.set(PASSCODE_KEY, sha256(passcode));
+ await UserPreferences.setStringAsync(PASSCODE_KEY, sha256(passcode));
};
export const biometryAuth = force => LocalAuthentication.authenticateAsync({
@@ -80,7 +80,7 @@ const checkBiometry = async(serverRecord) => {
};
export const checkHasPasscode = async({ force = true, serverRecord }) => {
- const storedPasscode = await RNUserDefaults.get(PASSCODE_KEY);
+ const storedPasscode = await UserPreferences.getStringAsync(PASSCODE_KEY);
if (!storedPasscode) {
await changePasscode({ force });
await checkBiometry(serverRecord);
diff --git a/app/utils/log/events.js b/app/utils/log/events.js
index c6d51d7e2..89b77848e 100644
--- a/app/utils/log/events.js
+++ b/app/utils/log/events.js
@@ -58,6 +58,7 @@ export default {
RL_ADD_SERVER: 'rl_add_server',
RL_CHANGE_SERVER: 'rl_change_server',
RL_GO_NEW_MSG: 'rl_go_new_msg',
+ RL_GO_E2E_SAVE_PASSWORD: 'rl_go_e2e_save_password',
RL_SEARCH: 'rl_search',
RL_GO_DIRECTORY: 'rl_go_directory',
RL_GO_QUEUE: 'rl_go_queue',
@@ -103,6 +104,7 @@ export default {
CREATE_CHANNEL_TOGGLE_TYPE: 'create_channel_toggle_type',
CREATE_CHANNEL_TOGGLE_READ_ONLY: 'create_channel_toggle_read_only',
CREATE_CHANNEL_TOGGLE_BROADCAST: 'create_channel_toggle_broadcast',
+ CREATE_CHANNEL_TOGGLE_ENCRYPTED: 'create_channel_toggle_encrypted',
CREATE_CHANNEL_REMOVE_USER: 'create_channel_remove_user',
// CREATE DISCUSSION VIEW
@@ -119,6 +121,8 @@ export default {
PROFILE_SAVE_AVATAR_F: 'profile_save_avatar_f',
PROFILE_SAVE_CHANGES: 'profile_save_changes',
PROFILE_SAVE_CHANGES_F: 'profile_save_changes_f',
+ PROFILE_LOGOUT_OTHER_LOCATIONS: 'profile_logout_other_locations',
+ PROFILE_LOGOUT_OTHER_LOCATIONS_F: 'profile_logout_other_locations_f',
// SETTINGS VIEW
SE_CONTACT_US: 'se_contact_us',
@@ -135,6 +139,7 @@ export default {
SE_COPY_APP_VERSION: 'se_copy_app_version',
SE_COPY_SERVER_VERSION: 'se_copy_server_version',
SE_TOGGLE_CRASH_REPORT: 'se_toggle_crash_report',
+ SE_TOGGLE_ANALYTICS_EVENTS: 'se_toggle_analytics_events',
SE_CLEAR_LOCAL_SERVER_CACHE: 'se_clear_local_server_cache',
SE_LOG_OUT: 'se_log_out',
@@ -159,6 +164,7 @@ export default {
// ROOM VIEW
ROOM_SEND_MESSAGE: 'room_send_message',
+ ROOM_ENCRYPTED_PRESS: 'room_encrypted_press',
ROOM_OPEN_EMOJI: 'room_open_emoji',
ROOM_AUDIO_RECORD: 'room_audio_record',
ROOM_AUDIO_RECORD_F: 'room_audio_record_f',
@@ -231,6 +237,8 @@ export default {
RA_LEAVE_F: 'ra_leave_f',
RA_TOGGLE_BLOCK_USER: 'ra_toggle_block_user',
RA_TOGGLE_BLOCK_USER_F: 'ra_toggle_block_user_f',
+ RA_TOGGLE_ENCRYPTED: 'ra_toggle_encrypted',
+ RA_TOGGLE_ENCRYPTED_F: 'ra_toggle_encrypted_f',
// ROOM INFO VIEW
RI_GO_RI_EDIT: 'ri_go_ri_edit',
@@ -242,6 +250,7 @@ export default {
RI_EDIT_TOGGLE_READ_ONLY: 'ri_edit_toggle_read_only',
RI_EDIT_TOGGLE_REACTIONS: 'ri_edit_toggle_reactions',
RI_EDIT_TOGGLE_SYSTEM_MSG: 'ri_edit_toggle_system_msg',
+ RI_EDIT_TOGGLE_ENCRYPTED: 'ri_edit_toggle_encrypted',
RI_EDIT_SAVE: 'ri_edit_save',
RI_EDIT_SAVE_F: 'ri_edit_save_f',
RI_EDIT_RESET: 'ri_edit_reset',
@@ -286,5 +295,13 @@ export default {
NP_DESKTOPNOTIFICATIONDURATION: 'np_desktopnotificationduration',
NP_DESKTOPNOTIFICATIONDURATION_F: 'np_desktopnotificationduration_f',
NP_EMAILNOTIFICATIONS: 'np_email_notifications',
- NP_EMAILNOTIFICATIONS_F: 'np_email_notifications_f'
+ NP_EMAILNOTIFICATIONS_F: 'np_email_notifications_f',
+
+ // E2E SAVE YOUR PASSWORD VIEW
+ E2E_SAVE_PW_SAVED: 'e2e_save_pw_saved',
+ E2E_SAVE_PW_COPY: 'e2e_save_pw_copy',
+ E2E_SAVE_PW_HOW_IT_WORKS: 'e2e_save_pw_how_it_works',
+
+ // E2E ENTER YOUR PASSWORD VIEW
+ E2E_ENTER_PW_SUBMIT: 'e2e_enter_pw_submit'
};
diff --git a/app/utils/log/index.js b/app/utils/log/index.js
index ea00ca325..da1be54a9 100644
--- a/app/utils/log/index.js
+++ b/app/utils/log/index.js
@@ -1,10 +1,17 @@
-import { Client } from 'bugsnag-react-native';
-import analytics from '@react-native-firebase/analytics';
-import crashlytics from '@react-native-firebase/crashlytics';
+import firebaseAnalytics from '@react-native-firebase/analytics';
+import { isFDroidBuild } from '../../constants/environment';
import config from '../../../config';
import events from './events';
-const bugsnag = new Client(config.BUGSNAG_API_KEY);
+const analytics = firebaseAnalytics || '';
+let bugsnag = '';
+let crashlytics;
+
+if (!isFDroidBuild) {
+ const { Client } = require('bugsnag-react-native');
+ crashlytics = require('@react-native-firebase/crashlytics').default;
+ bugsnag = new Client(config.BUGSNAG_API_KEY);
+}
export { analytics };
export const loggerConfig = bugsnag.config;
@@ -21,20 +28,24 @@ export const logServerVersion = (serverVersion) => {
export const logEvent = (eventName, payload) => {
try {
- analytics().logEvent(eventName, payload);
- leaveBreadcrumb(eventName, payload);
+ if (!isFDroidBuild) {
+ analytics().logEvent(eventName, payload);
+ leaveBreadcrumb(eventName, payload);
+ }
} catch {
// Do nothing
}
};
export const setCurrentScreen = (currentScreen) => {
- analytics().setCurrentScreen(currentScreen);
- leaveBreadcrumb(currentScreen, { type: 'navigation' });
+ if (!isFDroidBuild) {
+ analytics().setCurrentScreen(currentScreen);
+ leaveBreadcrumb(currentScreen, { type: 'navigation' });
+ }
};
export default (e) => {
- if (e instanceof Error && e.message !== 'Aborted' && !__DEV__) {
+ if (e instanceof Error && bugsnag && e.message !== 'Aborted' && !__DEV__) {
bugsnag.notify(e, (report) => {
report.metadata = {
details: {
@@ -42,7 +53,9 @@ export default (e) => {
}
};
});
- crashlytics().recordError(e);
+ if (!isFDroidBuild) {
+ crashlytics().recordError(e);
+ }
} else {
console.log(e);
}
diff --git a/app/utils/openLink.js b/app/utils/openLink.js
index 9341693a1..36f72c0e2 100644
--- a/app/utils/openLink.js
+++ b/app/utils/openLink.js
@@ -1,8 +1,8 @@
import { Linking } from 'react-native';
import * as WebBrowser from 'expo-web-browser';
-import RNUserDefaults from 'rn-user-defaults';
import parse from 'url-parse';
+import UserPreferences from '../lib/userPreferences';
import { themes } from '../constants/colors';
export const DEFAULT_BROWSER_KEY = 'DEFAULT_BROWSER_KEY';
@@ -37,7 +37,7 @@ const appSchemeURL = (url, browser) => {
const openLink = async(url, theme = 'light') => {
try {
- const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);
+ const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
if (browser) {
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
diff --git a/app/utils/review.js b/app/utils/review.js
index 17ba73c90..98d673631 100644
--- a/app/utils/review.js
+++ b/app/utils/review.js
@@ -5,6 +5,7 @@ import { isIOS } from './deviceInfo';
import I18n from '../i18n';
import { showErrorAlert } from './info';
import { STORE_REVIEW_LINK } from '../constants/links';
+import { isFDroidBuild } from '../constants/environment';
import { logEvent, events } from './log';
const store = isIOS ? 'App Store' : 'Play Store';
@@ -87,12 +88,14 @@ class ReviewApp {
positiveEventCount = 0;
pushPositiveEvent = () => {
- if (this.positiveEventCount >= numberOfPositiveEvent) {
- return;
- }
- this.positiveEventCount += 1;
- if (this.positiveEventCount === numberOfPositiveEvent) {
- tryReview();
+ if (!isFDroidBuild) {
+ if (this.positiveEventCount >= numberOfPositiveEvent) {
+ return;
+ }
+ this.positiveEventCount += 1;
+ if (this.positiveEventCount === numberOfPositiveEvent) {
+ tryReview();
+ }
}
}
}
diff --git a/app/views/AttachmentView.js b/app/views/AttachmentView.js
index 6f43823da..770c30c21 100644
--- a/app/views/AttachmentView.js
+++ b/app/views/AttachmentView.js
@@ -42,7 +42,8 @@ class AttachmentView extends React.Component {
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
- })
+ }),
+ Allow_Save_Media_to_Gallery: PropTypes.bool
}
constructor(props) {
@@ -68,7 +69,9 @@ class AttachmentView extends React.Component {
}
setHeader = () => {
- const { route, navigation, theme } = this.props;
+ const {
+ route, navigation, theme, Allow_Save_Media_to_Gallery
+ } = this.props;
const attachment = route.params?.attachment;
let { title } = attachment;
try {
@@ -79,10 +82,14 @@ class AttachmentView extends React.Component {
const options = {
title,
headerLeft: () => ,
- headerRight: () => ,
+ headerRight: () => (
+ Allow_Save_Media_to_Gallery
+ ?
+ : null
+ ),
headerBackground: () => ,
headerTintColor: themes[theme].previewTintColor,
- headerTitleStyle: { color: themes[theme].previewTintColor }
+ headerTitleStyle: { color: themes[theme].previewTintColor, marginHorizontal: 10 }
};
navigation.setOptions(options);
}
@@ -180,7 +187,8 @@ class AttachmentView extends React.Component {
const mapStateToProps = state => ({
baseUrl: state.server.server,
- user: getUserSelector(state)
+ user: getUserSelector(state),
+ Allow_Save_Media_to_Gallery: state.settings.Allow_Save_Media_to_Gallery ?? true
});
export default connect(mapStateToProps)(withTheme(withDimensions(withSafeAreaInsets(AttachmentView))));
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index c4f2c5407..c78be31dd 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -85,6 +85,7 @@ class CreateChannelView extends React.Component {
error: PropTypes.object,
failure: PropTypes.bool,
isFetching: PropTypes.bool,
+ e2eEnabled: PropTypes.bool,
users: PropTypes.array.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
@@ -97,14 +98,17 @@ class CreateChannelView extends React.Component {
channelName: '',
type: true,
readOnly: false,
+ encrypted: false,
broadcast: false
}
shouldComponentUpdate(nextProps, nextState) {
const {
- channelName, type, readOnly, broadcast
+ channelName, type, readOnly, broadcast, encrypted
} = this.state;
- const { users, isFetching, theme } = this.props;
+ const {
+ users, isFetching, e2eEnabled, theme
+ } = this.props;
if (nextProps.theme !== theme) {
return true;
}
@@ -117,12 +121,18 @@ class CreateChannelView extends React.Component {
if (nextState.readOnly !== readOnly) {
return true;
}
+ if (nextState.encrypted !== encrypted) {
+ return true;
+ }
if (nextState.broadcast !== broadcast) {
return true;
}
if (nextProps.isFetching !== isFetching) {
return true;
}
+ if (nextProps.e2eEnabled !== e2eEnabled) {
+ return true;
+ }
if (!equal(nextProps.users, users)) {
return true;
}
@@ -147,7 +157,7 @@ class CreateChannelView extends React.Component {
submit = () => {
const {
- channelName, type, readOnly, broadcast
+ channelName, type, readOnly, broadcast, encrypted
} = this.state;
const { users: usersProps, isFetching, create } = this.props;
@@ -160,7 +170,7 @@ class CreateChannelView extends React.Component {
// create channel
create({
- name: channelName, users, type, readOnly, broadcast
+ name: channelName, users, type, readOnly, broadcast, encrypted
});
Review.pushPositiveEvent();
@@ -198,7 +208,8 @@ class CreateChannelView extends React.Component {
label: 'Private_Channel',
onValueChange: (value) => {
logEvent(events.CREATE_CHANNEL_TOGGLE_TYPE);
- this.setState({ type: value });
+ // If we set the channel as public, encrypted status should be false
+ this.setState(({ encrypted }) => ({ type: value, encrypted: value && encrypted }));
}
});
}
@@ -217,6 +228,26 @@ class CreateChannelView extends React.Component {
});
}
+ renderEncrypted() {
+ const { type, encrypted } = this.state;
+ const { e2eEnabled } = this.props;
+
+ if (!e2eEnabled) {
+ return null;
+ }
+
+ return this.renderSwitch({
+ id: 'encrypted',
+ value: encrypted,
+ label: 'Encrypted',
+ onValueChange: (value) => {
+ logEvent(events.CREATE_CHANNEL_TOGGLE_ENCRYPTED);
+ this.setState({ encrypted: value });
+ },
+ disabled: !type
+ });
+ }
+
renderBroadcast() {
const { broadcast, readOnly } = this.state;
return this.renderSwitch({
@@ -315,6 +346,8 @@ class CreateChannelView extends React.Component {
{this.renderFormSeparator()}
{this.renderReadOnly()}
{this.renderFormSeparator()}
+ {this.renderEncrypted()}
+ {this.renderFormSeparator()}
{this.renderBroadcast()}
@@ -333,6 +366,7 @@ class CreateChannelView extends React.Component {
const mapStateToProps = state => ({
baseUrl: state.server.server,
isFetching: state.createChannel.isFetching,
+ e2eEnabled: state.settings.E2E_Enable,
users: state.selectedUsers.users,
user: getUserSelector(state)
});
diff --git a/app/views/DefaultBrowserView.js b/app/views/DefaultBrowserView.js
index e51b11f02..d5c14be56 100644
--- a/app/views/DefaultBrowserView.js
+++ b/app/views/DefaultBrowserView.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import {
StyleSheet, FlatList, View, Text, Linking
} from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import I18n from '../i18n';
import { withTheme } from '../theme';
@@ -16,6 +15,7 @@ import { CustomIcon } from '../lib/Icons';
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
import { isIOS } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
+import UserPreferences from '../lib/userPreferences';
import { logEvent, events } from '../utils/log';
const DEFAULT_BROWSERS = [
@@ -81,12 +81,8 @@ class DefaultBrowserView extends React.Component {
async componentDidMount() {
this.mounted = true;
- try {
- const browser = await RNUserDefaults.get(DEFAULT_BROWSER_KEY);
- this.setState({ browser });
- } catch {
- // do nothing
- }
+ const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
+ this.setState({ browser });
}
init = () => {
@@ -117,7 +113,7 @@ class DefaultBrowserView extends React.Component {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser });
try {
const browser = newBrowser !== 'inApp' ? newBrowser : null;
- await RNUserDefaults.set(DEFAULT_BROWSER_KEY, browser);
+ await UserPreferences.setStringAsync(DEFAULT_BROWSER_KEY, browser);
this.setState({ browser });
} catch {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER_F);
diff --git a/app/views/DirectoryView/Options.js b/app/views/DirectoryView/Options.js
index 18483bda9..ab69b11f4 100644
--- a/app/views/DirectoryView/Options.js
+++ b/app/views/DirectoryView/Options.js
@@ -111,8 +111,8 @@ export default class DirectoryOptions extends PureComponent {
- {I18n.t('Search_global_users')}
- {I18n.t('Search_global_users_description')}
+ {I18n.t('Search_global_users')}
+ {I18n.t('Search_global_users_description')}
diff --git a/app/views/E2EEnterYourPasswordView.js b/app/views/E2EEnterYourPasswordView.js
new file mode 100644
index 000000000..3b607577e
--- /dev/null
+++ b/app/views/E2EEnterYourPasswordView.js
@@ -0,0 +1,98 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Text, StyleSheet, ScrollView } from 'react-native';
+import { connect } from 'react-redux';
+
+import I18n from '../i18n';
+import sharedStyles from './Styles';
+import { withTheme } from '../theme';
+import Button from '../containers/Button';
+import { themes } from '../constants/colors';
+import TextInput from '../containers/TextInput';
+import SafeAreaView from '../containers/SafeAreaView';
+import { CloseModalButton } from '../containers/HeaderButton';
+import { encryptionDecodeKey as encryptionDecodeKeyAction } from '../actions/encryption';
+import scrollPersistTaps from '../utils/scrollPersistTaps';
+import KeyboardView from '../presentation/KeyboardView';
+import StatusBar from '../containers/StatusBar';
+import { logEvent, events } from '../utils/log';
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 28
+ },
+ info: {
+ fontSize: 14,
+ marginVertical: 8,
+ ...sharedStyles.textRegular
+ }
+});
+class E2EEnterYourPasswordView extends React.Component {
+ static navigationOptions = ({ navigation }) => ({
+ headerLeft: () => ,
+ title: I18n.t('Enter_Your_E2E_Password')
+ })
+
+ static propTypes = {
+ encryptionDecodeKey: PropTypes.func,
+ theme: PropTypes.string
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ password: ''
+ };
+ }
+
+ submit = () => {
+ logEvent(events.E2E_ENTER_PW_SUBMIT);
+ const { password } = this.state;
+ const { encryptionDecodeKey } = this.props;
+ encryptionDecodeKey(password);
+ }
+
+ render() {
+ const { password } = this.state;
+ const { theme } = this.props;
+
+ return (
+
+
+
+
+ { this.passwordInput = e; }}
+ placeholder={I18n.t('Password')}
+ returnKeyType='send'
+ secureTextEntry
+ onSubmitEditing={this.submit}
+ onChangeText={value => this.setState({ password: value })}
+ testID='e2e-enter-your-password-view-password'
+ textContentType='password'
+ autoCompleteType='password'
+ theme={theme}
+ />
+
+ {I18n.t('Enter_Your_Encryption_Password_desc1')}
+ {I18n.t('Enter_Your_Encryption_Password_desc2')}
+
+
+
+ );
+ }
+}
+
+const mapDispatchToProps = dispatch => ({
+ encryptionDecodeKey: password => dispatch(encryptionDecodeKeyAction(password))
+});
+export default connect(null, mapDispatchToProps)(withTheme(E2EEnterYourPasswordView));
diff --git a/app/views/E2EHowItWorksView.js b/app/views/E2EHowItWorksView.js
new file mode 100644
index 000000000..97dc71b5e
--- /dev/null
+++ b/app/views/E2EHowItWorksView.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { StyleSheet } from 'react-native';
+
+import SafeAreaView from '../containers/SafeAreaView';
+import { themes } from '../constants/colors';
+import { CloseModalButton } from '../containers/HeaderButton';
+import Markdown from '../containers/markdown';
+import { withTheme } from '../theme';
+import I18n from '../i18n';
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 44,
+ paddingTop: 32
+ },
+ info: {
+ fontSize: 14,
+ marginVertical: 8
+ }
+});
+
+class E2EHowItWorksView extends React.Component {
+ static navigationOptions = ({ route, navigation }) => {
+ const showCloseModal = route.params?.showCloseModal;
+ return {
+ title: I18n.t('How_It_Works'),
+ headerLeft: showCloseModal ? () => : undefined
+ };
+ }
+
+ static propTypes = {
+ theme: PropTypes.string
+ }
+
+ render() {
+ const { theme } = this.props;
+
+ const infoStyle = [styles.info, { color: themes[theme].bodyText }];
+
+ return (
+
+
+
+
+
+
+ );
+ }
+}
+
+export default withTheme(E2EHowItWorksView);
diff --git a/app/views/E2ESaveYourPasswordView.js b/app/views/E2ESaveYourPasswordView.js
new file mode 100644
index 000000000..8653ec93d
--- /dev/null
+++ b/app/views/E2ESaveYourPasswordView.js
@@ -0,0 +1,172 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import {
+ Text,
+ View,
+ Clipboard,
+ ScrollView,
+ StyleSheet
+} from 'react-native';
+
+import { encryptionSetBanner as encryptionSetBannerAction } from '../actions/encryption';
+import { E2E_RANDOM_PASSWORD_KEY } from '../lib/encryption/constants';
+import { CloseModalButton } from '../containers/HeaderButton';
+import scrollPersistTaps from '../utils/scrollPersistTaps';
+import SafeAreaView from '../containers/SafeAreaView';
+import UserPreferences from '../lib/userPreferences';
+import { logEvent, events } from '../utils/log';
+import StatusBar from '../containers/StatusBar';
+import { LISTENER } from '../containers/Toast';
+import { themes } from '../constants/colors';
+import EventEmitter from '../utils/events';
+import Button from '../containers/Button';
+import { withTheme } from '../theme';
+import sharedStyles from './Styles';
+import I18n from '../i18n';
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 44,
+ paddingTop: 32
+ },
+ content: {
+ marginVertical: 68,
+ alignItems: 'center'
+ },
+ warning: {
+ fontSize: 14,
+ ...sharedStyles.textMedium
+ },
+ passwordText: {
+ marginBottom: 8,
+ ...sharedStyles.textAlignCenter
+ },
+ password: {
+ fontSize: 24,
+ marginBottom: 24,
+ ...sharedStyles.textBold
+ },
+ copyButton: {
+ width: 72,
+ height: 32
+ },
+ info: {
+ fontSize: 14,
+ marginBottom: 64,
+ ...sharedStyles.textRegular
+ }
+});
+
+class E2ESaveYourPasswordView extends React.Component {
+ static navigationOptions = ({ navigation }) => ({
+ headerLeft: () => ,
+ title: I18n.t('Save_Your_E2E_Password')
+ })
+
+ static propTypes = {
+ server: PropTypes.string,
+ navigation: PropTypes.object,
+ encryptionSetBanner: PropTypes.func,
+ theme: PropTypes.string
+ }
+
+ constructor(props) {
+ super(props);
+ this.mounted = false;
+ this.state = { password: '' };
+ this.init();
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ init = async() => {
+ const { server } = this.props;
+ try {
+ // Set stored password on local state
+ const password = await UserPreferences.getStringAsync(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`);
+ if (this.mounted) {
+ this.setState({ password });
+ } else {
+ this.state.password = password;
+ }
+ } catch {
+ // Do nothing
+ }
+ }
+
+ onSaved = async() => {
+ logEvent(events.E2E_SAVE_PW_SAVED);
+ const { navigation, server, encryptionSetBanner } = this.props;
+ // Remove stored password
+ await UserPreferences.removeItem(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`);
+ // Hide encryption banner
+ encryptionSetBanner();
+ navigation.pop();
+ }
+
+ onCopy = () => {
+ logEvent(events.E2E_SAVE_PW_COPY);
+ const { password } = this.state;
+ Clipboard.setString(password);
+ EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
+ }
+
+ onHowItWorks = () => {
+ logEvent(events.E2E_SAVE_PW_HOW_IT_WORKS);
+ const { navigation } = this.props;
+ navigation.navigate('E2EHowItWorksView');
+ }
+
+ render() {
+ const { password } = this.state;
+ const { theme } = this.props;
+
+ return (
+
+
+
+
+ {I18n.t('Save_Your_Encryption_Password_warning')}
+
+ {I18n.t('Your_password_is')}
+ {password}
+
+
+ {I18n.t('Save_Your_Encryption_Password_info')}
+
+
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = state => ({
+ server: state.server.server
+});
+const mapDispatchToProps = dispatch => ({
+ encryptionSetBanner: () => dispatch(encryptionSetBannerAction())
+});
+export default connect(mapStateToProps, mapDispatchToProps)(withTheme(E2ESaveYourPasswordView));
diff --git a/app/views/LanguageView/index.js b/app/views/LanguageView/index.js
index 0420da72f..2bc683605 100644
--- a/app/views/LanguageView/index.js
+++ b/app/views/LanguageView/index.js
@@ -4,7 +4,7 @@ import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import RocketChat from '../../lib/rocketchat';
-import I18n from '../../i18n';
+import I18n, { LANGUAGES } from '../../i18n';
import { showErrorAlert } from '../../utils/info';
import log, { logEvent, events } from '../../utils/log';
import { setUser as setUserAction } from '../../actions/login';
@@ -20,43 +20,6 @@ import { getUserSelector } from '../../selectors/login';
import database from '../../lib/database';
import SafeAreaView from '../../containers/SafeAreaView';
-const LANGUAGES = [
- {
- label: '简体中文',
- value: 'zh-CN'
- }, {
- label: 'Deutsch',
- value: 'de'
- }, {
- label: 'English',
- value: 'en'
- }, {
- label: 'Español (ES)',
- value: 'es-ES'
- }, {
- label: 'Français',
- value: 'fr'
- }, {
- label: 'Português (BR)',
- value: 'pt-BR'
- }, {
- label: 'Português (PT)',
- value: 'pt-PT'
- }, {
- label: 'Russian',
- value: 'ru'
- }, {
- label: 'Nederlands',
- value: 'nl'
- }, {
- label: 'Italiano',
- value: 'it'
- }, {
- label: '日本語',
- value: 'ja'
- }
-];
-
class LanguageView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Change_Language')
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index 95d8a90d3..f96adbeef 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -56,6 +56,7 @@ class LoginView extends React.Component {
static propTypes = {
navigation: PropTypes.object,
+ route: PropTypes.object,
Site_Name: PropTypes.string,
Accounts_RegistrationForm: PropTypes.string,
Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
@@ -74,7 +75,7 @@ class LoginView extends React.Component {
constructor(props) {
super(props);
this.state = {
- user: '',
+ user: props.route.params?.username ?? '',
password: ''
};
}
@@ -123,6 +124,7 @@ class LoginView extends React.Component {
}
renderUserForm = () => {
+ const { user } = this.state;
const {
Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, Accounts_RegistrationForm_LinkReplacementText, isFetching, theme, Accounts_ShowFormLogin
} = this.props;
@@ -146,6 +148,7 @@ class LoginView extends React.Component {
textContentType='username'
autoCompleteType='username'
theme={theme}
+ value={user}
/>
{
- const { user, baseUrl, theme } = this.props;
+ const {
+ user, baseUrl, theme, useRealName
+ } = this.props;
const renderItemCommonProps = item => ({
item,
baseUrl,
user,
author: item.u || item.user,
- ts: item.ts || item.uploadedAt,
timeFormat: 'MMM Do YYYY, h:mm:ss a',
isEdited: !!item.editedAt,
isHeader: true,
attachments: item.attachments || [],
+ useRealName,
showAttachment: this.showAttachment,
getCustomEmoji: this.getCustomEmoji,
navToRoomInfo: this.navToRoomInfo
@@ -114,6 +117,7 @@ class MessagesView extends React.Component {
item={{
...item,
u: item.user,
+ ts: item.ts || item.uploadedAt,
attachments: [{
title: item.name,
description: item.description,
@@ -311,7 +315,8 @@ class MessagesView extends React.Component {
const mapStateToProps = state => ({
baseUrl: state.server.server,
user: getUserSelector(state),
- customEmojis: state.customEmojis
+ customEmojis: state.customEmojis,
+ useRealName: state.settings.UI_Use_Real_Name
});
export default connect(mapStateToProps)(withTheme(withActionSheet(MessagesView)));
diff --git a/app/views/NewServerView/ServerInput/Item.js b/app/views/NewServerView/ServerInput/Item.js
new file mode 100644
index 000000000..f9496332a
--- /dev/null
+++ b/app/views/NewServerView/ServerInput/Item.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import {
+ View, StyleSheet, Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+import { BorderlessButton } from 'react-native-gesture-handler';
+
+import { themes } from '../../../constants/colors';
+import { CustomIcon } from '../../../lib/Icons';
+import sharedStyles from '../../Styles';
+import Touch from '../../../utils/touch';
+
+const styles = StyleSheet.create({
+ container: {
+ height: 56,
+ paddingHorizontal: 15,
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ content: {
+ flex: 1,
+ flexDirection: 'column'
+ },
+ server: {
+ ...sharedStyles.textMedium,
+ fontSize: 16
+ }
+});
+
+const Item = ({
+ item, theme, onPress, onDelete
+}) => (
+ onPress(item.url)} theme={theme} testID={`server-history-${ item.url }`}>
+
+ {item.url}
+ {item.username}
+
+ onDelete(item)} testID={`server-history-delete-${ item.url }`}>
+
+
+
+);
+
+Item.propTypes = {
+ item: PropTypes.object,
+ theme: PropTypes.string,
+ onPress: PropTypes.func,
+ onDelete: PropTypes.func
+};
+
+export default Item;
diff --git a/app/views/NewServerView/ServerInput/index.js b/app/views/NewServerView/ServerInput/index.js
new file mode 100644
index 000000000..73e85b910
--- /dev/null
+++ b/app/views/NewServerView/ServerInput/index.js
@@ -0,0 +1,87 @@
+import React, { useState } from 'react';
+import { View, FlatList, StyleSheet } from 'react-native';
+import PropTypes from 'prop-types';
+
+import TextInput from '../../../containers/TextInput';
+import { themes } from '../../../constants/colors';
+import Item from './Item';
+import Separator from '../../../containers/Separator';
+
+const styles = StyleSheet.create({
+ container: {
+ zIndex: 1,
+ marginTop: 24,
+ marginBottom: 32
+ },
+ inputContainer: {
+ marginTop: 0,
+ marginBottom: 0
+ },
+ serverHistory: {
+ maxHeight: 180,
+ width: '100%',
+ top: '100%',
+ zIndex: 1,
+ position: 'absolute',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ borderTopWidth: 0
+ }
+});
+
+const ServerInput = ({
+ text,
+ theme,
+ serversHistory,
+ onChangeText,
+ onSubmit,
+ onDelete,
+ onPressServerHistory
+}) => {
+ const [focused, setFocused] = useState(false);
+ return (
+
+ setFocused(true)}
+ onBlur={() => setFocused(false)}
+ />
+ {
+ focused && serversHistory?.length
+ ? (
+
+ - onPressServerHistory(item)} onDelete={onDelete} />}
+ ItemSeparatorComponent={() => }
+ keyExtractor={item => item.id}
+ />
+
+ ) : null
+ }
+
+ );
+};
+
+ServerInput.propTypes = {
+ text: PropTypes.string,
+ theme: PropTypes.string,
+ serversHistory: PropTypes.array,
+ onChangeText: PropTypes.func,
+ onSubmit: PropTypes.func,
+ onDelete: PropTypes.func,
+ onPressServerHistory: PropTypes.func
+};
+
+export default ServerInput;
diff --git a/app/views/NewServerView.js b/app/views/NewServerView/index.js
similarity index 69%
rename from app/views/NewServerView.js
rename to app/views/NewServerView/index.js
index ec1229393..5d5e47044 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView/index.js
@@ -1,42 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- Text, Keyboard, StyleSheet, TouchableOpacity, View, Alert, BackHandler
+ Text, Keyboard, StyleSheet, View, Alert, BackHandler
} from 'react-native';
import { connect } from 'react-redux';
import * as FileSystem from 'expo-file-system';
import DocumentPicker from 'react-native-document-picker';
-import RNUserDefaults from 'rn-user-defaults';
import { Base64 } from 'js-base64';
import parse from 'url-parse';
+import { Q } from '@nozbe/watermelondb';
-import EventEmitter from '../utils/events';
-import { selectServerRequest, serverRequest } from '../actions/server';
-import { inviteLinksClear as inviteLinksClearAction } from '../actions/inviteLinks';
-import sharedStyles from './Styles';
-import Button from '../containers/Button';
-import TextInput from '../containers/TextInput';
-import OrSeparator from '../containers/OrSeparator';
-import FormContainer, { FormContainerInner } from '../containers/FormContainer';
-import I18n from '../i18n';
-import { isIOS } from '../utils/deviceInfo';
-import { themes } from '../constants/colors';
-import log, { logEvent, events } from '../utils/log';
-import { animateNextTransition } from '../utils/layoutAnimation';
-import { withTheme } from '../theme';
-import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';
-import { CloseModalButton } from '../containers/HeaderButton';
-import { showConfirmationAlert } from '../utils/info';
+import { TouchableOpacity } from 'react-native-gesture-handler';
+import UserPreferences from '../../lib/userPreferences';
+import EventEmitter from '../../utils/events';
+import { selectServerRequest, serverRequest } from '../../actions/server';
+import { inviteLinksClear as inviteLinksClearAction } from '../../actions/inviteLinks';
+import sharedStyles from '../Styles';
+import Button from '../../containers/Button';
+import OrSeparator from '../../containers/OrSeparator';
+import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
+import I18n from '../../i18n';
+import { isIOS } from '../../utils/deviceInfo';
+import { themes } from '../../constants/colors';
+import log, { logEvent, events } from '../../utils/log';
+import { animateNextTransition } from '../../utils/layoutAnimation';
+import { withTheme } from '../../theme';
+import { setBasicAuth, BASIC_AUTH_KEY } from '../../utils/fetch';
+import { CloseModalButton } from '../../containers/HeaderButton';
+import { showConfirmationAlert } from '../../utils/info';
+import database from '../../lib/database';
+import ServerInput from './ServerInput';
+import { sanitizeLikeString } from '../../lib/database/utils';
const styles = StyleSheet.create({
title: {
...sharedStyles.textBold,
fontSize: 22
},
- inputContainer: {
- marginTop: 24,
- marginBottom: 32
- },
certificatePicker: {
marginBottom: 32,
alignItems: 'center',
@@ -84,12 +84,17 @@ class NewServerView extends React.Component {
this.state = {
text: '',
connectingOpen: false,
- certificate: null
+ certificate: null,
+ serversHistory: []
};
EventEmitter.addEventListener('NewServer', this.handleNewServerEvent);
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
+ componentDidMount() {
+ this.queryServerHistory();
+ }
+
componentDidUpdate(prevProps) {
const { adding } = this.props;
if (prevProps.adding !== adding) {
@@ -122,6 +127,30 @@ class NewServerView extends React.Component {
onChangeText = (text) => {
this.setState({ text });
+ this.queryServerHistory(text);
+ }
+
+ queryServerHistory = async(text) => {
+ const db = database.servers;
+ try {
+ const serversHistoryCollection = db.collections.get('servers_history');
+ let whereClause = [
+ Q.where('username', Q.notEq(null)),
+ Q.experimentalSortBy('updated_at', Q.desc),
+ Q.experimentalTake(3)
+ ];
+ const likeString = sanitizeLikeString(text);
+ if (text) {
+ whereClause = [
+ ...whereClause,
+ Q.where('url', Q.like(`%${ likeString }%`))
+ ];
+ }
+ const serversHistory = await serversHistoryCollection.query(...whereClause).fetch();
+ this.setState({ serversHistory });
+ } catch {
+ // Do nothing
+ }
}
close = () => {
@@ -138,7 +167,11 @@ class NewServerView extends React.Component {
connectServer(server);
}
- submit = async() => {
+ onPressServerHistory = (serverHistory) => {
+ this.setState({ text: serverHistory?.url }, () => this.submit({ fromServerHistory: true, username: serverHistory?.username }));
+ }
+
+ submit = async({ fromServerHistory = false, username }) => {
logEvent(events.NEWSERVER_CONNECT_TO_WORKSPACE);
const { text, certificate } = this.state;
const { connectServer } = this.props;
@@ -164,7 +197,11 @@ class NewServerView extends React.Component {
Keyboard.dismiss();
const server = this.completeUrl(text);
await this.basicAuth(server, text);
- connectServer(server, cert);
+ if (fromServerHistory) {
+ connectServer(server, cert, username, true);
+ } else {
+ connectServer(server, cert);
+ }
}
}
@@ -180,7 +217,7 @@ class NewServerView extends React.Component {
const parsedUrl = parse(text, true);
if (parsedUrl.auth.length) {
const credentials = Base64.encode(parsedUrl.auth);
- await RNUserDefaults.set(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
+ await UserPreferences.setStringAsync(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
setBasicAuth(credentials);
}
} catch {
@@ -246,11 +283,24 @@ class NewServerView extends React.Component {
handleRemove = () => {
showConfirmationAlert({
message: I18n.t('You_will_unset_a_certificate_for_this_server'),
- callToAction: I18n.t('Remove'),
+ confirmationText: I18n.t('Remove'),
onPress: this.setState({ certificate: null }) // We not need delete file from DocumentPicker because it is a temp file
});
}
+ deleteServerHistory = async(item) => {
+ const { serversHistory } = this.state;
+ const db = database.servers;
+ try {
+ await db.action(async() => {
+ await item.destroyPermanently();
+ });
+ this.setState({ serversHistory: serversHistory.filter(server => server.id !== item.id) });
+ } catch {
+ // Nothing
+ }
+ }
+
renderCertificatePicker = () => {
const { certificate } = this.state;
const { theme } = this.props;
@@ -283,24 +333,25 @@ class NewServerView extends React.Component {
render() {
const { connecting, theme } = this.props;
- const { text, connectingOpen } = this.state;
+ const {
+ text, connectingOpen, serversHistory
+ } = this.state;
return (
-
+
{I18n.t('Join_your_workspace')}
-
- { isIOS ? this.renderCertificatePicker() : null }
+ {isIOS ? this.renderCertificatePicker() : null}
);
}
@@ -338,7 +389,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
- connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)),
+ connectServer: (server, certificate, username, fromServerHistory) => dispatch(serverRequest(server, certificate, username, fromServerHistory)),
selectServer: server => dispatch(selectServerRequest(server)),
inviteLinksClear: () => dispatch(inviteLinksClearAction())
});
diff --git a/app/views/NotificationPreferencesView/Info.js b/app/views/NotificationPreferencesView/Info.js
new file mode 100644
index 000000000..0c01e3c16
--- /dev/null
+++ b/app/views/NotificationPreferencesView/Info.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const Info = React.memo(({ info, theme }) => (
+
+ {info}
+
+));
+
+Info.propTypes = {
+ info: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default Info;
diff --git a/app/views/NotificationPreferencesView/SectionSeparator.js b/app/views/NotificationPreferencesView/SectionSeparator.js
new file mode 100644
index 000000000..88acd1a0e
--- /dev/null
+++ b/app/views/NotificationPreferencesView/SectionSeparator.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import {
+ View
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const SectionSeparator = React.memo(({ theme }) => (
+
+));
+
+SectionSeparator.propTypes = {
+ theme: PropTypes.string
+};
+
+export default SectionSeparator;
diff --git a/app/views/NotificationPreferencesView/SectionTitle.js b/app/views/NotificationPreferencesView/SectionTitle.js
new file mode 100644
index 000000000..e93cb0e01
--- /dev/null
+++ b/app/views/NotificationPreferencesView/SectionTitle.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const SectionTitle = React.memo(({ title, theme }) => (
+
+ {title}
+
+));
+
+SectionTitle.propTypes = {
+ title: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default SectionTitle;
diff --git a/app/views/NotificationPreferencesView/index.js b/app/views/NotificationPreferencesView/index.js
index 4fad41839..1cbc23db3 100644
--- a/app/views/NotificationPreferencesView/index.js
+++ b/app/views/NotificationPreferencesView/index.js
@@ -17,126 +17,10 @@ import { withTheme } from '../../theme';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import SafeAreaView from '../../containers/SafeAreaView';
import log, { events, logEvent } from '../../utils/log';
-
-const SectionTitle = React.memo(({ title, theme }) => (
-
- {title}
-
-));
-
-const SectionSeparator = React.memo(({ theme }) => (
-
-));
-
-const Info = React.memo(({ info, theme }) => (
-
- {info}
-
-));
-
-SectionTitle.propTypes = {
- title: PropTypes.string,
- theme: PropTypes.string
-};
-
-SectionSeparator.propTypes = {
- theme: PropTypes.string
-};
-
-Info.propTypes = {
- info: PropTypes.string,
- theme: PropTypes.string
-};
-
-const OPTIONS = {
- desktopNotifications: [{
- label: 'Default', value: 'default'
- }, {
- label: 'All_Messages', value: 'all'
- }, {
- label: 'Mentions', value: 'mentions'
- }, {
- label: 'Nothing', value: 'nothing'
- }],
- audioNotifications: [{
- label: 'Default', value: 'default'
- }, {
- label: 'All_Messages', value: 'all'
- }, {
- label: 'Mentions', value: 'mentions'
- }, {
- label: 'Nothing', value: 'nothing'
- }],
- mobilePushNotifications: [{
- label: 'Default', value: 'default'
- }, {
- label: 'All_Messages', value: 'all'
- }, {
- label: 'Mentions', value: 'mentions'
- }, {
- label: 'Nothing', value: 'nothing'
- }],
- emailNotifications: [{
- label: 'Default', value: 'default'
- }, {
- label: 'All_Messages', value: 'all'
- }, {
- label: 'Mentions', value: 'mentions'
- }, {
- label: 'Nothing', value: 'nothing'
- }],
- desktopNotificationDuration: [{
- label: 'Default', value: 0
- }, {
- label: 'Seconds', second: 1, value: 1
- }, {
- label: 'Seconds', second: 2, value: 2
- }, {
- label: 'Seconds', second: 3, value: 3
- }, {
- label: 'Seconds', second: 4, value: 4
- }, {
- label: 'Seconds', second: 5, value: 5
- }],
- audioNotificationValue: [{
- label: 'None', value: 'none None'
- }, {
- label: 'Default', value: '0 Default'
- }, {
- label: 'Beep', value: 'beep Beep'
- }, {
- label: 'Ding', value: 'ding Ding'
- }, {
- label: 'Chelle', value: 'chelle Chelle'
- }, {
- label: 'Droplet', value: 'droplet Droplet'
- }, {
- label: 'Highbell', value: 'highbell Highbell'
- }, {
- label: 'Seasons', value: 'seasons Seasons'
- }]
-};
+import SectionTitle from './SectionTitle';
+import SectionSeparator from './SectionSeparator';
+import Info from './Info';
+import { OPTIONS } from './options';
class NotificationPreferencesView extends React.Component {
static navigationOptions = () => ({
diff --git a/app/views/NotificationPreferencesView/options.js b/app/views/NotificationPreferencesView/options.js
new file mode 100644
index 000000000..7c572718f
--- /dev/null
+++ b/app/views/NotificationPreferencesView/options.js
@@ -0,0 +1,68 @@
+export const OPTIONS = {
+ desktopNotifications: [{
+ label: 'Default', value: 'default'
+ }, {
+ label: 'All_Messages', value: 'all'
+ }, {
+ label: 'Mentions', value: 'mentions'
+ }, {
+ label: 'Nothing', value: 'nothing'
+ }],
+ audioNotifications: [{
+ label: 'Default', value: 'default'
+ }, {
+ label: 'All_Messages', value: 'all'
+ }, {
+ label: 'Mentions', value: 'mentions'
+ }, {
+ label: 'Nothing', value: 'nothing'
+ }],
+ mobilePushNotifications: [{
+ label: 'Default', value: 'default'
+ }, {
+ label: 'All_Messages', value: 'all'
+ }, {
+ label: 'Mentions', value: 'mentions'
+ }, {
+ label: 'Nothing', value: 'nothing'
+ }],
+ emailNotifications: [{
+ label: 'Default', value: 'default'
+ }, {
+ label: 'All_Messages', value: 'all'
+ }, {
+ label: 'Mentions', value: 'mentions'
+ }, {
+ label: 'Nothing', value: 'nothing'
+ }],
+ desktopNotificationDuration: [{
+ label: 'Default', value: 0
+ }, {
+ label: 'Seconds', second: 1, value: 1
+ }, {
+ label: 'Seconds', second: 2, value: 2
+ }, {
+ label: 'Seconds', second: 3, value: 3
+ }, {
+ label: 'Seconds', second: 4, value: 4
+ }, {
+ label: 'Seconds', second: 5, value: 5
+ }],
+ audioNotificationValue: [{
+ label: 'None', value: 'none None'
+ }, {
+ label: 'Default', value: '0 Default'
+ }, {
+ label: 'Beep', value: 'beep Beep'
+ }, {
+ label: 'Ding', value: 'ding Ding'
+ }, {
+ label: 'Chelle', value: 'chelle Chelle'
+ }, {
+ label: 'Droplet', value: 'droplet Droplet'
+ }, {
+ label: 'Highbell', value: 'highbell Highbell'
+ }, {
+ label: 'Seasons', value: 'seasons Seasons'
+ }]
+};
diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js
index 8d289935b..7c96c5280 100644
--- a/app/views/ProfileView/index.js
+++ b/app/views/ProfileView/index.js
@@ -13,7 +13,7 @@ import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles';
import styles from './styles';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
-import { showErrorAlert } from '../../utils/info';
+import { showErrorAlert, showConfirmationAlert } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import RocketChat from '../../lib/rocketchat';
@@ -24,7 +24,7 @@ import Button from '../../containers/Button';
import Avatar from '../../containers/Avatar';
import { setUser as setUserAction } from '../../actions/login';
import { CustomIcon } from '../../lib/Icons';
-import { DrawerButton } from '../../containers/HeaderButton';
+import { DrawerButton, PreferencesButton } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
@@ -39,6 +39,9 @@ class ProfileView extends React.Component {
if (!isMasterDetail) {
options.headerLeft = () => ;
}
+ options.headerRight = () => (
+ navigation.navigate('UserPreferencesView')} testID='preferences-view-open' />
+ );
return options;
}
@@ -423,6 +426,23 @@ class ProfileView extends React.Component {
}
}
+ logoutOtherLocations = () => {
+ logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS);
+ showConfirmationAlert({
+ message: I18n.t('You_will_be_logged_out_from_other_locations'),
+ callToAction: I18n.t('Logout'),
+ onPress: async() => {
+ try {
+ await RocketChat.logoutOtherLocations();
+ EventEmitter.emit(LISTENER, { message: I18n.t('Logged_out_of_other_clients_successfully') });
+ } catch {
+ logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS_F);
+ EventEmitter.emit(LISTENER, { message: I18n.t('Logout_failed') });
+ }
+ }
+ });
+ }
+
render() {
const {
name, username, email, newPassword, avatarUrl, customFields, avatar, saving
@@ -549,6 +569,14 @@ class ProfileView extends React.Component {
loading={saving}
theme={theme}
/>
+
diff --git a/app/views/ReadReceiptView/index.js b/app/views/ReadReceiptView/index.js
index 9ec188d0d..9cb916ed8 100644
--- a/app/views/ReadReceiptView/index.js
+++ b/app/views/ReadReceiptView/index.js
@@ -100,6 +100,9 @@ class ReadReceiptView extends React.Component {
Message_TimeFormat, user: { id: userId, token }, baseUrl, theme
} = this.props;
const time = moment(item.ts).format(Message_TimeFormat);
+ if (!item?.user?.username) {
+ return null;
+ }
return (
- {item.user.name}
+ {item?.user?.name}
{time}
@@ -142,25 +145,23 @@ class ReadReceiptView extends React.Component {
return (
-
- {loading
- ?
- : (
- item._id}
- />
- )}
-
+ {loading
+ ?
+ : (
+ item._id}
+ />
+ )}
);
}
diff --git a/app/views/ReadReceiptView/styles.js b/app/views/ReadReceiptView/styles.js
index c93754294..828e814c0 100644
--- a/app/views/ReadReceiptView/styles.js
+++ b/app/views/ReadReceiptView/styles.js
@@ -29,7 +29,6 @@ export default StyleSheet.create({
padding: 10
},
list: {
- ...sharedStyles.separatorVertical,
- marginVertical: 10
+ ...sharedStyles.separatorVertical
}
});
diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js
index 13070fb49..3e281691d 100644
--- a/app/views/RoomActionsView/index.js
+++ b/app/views/RoomActionsView/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- View, SectionList, Text, Alert, Share
+ View, SectionList, Text, Alert, Share, Switch
} from 'react-native';
import { connect } from 'react-redux';
import _ from 'lodash';
@@ -21,13 +21,16 @@ import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { CustomIcon } from '../../lib/Icons';
import DisclosureIndicator from '../../containers/DisclosureIndicator';
import StatusBar from '../../containers/StatusBar';
-import { themes } from '../../constants/colors';
+import { themes, SWITCH_TRACK_COLOR } from '../../constants/colors';
import { withTheme } from '../../theme';
import { CloseModalButton } from '../../containers/HeaderButton';
import { getUserSelector } from '../../selectors/login';
import Markdown from '../../containers/markdown';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import SafeAreaView from '../../containers/SafeAreaView';
+import { E2E_ROOM_TYPES } from '../../lib/encryption/constants';
+import protectedFunction from '../../lib/methods/helpers/protectedFunction';
+import database from '../../lib/database';
class RoomActionsView extends React.Component {
static navigationOptions = ({ navigation, isMasterDetail }) => {
@@ -50,6 +53,7 @@ class RoomActionsView extends React.Component {
}),
leaveRoom: PropTypes.func,
jitsiEnabled: PropTypes.bool,
+ e2eEnabled: PropTypes.bool,
setLoadingInvite: PropTypes.func,
closeRoom: PropTypes.func,
theme: PropTypes.string
@@ -72,7 +76,8 @@ class RoomActionsView extends React.Component {
canAddUser: false,
canInviteUser: false,
canForwardGuest: false,
- canReturnQueue: false
+ canReturnQueue: false,
+ canEdit: false
};
if (room && room.observe && room.rid) {
this.roomObservable = room.observe();
@@ -120,6 +125,7 @@ class RoomActionsView extends React.Component {
this.canAddUser();
this.canInviteUser();
+ this.canEdit();
// livechat permissions
if (room.t === 'l') {
@@ -184,6 +190,15 @@ class RoomActionsView extends React.Component {
this.setState({ canInviteUser });
}
+ canEdit = async() => {
+ const { room } = this.state;
+ const { rid } = room;
+ const permissions = await RocketChat.hasPermission(['edit-room'], rid);
+
+ const canEdit = permissions && permissions['edit-room'];
+ this.setState({ canEdit });
+ }
+
canViewMembers = async() => {
const { room } = this.state;
const { rid, t, broadcast } = room;
@@ -227,11 +242,11 @@ class RoomActionsView extends React.Component {
get sections() {
const {
- room, member, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue
+ room, member, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue, canEdit
} = this.state;
- const { jitsiEnabled } = this.props;
+ const { jitsiEnabled, e2eEnabled } = this.props;
const {
- rid, t, blocker
+ rid, t, blocker, encrypted
} = room;
const isGroupChat = RocketChat.isGroupChat(room);
@@ -240,7 +255,8 @@ class RoomActionsView extends React.Component {
name: I18n.t('Notifications'),
route: 'NotificationPrefView',
params: { rid, room },
- testID: 'room-actions-notifications'
+ testID: 'room-actions-notifications',
+ right: this.renderDisclosure
};
const jitsiActions = jitsiEnabled ? [
@@ -248,13 +264,15 @@ class RoomActionsView extends React.Component {
icon: 'phone',
name: I18n.t('Voice_call'),
event: () => RocketChat.callJitsi(rid, true),
- testID: 'room-actions-voice'
+ testID: 'room-actions-voice',
+ right: this.renderDisclosure
},
{
icon: 'camera',
name: I18n.t('Video_call'),
event: () => RocketChat.callJitsi(rid),
- testID: 'room-actions-video'
+ testID: 'room-actions-video',
+ right: this.renderDisclosure
}
] : [];
@@ -281,41 +299,47 @@ class RoomActionsView extends React.Component {
name: I18n.t('Files'),
route: 'MessagesView',
params: { rid, t, name: 'Files' },
- testID: 'room-actions-files'
+ testID: 'room-actions-files',
+ right: this.renderDisclosure
},
{
icon: 'mention',
name: I18n.t('Mentions'),
route: 'MessagesView',
params: { rid, t, name: 'Mentions' },
- testID: 'room-actions-mentioned'
+ testID: 'room-actions-mentioned',
+ right: this.renderDisclosure
},
{
icon: 'star',
name: I18n.t('Starred'),
route: 'MessagesView',
params: { rid, t, name: 'Starred' },
- testID: 'room-actions-starred'
+ testID: 'room-actions-starred',
+ right: this.renderDisclosure
},
{
icon: 'search',
name: I18n.t('Search'),
route: 'SearchMessagesView',
- params: { rid },
- testID: 'room-actions-search'
+ params: { rid, encrypted },
+ testID: 'room-actions-search',
+ right: this.renderDisclosure
},
{
icon: 'share',
name: I18n.t('Share'),
event: this.handleShare,
- testID: 'room-actions-share'
+ testID: 'room-actions-share',
+ right: this.renderDisclosure
},
{
icon: 'pin',
name: I18n.t('Pinned'),
route: 'MessagesView',
params: { rid, t, name: 'Pinned' },
- testID: 'room-actions-pinned'
+ testID: 'room-actions-pinned',
+ right: this.renderDisclosure
}
],
renderItem: this.renderItem
@@ -327,7 +351,8 @@ class RoomActionsView extends React.Component {
name: I18n.t('Auto_Translate'),
route: 'AutoTranslateView',
params: { rid, room },
- testID: 'room-actions-auto-translate'
+ testID: 'room-actions-auto-translate',
+ right: this.renderDisclosure
});
}
@@ -338,7 +363,8 @@ class RoomActionsView extends React.Component {
description: membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null,
route: 'RoomMembersView',
params: { rid, room },
- testID: 'room-actions-members'
+ testID: 'room-actions-members',
+ right: this.renderDisclosure
});
}
@@ -350,7 +376,8 @@ class RoomActionsView extends React.Component {
name: I18n.t(`${ blocker ? 'Unblock' : 'Block' }_user`),
type: 'danger',
event: this.toggleBlockUser,
- testID: 'room-actions-block-user'
+ testID: 'room-actions-block-user',
+ right: this.renderDisclosure
}
],
renderItem: this.renderItem
@@ -366,7 +393,8 @@ class RoomActionsView extends React.Component {
description: membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null,
route: 'RoomMembersView',
params: { rid, room },
- testID: 'room-actions-members'
+ testID: 'room-actions-members',
+ right: this.renderDisclosure
});
}
@@ -380,7 +408,8 @@ class RoomActionsView extends React.Component {
title: I18n.t('Add_users'),
nextAction: this.addUser
},
- testID: 'room-actions-add-user'
+ testID: 'room-actions-add-user',
+ right: this.renderDisclosure
});
}
if (canInviteUser) {
@@ -391,7 +420,8 @@ class RoomActionsView extends React.Component {
params: {
rid
},
- testID: 'room-actions-invite-user'
+ testID: 'room-actions-invite-user',
+ right: this.renderDisclosure
});
}
sections[2].data = [...actions, ...sections[2].data];
@@ -405,7 +435,8 @@ class RoomActionsView extends React.Component {
name: I18n.t('Leave_channel'),
type: 'danger',
event: this.leaveChannel,
- testID: 'room-actions-leave-channel'
+ testID: 'room-actions-leave-channel',
+ right: this.renderDisclosure
}
],
renderItem: this.renderItem
@@ -418,7 +449,8 @@ class RoomActionsView extends React.Component {
sections[2].data.push({
icon: 'close',
name: I18n.t('Close'),
- event: this.closeLivechat
+ event: this.closeLivechat,
+ right: this.renderDisclosure
});
if (canForwardGuest) {
@@ -426,7 +458,8 @@ class RoomActionsView extends React.Component {
icon: 'user-forward',
name: I18n.t('Forward'),
route: 'ForwardLivechatView',
- params: { rid }
+ params: { rid },
+ right: this.renderDisclosure
});
}
@@ -434,7 +467,8 @@ class RoomActionsView extends React.Component {
sections[2].data.push({
icon: 'undo',
name: I18n.t('Return'),
- event: this.returnLivechat
+ event: this.returnLivechat,
+ right: this.renderDisclosure
});
}
@@ -442,7 +476,8 @@ class RoomActionsView extends React.Component {
icon: 'history',
name: I18n.t('Navigation_history'),
route: 'VisitorNavigationView',
- params: { rid }
+ params: { rid },
+ right: this.renderDisclosure
});
}
@@ -452,14 +487,47 @@ class RoomActionsView extends React.Component {
});
}
+ // If can edit this room
+ // If this room type can be Encrypted
+ // If e2e is enabled for this server
+ if (canEdit && E2E_ROOM_TYPES[t] && e2eEnabled) {
+ sections.splice(2, 0, {
+ data: [{
+ icon: 'encrypted',
+ name: I18n.t('Encrypted'),
+ testID: 'room-actions-encrypt',
+ right: this.renderEncryptedSwitch
+ }],
+ renderItem: this.renderItem
+ });
+ }
+
return sections;
}
+ renderDisclosure = () => {
+ const { theme } = this.props;
+ return ;
+ }
+
renderSeparator = () => {
const { theme } = this.props;
return ;
}
+ renderEncryptedSwitch = () => {
+ const { room } = this.state;
+ const { encrypted } = room;
+ return (
+
+ );
+ }
+
closeLivechat = () => {
const { room: { rid } } = this.state;
const { closeRoom } = this.props;
@@ -471,7 +539,7 @@ class RoomActionsView extends React.Component {
const { room: { rid } } = this.state;
showConfirmationAlert({
message: I18n.t('Would_you_like_to_return_the_inquiry'),
- callToAction: I18n.t('Yes'),
+ confirmationText: I18n.t('Yes'),
onPress: async() => {
try {
await RocketChat.returnLivechat(rid);
@@ -514,19 +582,58 @@ class RoomActionsView extends React.Component {
}
}
- toggleBlockUser = () => {
+ toggleBlockUser = async() => {
logEvent(events.RA_TOGGLE_BLOCK_USER);
const { room } = this.state;
const { rid, blocker } = room;
const { member } = this.state;
try {
- RocketChat.toggleBlockUser(rid, member._id, !blocker);
+ await RocketChat.toggleBlockUser(rid, member._id, !blocker);
} catch (e) {
logEvent(events.RA_TOGGLE_BLOCK_USER_F);
log(e);
}
}
+ toggleEncrypted = async() => {
+ logEvent(events.RA_TOGGLE_ENCRYPTED);
+ const { room } = this.state;
+ const { rid } = room;
+ const db = database.active;
+
+ // Toggle encrypted value
+ const encrypted = !room.encrypted;
+ try {
+ // Instantly feedback to the user
+ await db.action(async() => {
+ await room.update(protectedFunction((r) => {
+ r.encrypted = encrypted;
+ }));
+ });
+
+ try {
+ // Send new room setting value to server
+ const { result } = await RocketChat.saveRoomSettings(rid, { encrypted });
+ // If it was saved successfully
+ if (result) {
+ return;
+ }
+ } catch {
+ // do nothing
+ }
+
+ // If something goes wrong we go back to the previous value
+ await db.action(async() => {
+ await room.update(protectedFunction((r) => {
+ r.encrypted = room.encrypted;
+ }));
+ });
+ } catch (e) {
+ logEvent(events.RA_TOGGLE_ENCRYPTED_F);
+ log(e);
+ }
+ }
+
handleShare = () => {
logEvent(events.RA_SHARE);
const { room } = this.state;
@@ -638,7 +745,7 @@ class RoomActionsView extends React.Component {
{ item.name }
{item.description ? { item.description } : null}
-
+ {item?.right?.()}
>
);
return this.renderTouchableItem(subview, item);
@@ -679,7 +786,8 @@ class RoomActionsView extends React.Component {
const mapStateToProps = state => ({
user: getUserSelector(state),
baseUrl: state.server.server,
- jitsiEnabled: state.settings.Jitsi_Enabled || false
+ jitsiEnabled: state.settings.Jitsi_Enabled || false,
+ e2eEnabled: state.settings.E2E_Enable || false
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomActionsView/styles.js b/app/views/RoomActionsView/styles.js
index 93079c1d8..56d2b5366 100644
--- a/app/views/RoomActionsView/styles.js
+++ b/app/views/RoomActionsView/styles.js
@@ -58,5 +58,8 @@ export default StyleSheet.create({
paddingRight: 16,
flexDirection: 'row',
alignItems: 'center'
+ },
+ encryptedSwitch: {
+ marginHorizontal: 16
}
});
diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js
index 4d913e1cd..8b4ffd6a8 100644
--- a/app/views/RoomInfoEditView/index.js
+++ b/app/views/RoomInfoEditView/index.js
@@ -56,6 +56,7 @@ class RoomInfoEditView extends React.Component {
route: PropTypes.object,
deleteRoom: PropTypes.func,
serverVersion: PropTypes.string,
+ e2eEnabled: PropTypes.bool,
theme: PropTypes.string
};
@@ -76,7 +77,8 @@ class RoomInfoEditView extends React.Component {
reactWhenReadOnly: false,
archived: false,
systemMessages: [],
- enableSysMes: false
+ enableSysMes: false,
+ encrypted: false
};
this.loadRoom();
}
@@ -123,7 +125,7 @@ class RoomInfoEditView extends React.Component {
init = (room) => {
const {
- description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired, sysMes
+ description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired, sysMes, encrypted
} = room;
// fake password just to user knows about it
this.randomValue = random(15);
@@ -139,7 +141,8 @@ class RoomInfoEditView extends React.Component {
joinCode: joinCodeRequired ? this.randomValue : '',
archived: room.archived,
systemMessages: sysMes,
- enableSysMes: sysMes && sysMes.length > 0
+ enableSysMes: sysMes && sysMes.length > 0,
+ encrypted
});
}
@@ -157,7 +160,7 @@ class RoomInfoEditView extends React.Component {
formIsChanged = () => {
const {
- room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages, enableSysMes
+ room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages, enableSysMes, encrypted
} = this.state;
const { joinCodeRequired } = room;
return !(room.name === name
@@ -170,6 +173,7 @@ class RoomInfoEditView extends React.Component {
&& room.reactWhenReadOnly === reactWhenReadOnly
&& isEqual(room.sysMes, systemMessages)
&& enableSysMes === (room.sysMes && room.sysMes.length > 0)
+ && room.encrypted === encrypted
);
}
@@ -177,7 +181,7 @@ class RoomInfoEditView extends React.Component {
logEvent(events.RI_EDIT_SAVE);
Keyboard.dismiss();
const {
- room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages
+ room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages, encrypted
} = this.state;
this.setState({ saving: true });
@@ -231,6 +235,11 @@ class RoomInfoEditView extends React.Component {
params.joinCode = joinCode;
}
+ // Encrypted
+ if (room.encrypted !== encrypted) {
+ params.encrypted = encrypted;
+ }
+
try {
await RocketChat.saveRoomSettings(room.rid, params);
} catch (e) {
@@ -340,7 +349,7 @@ class RoomInfoEditView extends React.Component {
toggleRoomType = (value) => {
logEvent(events.RI_EDIT_TOGGLE_ROOM_TYPE);
- this.setState({ t: value });
+ this.setState(({ encrypted }) => ({ t: value, encrypted: value && encrypted }));
}
toggleReadOnly = (value) => {
@@ -358,11 +367,16 @@ class RoomInfoEditView extends React.Component {
this.setState(({ systemMessages }) => ({ enableSysMes: value, systemMessages: value ? systemMessages : [] }));
}
+ toggleEncrypted = (value) => {
+ logEvent(events.RI_EDIT_TOGGLE_ENCRYPTED);
+ this.setState({ encrypted: value });
+ }
+
render() {
const {
- name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes
+ name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes, encrypted
} = this.state;
- const { serverVersion, theme } = this.props;
+ const { serverVersion, e2eEnabled, theme } = this.props;
const { dangerColor } = themes[theme];
return (
@@ -487,6 +501,19 @@ class RoomInfoEditView extends React.Component {
{this.renderSystemMessages()}
) : null}
+ {e2eEnabled ? (
+
+ ) : null}
({
- serverVersion: state.server.version
+ serverVersion: state.server.version,
+ e2eEnabled: state.settings.E2E_Enable || false
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js
index cb6c7b7d4..e7829dbdb 100644
--- a/app/views/RoomMembersView/index.js
+++ b/app/views/RoomMembersView/index.js
@@ -153,7 +153,7 @@ class RoomMembersView extends React.Component {
message: I18n.t(`The_user_${ userIsMuted ? 'will' : 'wont' }_be_able_to_type_in_roomName`, {
roomName: RocketChat.getRoomTitle(room)
}),
- callToAction: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
+ confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
onPress: () => this.handleMute(user)
});
}
diff --git a/app/views/RoomView/Header/Header.js b/app/views/RoomView/Header/Header.js
index 9022514ab..6acc6646e 100644
--- a/app/views/RoomView/Header/Header.js
+++ b/app/views/RoomView/Header/Header.js
@@ -129,6 +129,7 @@ const Header = React.memo(({
return (
{
+ logEvent(events.ROOM_ENCRYPTED_PRESS);
+ const { navigation, isMasterDetail } = this.props;
+
+ const screen = { screen: 'E2EHowItWorksView', params: { showCloseModal: true } };
+
+ if (isMasterDetail) {
+ return navigation.navigate('ModalStackNavigator', screen);
+ }
+ navigation.navigate('E2ESaveYourPasswordStackNavigator', screen);
+ }
+
onDiscussionPress = debounce((item) => {
const { navigation } = this.props;
navigation.push('RoomView', {
@@ -614,8 +630,12 @@ class RoomView extends React.Component {
if (!item.tmsg) {
await this.fetchThreadName(item.tmid, item.id);
}
+ let name = item.tmsg;
+ if (item.t === E2E_MESSAGE_TYPE && item.e2e !== E2E_STATUS.DONE) {
+ name = I18n.t('Encrypted_message');
+ }
navigation.push('RoomView', {
- rid: item.subscription.id, tmid: item.tmid, name: item.tmsg, t: 'thread'
+ rid: item.subscription.id, tmid: item.tmid, name, t: 'thread'
});
} else if (item.tlm) {
navigation.push('RoomView', {
@@ -689,7 +709,7 @@ class RoomView extends React.Component {
const { room } = this.state;
if (this.isOmnichannel) {
- await RocketChat.takeInquiry(room._id);
+ await takeInquiry(room._id);
} else {
await RocketChat.joinRoom(this.rid, this.t);
}
@@ -721,7 +741,8 @@ class RoomView extends React.Component {
});
});
} else {
- const { message: thread } = await RocketChat.getSingleMessage(tmid);
+ let { message: thread } = await RocketChat.getSingleMessage(tmid);
+ thread = await Encryption.decryptMessage(thread);
await db.action(async() => {
await db.batch(
threadCollection.prepareCreate((t) => {
@@ -856,6 +877,7 @@ class RoomView extends React.Component {
onReactionPress={this.onReactionPress}
onReactionLongPress={this.onReactionLongPress}
onLongPress={this.onMessageLongPress}
+ onEncryptedPress={this.onEncryptedPress}
onDiscussionPress={this.onDiscussionPress}
onThreadPress={this.onThreadPress}
showAttachment={this.showAttachment}
diff --git a/app/views/RoomsListView/ListHeader/Directory.js b/app/views/RoomsListView/ListHeader/Directory.js
deleted file mode 100644
index e8ed5f1a6..000000000
--- a/app/views/RoomsListView/ListHeader/Directory.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
-import PropTypes from 'prop-types';
-
-import Touch from '../../../utils/touch';
-import { CustomIcon } from '../../../lib/Icons';
-import I18n from '../../../i18n';
-import styles from '../styles';
-import DisclosureIndicator from '../../../containers/DisclosureIndicator';
-import { themes } from '../../../constants/colors';
-import { withTheme } from '../../../theme';
-
-const Directory = React.memo(({ goDirectory, theme, searching }) => {
- if (searching > 0) {
- return null;
- }
- const color = { color: themes[theme].headerSecondaryText };
- return (
-
-
-
- {I18n.t('Directory')}
-
-
-
- );
-});
-
-Directory.propTypes = {
- searching: PropTypes.bool,
- goDirectory: PropTypes.func,
- theme: PropTypes.string
-};
-
-export default withTheme(Directory);
diff --git a/app/views/RoomsListView/ListHeader/Encryption.js b/app/views/RoomsListView/ListHeader/Encryption.js
new file mode 100644
index 000000000..a7a0ee7c4
--- /dev/null
+++ b/app/views/RoomsListView/ListHeader/Encryption.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import { Text } from 'react-native';
+import PropTypes from 'prop-types';
+import { BorderlessButton } from 'react-native-gesture-handler';
+
+import { withTheme } from '../../../theme';
+import { CustomIcon } from '../../../lib/Icons';
+import { themes } from '../../../constants/colors';
+import I18n from '../../../i18n';
+import styles from '../styles';
+import { E2E_BANNER_TYPE } from '../../../lib/encryption/constants';
+
+const Encryption = React.memo(({
+ searching,
+ goEncryption,
+ encryptionBanner,
+ theme
+}) => {
+ if (searching > 0 || !encryptionBanner) {
+ return null;
+ }
+
+ let text = I18n.t('Save_Your_Encryption_Password');
+ if (encryptionBanner === E2E_BANNER_TYPE.REQUEST_PASSWORD) {
+ text = I18n.t('Enter_Your_E2E_Password');
+ }
+
+ return (
+
+
+ {text}
+
+ );
+});
+
+Encryption.propTypes = {
+ searching: PropTypes.bool,
+ goEncryption: PropTypes.func,
+ encryptionBanner: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default withTheme(Encryption);
diff --git a/app/views/RoomsListView/ListHeader/Queue.js b/app/views/RoomsListView/ListHeader/Queue.js
deleted file mode 100644
index 0a85d657a..000000000
--- a/app/views/RoomsListView/ListHeader/Queue.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
-import PropTypes from 'prop-types';
-
-import Touch from '../../../utils/touch';
-import I18n from '../../../i18n';
-import styles from '../styles';
-import { themes } from '../../../constants/colors';
-import { withTheme } from '../../../theme';
-import UnreadBadge from '../../../presentation/UnreadBadge';
-
-const Queue = React.memo(({
- searching, goQueue, queueSize, inquiryEnabled, theme
-}) => {
- if (searching > 0 || !inquiryEnabled) {
- return null;
- }
- return (
-
-
- {I18n.t('Queued_chats')}
-
-
-
- );
-});
-
-Queue.propTypes = {
- searching: PropTypes.bool,
- goQueue: PropTypes.func,
- queueSize: PropTypes.number,
- inquiryEnabled: PropTypes.bool,
- theme: PropTypes.string
-};
-
-export default withTheme(Queue);
diff --git a/app/views/RoomsListView/ListHeader/Sort.js b/app/views/RoomsListView/ListHeader/Sort.js
index a2bdfac42..972b29801 100644
--- a/app/views/RoomsListView/ListHeader/Sort.js
+++ b/app/views/RoomsListView/ListHeader/Sort.js
@@ -28,8 +28,8 @@ const Sort = React.memo(({
{ borderBottomWidth: StyleSheet.hairlineWidth, borderColor: themes[theme].separatorColor }
]}
>
+
{I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
-
);
diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js
index 5df248787..aa4372732 100644
--- a/app/views/RoomsListView/ListHeader/index.js
+++ b/app/views/RoomsListView/ListHeader/index.js
@@ -1,23 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Queue from './Queue';
-import Directory from './Directory';
import Sort from './Sort';
+import Encryption from './Encryption';
+
+import OmnichannelStatus from '../../../ee/omnichannel/containers/OmnichannelStatus';
const ListHeader = React.memo(({
searching,
sortBy,
toggleSort,
- goDirectory,
+ goEncryption,
goQueue,
queueSize,
- inquiryEnabled
+ inquiryEnabled,
+ encryptionBanner,
+ user
}) => (
<>
-
+
-
+
>
));
@@ -25,10 +28,12 @@ ListHeader.propTypes = {
searching: PropTypes.bool,
sortBy: PropTypes.string,
toggleSort: PropTypes.func,
- goDirectory: PropTypes.func,
+ goEncryption: PropTypes.func,
goQueue: PropTypes.func,
queueSize: PropTypes.number,
- inquiryEnabled: PropTypes.bool
+ inquiryEnabled: PropTypes.bool,
+ encryptionBanner: PropTypes.string,
+ user: PropTypes.object
};
export default ListHeader;
diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js
index afeb15eea..0f49cef82 100644
--- a/app/views/RoomsListView/ServerDropdown.js
+++ b/app/views/RoomsListView/ServerDropdown.js
@@ -5,7 +5,6 @@ import {
import PropTypes from 'prop-types';
import { connect, batch } from 'react-redux';
import equal from 'deep-equal';
-import RNUserDefaults from 'rn-user-defaults';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms';
@@ -26,6 +25,7 @@ import { showConfirmationAlert } from '../../utils/info';
import { logEvent, events } from '../../utils/log';
import { headerHeight } from '../../containers/Header';
import { goRoom } from '../../utils/goRoom';
+import UserPreferences from '../../lib/userPreferences';
const ROW_HEIGHT = 68;
const ANIMATION_DURATION = 200;
@@ -150,7 +150,7 @@ class ServerDropdown extends Component {
this.close();
if (currentServer !== server) {
logEvent(events.RL_CHANGE_SERVER);
- const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
+ const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (isMasterDetail) {
goRoom({ item: {}, isMasterDetail });
}
@@ -170,7 +170,7 @@ class ServerDropdown extends Component {
remove = server => showConfirmationAlert({
message: I18n.t('This_will_remove_all_data_from_this_server'),
- callToAction: I18n.t('Delete'),
+ confirmationText: I18n.t('Delete'),
onPress: async() => {
this.close();
try {
diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js
index f50c939d7..536f5738c 100644
--- a/app/views/RoomsListView/SortDropdown/index.js
+++ b/app/views/RoomsListView/SortDropdown/index.js
@@ -156,8 +156,8 @@ class Sort extends PureComponent {
>
+
{I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
-
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 513ce311d..4a2436431 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -62,8 +62,11 @@ import { goRoom } from '../../utils/goRoom';
import SafeAreaView from '../../containers/SafeAreaView';
import Header, { getHeaderTitlePosition } from '../../containers/Header';
import { withDimensions } from '../../dimensions';
-import { showErrorAlert } from '../../utils/info';
-import { getInquiryQueueSelector } from '../../selectors/inquiry';
+import { showErrorAlert, showConfirmationAlert } from '../../utils/info';
+import { E2E_BANNER_TYPE } from '../../lib/encryption/constants';
+
+import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
+import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../../ee/omnichannel/lib';
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
const CHATS_HEADER = 'Chats';
@@ -73,10 +76,12 @@ const DISCUSSIONS_HEADER = 'Discussions';
const CHANNELS_HEADER = 'Channels';
const DM_HEADER = 'Direct_Messages';
const GROUPS_HEADER = 'Private_Groups';
+const OMNICHANNEL_HEADER = 'Open_Livechats';
const QUERY_SIZE = 20;
const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus;
const filterIsFavorite = s => s.f;
+const filterIsOmnichannel = s => s.t === 'l';
const shouldUpdateProps = [
'searchText',
@@ -94,7 +99,8 @@ const shouldUpdateProps = [
'isMasterDetail',
'refreshing',
'queueSize',
- 'inquiryEnabled'
+ 'inquiryEnabled',
+ 'encryptionBanner'
];
const getItemLayout = (data, index) => ({
length: ROW_HEIGHT,
@@ -109,7 +115,9 @@ class RoomsListView extends React.Component {
user: PropTypes.shape({
id: PropTypes.string,
username: PropTypes.string,
- token: PropTypes.string
+ token: PropTypes.string,
+ statusLivechat: PropTypes.string,
+ roles: PropTypes.object
}),
server: PropTypes.string,
searchText: PropTypes.string,
@@ -137,7 +145,8 @@ class RoomsListView extends React.Component {
width: PropTypes.number,
insets: PropTypes.object,
queueSize: PropTypes.number,
- inquiryEnabled: PropTypes.bool
+ inquiryEnabled: PropTypes.bool,
+ encryptionBanner: PropTypes.string
};
constructor(props) {
@@ -372,6 +381,12 @@ class RoomsListView extends React.Component {
onPress={this.initSearching}
testID='rooms-list-view-search'
/>
+
))
};
@@ -407,7 +422,8 @@ class RoomsListView extends React.Component {
sortBy,
showUnread,
showFavorites,
- groupByType
+ groupByType,
+ user
} = this.props;
const db = database.active;
@@ -444,7 +460,6 @@ class RoomsListView extends React.Component {
.observe();
}
-
this.querySubscription = observable.subscribe((data) => {
let tempChats = [];
let chats = data;
@@ -455,6 +470,13 @@ class RoomsListView extends React.Component {
*/
const chatsOrder = data.map(item => item.rid);
+ const isOmnichannelAgent = user?.roles?.includes('livechat-agent');
+ if (isOmnichannelAgent) {
+ const omnichannel = chats.filter(s => filterIsOmnichannel(s));
+ chats = chats.filter(s => !filterIsOmnichannel(s));
+ tempChats = this.addRoomsGroup(omnichannel, OMNICHANNEL_HEADER, tempChats);
+ }
+
// unread
if (showUnread) {
const unread = chats.filter(s => filterIsUnread(s));
@@ -479,7 +501,7 @@ class RoomsListView extends React.Component {
tempChats = this.addRoomsGroup(channels, CHANNELS_HEADER, tempChats);
tempChats = this.addRoomsGroup(privateGroup, GROUPS_HEADER, tempChats);
tempChats = this.addRoomsGroup(direct, DM_HEADER, tempChats);
- } else if (showUnread || showFavorites) {
+ } else if (showUnread || showFavorites || isOmnichannelAgent) {
tempChats = this.addRoomsGroup(chats, CHATS_HEADER, tempChats);
} else {
tempChats = chats;
@@ -679,7 +701,28 @@ class RoomsListView extends React.Component {
goQueue = () => {
logEvent(events.RL_GO_QUEUE);
- const { navigation, isMasterDetail, queueSize } = this.props;
+ const {
+ navigation, isMasterDetail, queueSize, inquiryEnabled, user
+ } = this.props;
+
+ // if not-available, prompt to change to available
+ if (!isOmnichannelStatusAvailable(user)) {
+ showConfirmationAlert({
+ message: I18n.t('Omnichannel_enable_alert'),
+ confirmationText: I18n.t('Yes'),
+ onPress: async() => {
+ try {
+ await changeLivechatStatus();
+ } catch {
+ // Do nothing
+ }
+ }
+ });
+ }
+
+ if (!inquiryEnabled) {
+ return;
+ }
// prevent navigation to empty list
if (!queueSize) {
return showErrorAlert(I18n.t('Queue_is_empty'), I18n.t('Oops'));
@@ -763,6 +806,20 @@ class RoomsListView extends React.Component {
}
}
+ goEncryption = () => {
+ logEvent(events.RL_GO_E2E_SAVE_PASSWORD);
+ const { navigation, isMasterDetail, encryptionBanner } = this.props;
+
+ const isSavePassword = encryptionBanner === E2E_BANNER_TYPE.SAVE_PASSWORD;
+ if (isMasterDetail) {
+ const screen = isSavePassword ? 'E2ESaveYourPasswordView' : 'E2EEnterYourPasswordView';
+ navigation.navigate('ModalStackNavigator', { screen });
+ } else {
+ const screen = isSavePassword ? 'E2ESaveYourPasswordStackNavigator' : 'E2EEnterYourPasswordStackNavigator';
+ navigation.navigate(screen);
+ }
+ }
+
handleCommands = ({ event }) => {
const { navigation, server, isMasterDetail } = this.props;
const { input } = event;
@@ -807,16 +864,20 @@ class RoomsListView extends React.Component {
renderListHeader = () => {
const { searching } = this.state;
- const { sortBy, queueSize, inquiryEnabled } = this.props;
+ const {
+ sortBy, queueSize, inquiryEnabled, encryptionBanner, user
+ } = this.props;
return (
);
};
@@ -985,7 +1046,8 @@ const mapStateToProps = state => ({
StoreLastMessage: state.settings.Store_Last_Message,
rooms: state.room.rooms,
queueSize: getInquiryQueueSelector(state).length,
- inquiryEnabled: state.inquiry.enabled
+ inquiryEnabled: state.inquiry.enabled,
+ encryptionBanner: state.encryption.banner
});
const mapDispatchToProps = dispatch => ({
diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js
index 70f2bb6dc..fe848d345 100644
--- a/app/views/RoomsListView/styles.js
+++ b/app/views/RoomsListView/styles.js
@@ -23,7 +23,11 @@ export default StyleSheet.create({
sortToggleText: {
fontSize: 16,
flex: 1,
- marginLeft: 12,
+ ...sharedStyles.textRegular
+ },
+ queueToggleText: {
+ fontSize: 16,
+ flex: 1,
...sharedStyles.textRegular
},
dropdownContainer: {
@@ -58,6 +62,11 @@ export default StyleSheet.create({
height: 22,
marginHorizontal: 12
},
+ queueIcon: {
+ width: 22,
+ height: 22,
+ marginHorizontal: 12
+ },
groupTitleContainer: {
paddingHorizontal: 12,
paddingTop: 17,
@@ -117,14 +126,22 @@ export default StyleSheet.create({
height: StyleSheet.hairlineWidth,
marginLeft: 72
},
- directoryIcon: {
- width: 22,
- height: 22,
- marginHorizontal: 12
+ encryptionButton: {
+ width: '100%',
+ flexDirection: 'row',
+ alignItems: 'center',
+ padding: 12
},
- directoryText: {
- fontSize: 16,
+ encryptionIcon: {
+ ...sharedStyles.textMedium
+ },
+ encryptionText: {
flex: 1,
- ...sharedStyles.textRegular
+ fontSize: 16,
+ marginHorizontal: 16,
+ ...sharedStyles.textMedium
+ },
+ omnichannelToggle: {
+ marginRight: 12
}
});
diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js
index b918bd60f..607711eb6 100644
--- a/app/views/SearchMessagesView/index.js
+++ b/app/views/SearchMessagesView/index.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, FlatList, Text } from 'react-native';
+import { Q } from '@nozbe/watermelondb';
import { connect } from 'react-redux';
import equal from 'deep-equal';
@@ -20,6 +21,8 @@ import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
import { CloseModalButton } from '../../containers/HeaderButton';
+import database from '../../lib/database';
+import { sanitizeLikeString } from '../../lib/database/utils';
class SearchMessagesView extends React.Component {
static navigationOptions = ({ navigation, route }) => {
@@ -50,6 +53,7 @@ class SearchMessagesView extends React.Component {
searchText: ''
};
this.rid = props.route.params?.rid;
+ this.encrypted = props.route.params?.encrypted;
}
shouldComponentUpdate(nextProps, nextState) {
@@ -71,21 +75,41 @@ class SearchMessagesView extends React.Component {
}
componentWillUnmount() {
- this.search.stop();
+ this.search?.stop?.();
+ }
+
+ // Handle encrypted rooms search messages
+ searchMessages = async(searchText) => {
+ // If it's a encrypted, room we'll search only on the local stored messages
+ if (this.encrypted) {
+ const db = database.active;
+ const messagesCollection = db.collections.get('messages');
+ const likeString = sanitizeLikeString(searchText);
+ return messagesCollection
+ .query(
+ // Messages of this room
+ Q.where('rid', this.rid),
+ // Message content is like the search text
+ Q.where('msg', Q.like(`%${ likeString }%`))
+ )
+ .fetch();
+ }
+ // If it's not a encrypted room, search messages on the server
+ const result = await RocketChat.searchMessages(this.rid, searchText);
+ if (result.success) {
+ return result.messages;
+ }
}
- // eslint-disable-next-line react/sort-comp
search = debounce(async(searchText) => {
this.setState({ searchText, loading: true, messages: [] });
try {
- const result = await RocketChat.searchMessages(this.rid, searchText);
- if (result.success) {
- this.setState({
- messages: result.messages || [],
- loading: false
- });
- }
+ const messages = await this.searchMessages(searchText);
+ this.setState({
+ messages: messages || [],
+ loading: false
+ });
} catch (e) {
this.setState({ loading: false });
log(e);
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index ee362b772..2b9a9d15e 100644
--- a/app/views/SettingsView/index.js
+++ b/app/views/SettingsView/index.js
@@ -6,10 +6,11 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import AsyncStorage from '@react-native-community/async-storage';
import FastImage from '@rocket.chat/react-native-fast-image';
+import CookieManager from '@react-native-community/cookies';
import { logout as logoutAction } from '../../actions/login';
import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
-import { toggleCrashReport as toggleCrashReportAction } from '../../actions/crashReport';
+import { toggleCrashReport as toggleCrashReportAction, toggleAnalyticsEvents as toggleAnalyticsEventsAction } from '../../actions/crashReport';
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import { DrawerButton, CloseModalButton } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
@@ -18,7 +19,7 @@ import ItemInfo from '../../containers/ItemInfo';
import { DisclosureImage } from '../../containers/DisclosureIndicator';
import Separator from '../../containers/Separator';
import I18n from '../../i18n';
-import RocketChat, { CRASH_REPORT_KEY } from '../../lib/rocketchat';
+import RocketChat, { CRASH_REPORT_KEY, ANALYTICS_EVENTS_KEY } from '../../lib/rocketchat';
import {
getReadableVersion, getDeviceModel, isAndroid
} from '../../utils/deviceInfo';
@@ -29,15 +30,20 @@ import styles from './styles';
import {
loggerConfig, analytics, logEvent, events
} from '../../utils/log';
-import { PLAY_MARKET_LINK, APP_STORE_LINK, LICENSE_LINK } from '../../constants/links';
+import {
+ PLAY_MARKET_LINK, FDROID_MARKET_LINK, APP_STORE_LINK, LICENSE_LINK
+} from '../../constants/links';
import { withTheme } from '../../theme';
import SidebarView from '../SidebarView';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app';
import { onReviewPress } from '../../utils/review';
-import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
+import database from '../../lib/database';
+import { isFDroidBuild } from '../../constants/environment';
+import { getUserSelector } from '../../selectors/login';
+
const SectionSeparator = React.memo(({ theme }) => (
{
+ const { logout, user } = this.props;
+ const db = database.servers;
+ const usersCollection = db.collections.get('users');
+ try {
+ const userRecord = await usersCollection.find(user.id);
+ if (!userRecord.loginEmailPassword) {
+ showConfirmationAlert({
+ title: I18n.t('Clear_cookies_alert'),
+ message: I18n.t('Clear_cookies_desc'),
+ confirmationText: I18n.t('Clear_cookies_yes'),
+ dismissText: I18n.t('Clear_cookies_no'),
+ onPress: async() => {
+ await CookieManager.clearAll(true);
+ logout();
+ },
+ onCancel: () => {
+ logout();
+ }
+ });
+ } else {
+ logout();
+ }
+ } catch {
+ // Do nothing: user not found
+ }
}
handleLogout = () => {
logEvent(events.SE_LOG_OUT);
showConfirmationAlert({
message: I18n.t('You_will_be_logged_out_of_this_application'),
- callToAction: I18n.t('Logout'),
- onPress: () => {
- const { logout } = this.props;
- logout();
- }
+ confirmationText: I18n.t('Logout'),
+ onPress: this.checkCookiesAndLogout
});
}
@@ -103,7 +129,7 @@ class SettingsView extends React.Component {
logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE);
showConfirmationAlert({
message: I18n.t('This_will_clear_all_your_offline_data'),
- callToAction: I18n.t('Clear'),
+ confirmationText: I18n.t('Clear'),
onPress: async() => {
const {
server: { server }, appStart, selectServerRequest
@@ -122,21 +148,22 @@ class SettingsView extends React.Component {
AsyncStorage.setItem(CRASH_REPORT_KEY, JSON.stringify(value));
const { toggleCrashReport } = this.props;
toggleCrashReport(value);
- loggerConfig.autoNotify = value;
- analytics().setAnalyticsCollectionEnabled(value);
- if (value) {
- loggerConfig.clearBeforeSendCallbacks();
- } else {
- loggerConfig.registerBeforeSendCallback(() => false);
+ if (!isFDroidBuild) {
+ loggerConfig.autoNotify = value;
+ if (value) {
+ loggerConfig.clearBeforeSendCallbacks();
+ } else {
+ loggerConfig.registerBeforeSendCallback(() => false);
+ }
}
}
- toggleLivechat = async() => {
- try {
- await RocketChat.changeLivechatStatus();
- } catch {
- // Do nothing
- }
+ toggleAnalyticsEvents = (value) => {
+ logEvent(events.SE_TOGGLE_ANALYTICS_EVENTS);
+ const { toggleAnalyticsEvents } = this.props;
+ AsyncStorage.setItem(ANALYTICS_EVENTS_KEY, JSON.stringify(value));
+ toggleAnalyticsEvents(value);
+ analytics().setAnalyticsCollectionEnabled(value);
}
navigateToScreen = (screen) => {
@@ -162,8 +189,16 @@ class SettingsView extends React.Component {
}
shareApp = () => {
- logEvent(events.SE_SHARE_THIS_APP);
- Share.share({ message: isAndroid ? PLAY_MARKET_LINK : APP_STORE_LINK });
+ let message;
+ if (isAndroid) {
+ message = PLAY_MARKET_LINK;
+ if (isFDroidBuild) {
+ message = FDROID_MARKET_LINK;
+ }
+ } else {
+ message = APP_STORE_LINK;
+ }
+ Share.share({ message });
}
copyServerVersion = () => {
@@ -204,14 +239,13 @@ class SettingsView extends React.Component {
);
}
- renderLivechatSwitch = () => {
- const { user } = this.props;
- const { statusLivechat } = user;
+ renderAnalyticsEventsSwitch = () => {
+ const { allowAnalyticsEvents } = this.props;
return (
);
}
@@ -262,14 +296,18 @@ class SettingsView extends React.Component {
theme={theme}
/>
-
+ {!isFDroidBuild ? (
+ <>
+
+ >
+ ) : null}
- {this.showLivechat ? (
+ {!isFDroidBuild ? (
<>
this.renderLivechatSwitch()}
+ title={I18n.t('Log_analytics_events')}
+ testID='settings-view-analytics-events'
+ right={() => this.renderAnalyticsEventsSwitch()}
theme={theme}
/>
-
+
+ this.renderCrashReportSwitch()}
+ theme={theme}
+ />
+
+
+
>
) : null}
- this.renderCrashReportSwitch()}
- theme={theme}
- />
-
-
-
-
({
server: state.server,
user: getUserSelector(state),
allowCrashReport: state.crashReport.allowCrashReport,
+ allowAnalyticsEvents: state.crashReport.allowAnalyticsEvents,
isMasterDetail: state.app.isMasterDetail
});
@@ -396,6 +434,7 @@ const mapDispatchToProps = dispatch => ({
logout: () => dispatch(logoutAction()),
selectServerRequest: params => dispatch(selectServerRequestAction(params)),
toggleCrashReport: params => dispatch(toggleCrashReportAction(params)),
+ toggleAnalyticsEvents: params => dispatch(toggleAnalyticsEventsAction(params)),
appStart: params => dispatch(appStartAction(params))
});
diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js
index c40bd1843..1f94ee743 100644
--- a/app/views/ShareListView/index.js
+++ b/app/views/ShareListView/index.js
@@ -26,6 +26,7 @@ import { animateNextTransition } from '../../utils/layoutAnimation';
import { withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView';
import RocketChat from '../../lib/rocketchat';
+import { sanitizeLikeString } from '../../lib/database/utils';
const permission = {
title: I18n.t('Read_External_Permission'),
@@ -169,7 +170,7 @@ class ShareListView extends React.Component {
? null
: (
-
+
)
)
@@ -195,13 +196,14 @@ class ShareListView extends React.Component {
Q.experimentalSortBy('room_updated_at', Q.desc)
];
if (text) {
+ const likeString = sanitizeLikeString(text);
return db.collections
.get('subscriptions')
.query(
...defaultWhereClause,
Q.or(
- Q.where('name', Q.like(`%${ Q.sanitizeLikeString(text) }%`)),
- Q.where('fname', Q.like(`%${ Q.sanitizeLikeString(text) }%`))
+ Q.where('name', Q.like(`%${ likeString }%`)),
+ Q.where('fname', Q.like(`%${ likeString }%`))
)
).fetch();
}
diff --git a/app/views/SidebarView/index.js b/app/views/SidebarView/index.js
index 6690b3b30..2eb44390f 100644
--- a/app/views/SidebarView/index.js
+++ b/app/views/SidebarView/index.js
@@ -19,6 +19,7 @@ import database from '../../lib/database';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
+import Navigation from '../../lib/Navigation';
const Separator = React.memo(({ theme }) => );
Separator.propTypes = {
@@ -135,8 +136,7 @@ class Sidebar extends Component {
sidebarNavigate = (route) => {
logEvent(events[`SIDEBAR_GO_${ route.replace('StackNavigator', '').replace('View', '').toUpperCase() }`]);
- const { navigation } = this.props;
- navigation.navigate(route);
+ Navigation.navigate(route);
}
get currentItemKey() {
diff --git a/app/views/ThemeView.js b/app/views/ThemeView.js
index e6e07d1f1..2686e3b69 100644
--- a/app/views/ThemeView.js
+++ b/app/views/ThemeView.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import {
FlatList, Text, View, StyleSheet
} from 'react-native';
-import RNUserDefaults from 'rn-user-defaults';
import I18n from '../i18n';
import { withTheme } from '../theme';
@@ -16,6 +15,7 @@ import { CustomIcon } from '../lib/Icons';
import { THEME_PREFERENCES_KEY } from '../lib/rocketchat';
import { supportSystemTheme } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
+import UserPreferences from '../lib/userPreferences';
import { events, logEvent } from '../utils/log';
const THEME_GROUP = 'THEME_GROUP';
@@ -111,7 +111,7 @@ class ThemeView extends React.Component {
const { setTheme, themePreferences } = this.props;
const newTheme = { ...themePreferences, ...theme };
setTheme(newTheme);
- await RNUserDefaults.setObjectForKey(THEME_PREFERENCES_KEY, newTheme);
+ await UserPreferences.setMapAsync(THEME_PREFERENCES_KEY, newTheme);
};
renderSeparator = () => {
diff --git a/app/views/UserNotificationPreferencesView/Info.js b/app/views/UserNotificationPreferencesView/Info.js
new file mode 100644
index 000000000..0c01e3c16
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/Info.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const Info = React.memo(({ info, theme }) => (
+
+ {info}
+
+));
+
+Info.propTypes = {
+ info: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default Info;
diff --git a/app/views/UserNotificationPreferencesView/SectionSeparator.js b/app/views/UserNotificationPreferencesView/SectionSeparator.js
new file mode 100644
index 000000000..88acd1a0e
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/SectionSeparator.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import {
+ View
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const SectionSeparator = React.memo(({ theme }) => (
+
+));
+
+SectionSeparator.propTypes = {
+ theme: PropTypes.string
+};
+
+export default SectionSeparator;
diff --git a/app/views/UserNotificationPreferencesView/SectionTitle.js b/app/views/UserNotificationPreferencesView/SectionTitle.js
new file mode 100644
index 000000000..e93cb0e01
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/SectionTitle.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+
+import styles from './styles';
+import { themes } from '../../constants/colors';
+
+const SectionTitle = React.memo(({ title, theme }) => (
+
+ {title}
+
+));
+
+SectionTitle.propTypes = {
+ title: PropTypes.string,
+ theme: PropTypes.string
+};
+
+export default SectionTitle;
diff --git a/app/views/UserNotificationPreferencesView/index.js b/app/views/UserNotificationPreferencesView/index.js
new file mode 100644
index 000000000..e07888594
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/index.js
@@ -0,0 +1,168 @@
+import React from 'react';
+import {
+ View, ScrollView, Text
+} from 'react-native';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
+import { themes } from '../../constants/colors';
+import StatusBar from '../../containers/StatusBar';
+import ListItem from '../../containers/ListItem';
+import Separator from '../../containers/Separator';
+import I18n from '../../i18n';
+import scrollPersistTaps from '../../utils/scrollPersistTaps';
+import styles from './styles';
+import RocketChat from '../../lib/rocketchat';
+import { withTheme } from '../../theme';
+import SafeAreaView from '../../containers/SafeAreaView';
+import SectionTitle from './SectionTitle';
+import SectionSeparator from './SectionSeparator';
+import Info from './Info';
+import { OPTIONS } from './options';
+import ActivityIndicator from '../../containers/ActivityIndicator';
+import { DisclosureImage } from '../../containers/DisclosureIndicator';
+import { getUserSelector } from '../../selectors/login';
+
+class UserNotificationPreferencesView extends React.Component {
+ static navigationOptions = () => ({
+ title: I18n.t('Notification_Preferences')
+ })
+
+ static propTypes = {
+ navigation: PropTypes.object,
+ route: PropTypes.object,
+ theme: PropTypes.string,
+ user: PropTypes.shape({
+ id: PropTypes.string
+ })
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ preferences: {},
+ loading: false
+ };
+ }
+
+ async componentDidMount() {
+ const { user } = this.props;
+ const { id } = user;
+ const result = await RocketChat.getUserPreferences(id);
+ const { preferences } = result;
+ this.setState({ preferences, loading: true });
+ }
+
+ findOption = (key) => {
+ const { preferences } = this.state;
+ const option = preferences[key] ? OPTIONS[key].find(item => item.value === preferences[key]) : OPTIONS[key][0];
+ return option;
+ }
+
+ renderPickerOption = (key) => {
+ const { theme } = this.props;
+ const text = this.findOption(key);
+ return {I18n.t(text?.label, { defaultValue: text?.label, second: text?.second })};
+ }
+
+ pickerSelection = (title, key) => {
+ const { preferences } = this.state;
+ const { navigation } = this.props;
+ let values = OPTIONS[key];
+ if (OPTIONS[key][0]?.value !== 'default') {
+ values = [{ label: `${ I18n.t('Default') } (${ I18n.t(this.findOption(key).label) })`, value: preferences[key]?.value }, ...OPTIONS[key]];
+ }
+ navigation.navigate('PickerView', {
+ title,
+ data: values,
+ value: preferences[key],
+ onChangeValue: value => this.onValueChangePicker(key, value)
+ });
+ }
+
+ onValueChangePicker = (key, value) => this.saveNotificationPreferences({ [key]: value.toString() });
+
+ saveNotificationPreferences = async(params) => {
+ const { user } = this.props;
+ const { id } = user;
+ const result = await RocketChat.setUserPreferences(id, params);
+ const { user: { settings } } = result;
+ this.setState({ preferences: settings.preferences });
+ }
+
+ renderDisclosure = () => {
+ const { theme } = this.props;
+ return ;
+ }
+
+ render() {
+ const { theme } = this.props;
+ const { loading } = this.state;
+ return (
+
+
+
+ {loading
+ ? (
+ <>
+
+
+
+ this.pickerSelection(title, 'desktopNotifications')}
+ right={() => this.renderPickerOption('desktopNotifications')}
+ theme={theme}
+ />
+
+
+
+
+
+
+
+ this.pickerSelection(title, 'mobileNotifications')}
+ right={() => this.renderPickerOption('mobileNotifications')}
+ theme={theme}
+ />
+
+
+
+
+
+
+
+ this.pickerSelection(title, 'emailNotificationMode')}
+ right={() => this.renderPickerOption('emailNotificationMode')}
+ theme={theme}
+ />
+
+
+
+ >
+ ) :
+ }
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = state => ({
+ user: getUserSelector(state)
+});
+
+export default connect(mapStateToProps)(withTheme(UserNotificationPreferencesView));
diff --git a/app/views/UserNotificationPreferencesView/options.js b/app/views/UserNotificationPreferencesView/options.js
new file mode 100644
index 000000000..7ed02e12e
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/options.js
@@ -0,0 +1,19 @@
+const commonOptions = [{
+ label: 'Default', value: 'default'
+}, {
+ label: 'All_Messages', value: 'all'
+}, {
+ label: 'Mentions', value: 'mentions'
+}, {
+ label: 'Nothing', value: 'nothing'
+}];
+
+export const OPTIONS = {
+ desktopNotifications: commonOptions,
+ mobileNotifications: commonOptions,
+ emailNotificationMode: [{
+ label: 'Email_Notification_Mode_All', value: 'mentions'
+ }, {
+ label: 'Email_Notification_Mode_Disabled', value: 'nothing'
+ }]
+};
diff --git a/app/views/UserNotificationPreferencesView/styles.js b/app/views/UserNotificationPreferencesView/styles.js
new file mode 100644
index 000000000..9b4c9e0e6
--- /dev/null
+++ b/app/views/UserNotificationPreferencesView/styles.js
@@ -0,0 +1,31 @@
+import { StyleSheet } from 'react-native';
+
+import sharedStyles from '../Styles';
+
+export default StyleSheet.create({
+ sectionSeparatorBorder: {
+ height: 10
+ },
+ marginBottom: {
+ height: 30
+ },
+ contentContainer: {
+ marginVertical: 10
+ },
+ infoText: {
+ ...sharedStyles.textRegular,
+ fontSize: 13,
+ paddingHorizontal: 15,
+ paddingVertical: 10
+ },
+ sectionTitle: {
+ ...sharedStyles.separatorBottom,
+ paddingHorizontal: 15,
+ paddingVertical: 10,
+ fontSize: 14
+ },
+ pickerText: {
+ ...sharedStyles.textRegular,
+ fontSize: 16
+ }
+});
diff --git a/app/views/UserPreferencesView/index.js b/app/views/UserPreferencesView/index.js
new file mode 100644
index 000000000..096736b7c
--- /dev/null
+++ b/app/views/UserPreferencesView/index.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import { ScrollView } from 'react-native';
+import PropTypes from 'prop-types';
+
+import I18n from '../../i18n';
+import {
+ logEvent, events
+} from '../../utils/log';
+import scrollPersistTaps from '../../utils/scrollPersistTaps';
+import Separator from '../../containers/Separator';
+import SafeAreaView from '../../containers/SafeAreaView';
+import StatusBar from '../../containers/StatusBar';
+import ListItem from '../../containers/ListItem';
+import { DisclosureImage } from '../../containers/DisclosureIndicator';
+import { withTheme } from '../../theme';
+
+class UserPreferencesView extends React.Component {
+ static navigationOptions = () => ({
+ title: I18n.t('Preferences')
+ });
+
+ static propTypes = {
+ navigation: PropTypes.object,
+ theme: PropTypes.string
+ }
+
+ renderDisclosure = () => {
+ const { theme } = this.props;
+ return ;
+ }
+
+ navigateToScreen = (screen, params) => {
+ logEvent(events[`SE_GO_${ screen.replace('View', '').toUpperCase() }`]);
+ const { navigation } = this.props;
+ navigation.navigate(screen, params);
+ }
+
+ render() {
+ const { theme } = this.props;
+
+ return (
+
+
+
+ this.navigateToScreen('UserNotificationPrefView')}
+ showActionIndicator
+ testID='preferences-view-notifications'
+ right={this.renderDisclosure}
+ theme={theme}
+ />
+
+
+
+ );
+ }
+}
+
+export default withTheme(UserPreferencesView);
diff --git a/e2e/tests/assorted/03-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js
index 9700da1b2..f531ef18a 100644
--- a/e2e/tests/assorted/03-profile.spec.js
+++ b/e2e/tests/assorted/03-profile.spec.js
@@ -13,7 +13,7 @@ async function waitForToast() {
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
- await sleep(1);
+ await sleep(300);
}
describe('Profile screen', () => {
diff --git a/e2e/tests/assorted/06-status.spec.js b/e2e/tests/assorted/06-status.spec.js
index 887dcb1e0..2ddd54e6b 100644
--- a/e2e/tests/assorted/06-status.spec.js
+++ b/e2e/tests/assorted/06-status.spec.js
@@ -7,7 +7,7 @@ const data = require('../../data');
const testuser = data.users.regular
async function waitForToast() {
- await sleep(1);
+ await sleep(300);
}
describe('Status screen', () => {
diff --git a/e2e/tests/onboarding/07-server-history.spec.js b/e2e/tests/onboarding/07-server-history.spec.js
new file mode 100644
index 000000000..d026965fb
--- /dev/null
+++ b/e2e/tests/onboarding/07-server-history.spec.js
@@ -0,0 +1,44 @@
+const {
+ device, expect, element, by, waitFor
+} = require('detox');
+const { login, navigateToLogin, logout, tapBack } = require('../../helpers/app');
+const data = require('../../data');
+
+describe('Server history', () => {
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ });
+
+ describe('Usage', () => {
+ it('should login, save server as history and logout', async() => {
+ await navigateToLogin();
+ await login(data.users.regular.username, data.users.regular.password);
+ await logout();
+ await element(by.id('join-workspace')).tap();
+ await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
+ })
+
+ it('should show servers history', async() => {
+ await element(by.id('new-server-view-input')).tap();
+ await waitFor(element(by.id(`server-history-${ data.server }`))).toBeVisible().withTimeout(2000);
+ });
+
+ it('should tap on a server history and navigate to login', async() => {
+ await element(by.id(`server-history-${ data.server }`)).tap();
+ await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('login-view-email'))).toHaveText(data.users.regular.username);
+ });
+
+ it('should delete server from history', async() => {
+ await tapBack();
+ await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(2000);
+ await tapBack();
+ await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('new-server-view-input')).tap();
+ await waitFor(element(by.id(`server-history-${ data.server }`))).toBeVisible().withTimeout(2000);
+ await element(by.id(`server-history-delete-${ data.server }`)).tap();
+ await element(by.id('new-server-view-input')).tap();
+ await waitFor(element(by.id(`server-history-${ data.server }`))).toBeNotVisible().withTimeout(2000);
+ });
+ });
+});
diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js
index 3fb464458..5a726a967 100644
--- a/e2e/tests/room/01-createroom.spec.js
+++ b/e2e/tests/room/01-createroom.spec.js
@@ -66,11 +66,13 @@ describe('Create room screen', () => {
});
it('should select/unselect user', async() => {
- await element(by.id('select-users-view-item-rocket.cat')).tap();
+ // Spotlight issues
+ await element(by.id('select-users-view-item-rocket.cat')).atIndex(0).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
await element(by.id('selected-user-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeNotVisible().withTimeout(10000);
- await element(by.id('select-users-view-item-rocket.cat')).tap();
+ // Spotlight issues
+ await element(by.id('select-users-view-item-rocket.cat')).atIndex(0).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
});
diff --git a/e2e/tests/room/02-room.spec.js b/e2e/tests/room/02-room.spec.js
index 00d5d434d..224c8c9fb 100644
--- a/e2e/tests/room/02-room.spec.js
+++ b/e2e/tests/room/02-room.spec.js
@@ -298,8 +298,8 @@ describe('Room screen', () => {
});
it('should navigate to thread from thread name', async() => {
- await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ mainRoom }`)))).toBeVisible().withTimeout(2000);
- await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
+ await waitFor(element(by.id('room-view-header-actions').and(by.label(`${ mainRoom }`)))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id('room-view-header-actions').and(by.label(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await sleep(500) //TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
await mockMessage('dummymessagebetweenthethread');
diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js
index 43ff025ef..fa3fdfbae 100644
--- a/e2e/tests/room/03-roomactions.spec.js
+++ b/e2e/tests/room/03-roomactions.spec.js
@@ -14,8 +14,8 @@ async function navigateToRoomActions(type) {
room = data.groups.private.name;
}
await searchRoom(room);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-${ room }`)).tap();
+ await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toExist().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
@@ -245,7 +245,7 @@ describe('Room actions screen', () => {
//Back into Room Actions
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
-
+ await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await element(by.id('room-actions-pinned')).tap();
await waitFor(element(by.id('pinned-messages-view'))).toExist().withTimeout(2000);
@@ -281,6 +281,7 @@ describe('Room actions screen', () => {
describe('Notification', async() => {
it('should navigate to notification preference view', async() => {
+ await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000);
await element(by.id('room-actions-notifications')).tap();
await waitFor(element(by.id('notification-preference-view'))).toExist().withTimeout(2000);
@@ -332,6 +333,7 @@ describe('Room actions screen', () => {
const user = data.users.alternate
it('should tap on leave channel and raise alert', async() => {
+ await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await waitFor(element(by.id('room-actions-leave-channel'))).toExist().withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.text('Yes, leave it!'))).toExist().withTimeout(2000);
@@ -352,6 +354,7 @@ describe('Room actions screen', () => {
await waitFor(element(by.id(`selected-user-${ user.username }`))).toExist().withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000);
+ await waitFor(element(by.id('room-actions-members'))).toExist().withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js
index 9382f16cf..3f4ee175d 100644
--- a/e2e/tests/room/04-roominfo.spec.js
+++ b/e2e/tests/room/04-roominfo.spec.js
@@ -28,7 +28,7 @@ async function waitForToast() {
// await expect(element(by.id('toast'))).toExist();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
- await sleep(1);
+ await sleep(300);
}
describe('Room info screen', () => {
diff --git a/ios/AppGroup/AppGroup.m b/ios/AppGroup/AppGroup.m
new file mode 100644
index 000000000..137c3edd8
--- /dev/null
+++ b/ios/AppGroup/AppGroup.m
@@ -0,0 +1,11 @@
+//
+// AppGroup.m
+// RocketChatRN
+//
+// Created by Djorkaeff Alexandre Vilela Pereira on 8/31/20.
+// Copyright © 2020 Facebook. All rights reserved.
+//
+
+#import "React/RCTBridgeModule.h"
+@interface RCT_EXTERN_MODULE(AppGroup, NSObject)
+@end
diff --git a/ios/AppGroup/AppGroup.swift b/ios/AppGroup/AppGroup.swift
new file mode 100644
index 000000000..278e9be57
--- /dev/null
+++ b/ios/AppGroup/AppGroup.swift
@@ -0,0 +1,26 @@
+//
+// AppGroup.swift
+// RocketChatRN
+//
+// Created by Djorkaeff Alexandre Vilela Pereira on 8/31/20.
+// Copyright © 2020 Facebook. All rights reserved.
+//
+
+import Foundation
+
+@objc(AppGroup)
+class AppGroup: NSObject {
+
+ @objc
+ func constantsToExport() -> [AnyHashable : Any]! {
+ // Get App Group directory
+ var path = ""
+ if let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String {
+ if let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName) {
+ path = directory.path
+ }
+ }
+
+ return ["path": "\(path)/"]
+ }
+}
diff --git a/ios/GoogleService-Info.prod.plist b/ios/GoogleService-Info.prod.plist
deleted file mode 100644
index 11d81294f..000000000
--- a/ios/GoogleService-Info.prod.plist
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
- CLIENT_ID
- 673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv.apps.googleusercontent.com
- REVERSED_CLIENT_ID
- com.googleusercontent.apps.673693445664-jbf9m30ta163gobjfp0v7j1v7kpo7kmv
- ANDROID_CLIENT_ID
- 673693445664-k0mvosdjoe5dbvqce3b377ckabb5dgu8.apps.googleusercontent.com
- API_KEY
- AIzaSyDL4sXxk8DeGYzacSib0GDQ3BoGACP0VDM
- GCM_SENDER_ID
- 673693445664
- PLIST_VERSION
- 1
- BUNDLE_ID
- chat.rocket.reactnative
- PROJECT_ID
- rocketchat-9e9be
- STORAGE_BUCKET
- rocketchat-9e9be.appspot.com
- IS_ADS_ENABLED
-
- IS_ANALYTICS_ENABLED
-
- IS_APPINVITE_ENABLED
-
- IS_GCM_ENABLED
-
- IS_SIGNIN_ENABLED
-
- GOOGLE_APP_ID
- 1:673693445664:ios:8be27b1f7c42a2ed
- DATABASE_URL
- https://rocketchat-9e9be.firebaseio.com
-
-
\ No newline at end of file
diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist
index 496dfc6ce..5705853e5 100644
--- a/ios/NotificationService/Info.plist
+++ b/ios/NotificationService/Info.plist
@@ -2,6 +2,8 @@
+ KeychainGroup
+ $(AppIdentifierPrefix)chat.rocket.reactnative
AppGroup
group.ios.chat.rocket
CFBundleDevelopmentRegion
diff --git a/ios/NotificationService/NotificationService-Bridging-Header.h b/ios/NotificationService/NotificationService-Bridging-Header.h
new file mode 100644
index 000000000..2417bb56d
--- /dev/null
+++ b/ios/NotificationService/NotificationService-Bridging-Header.h
@@ -0,0 +1,9 @@
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import
+#import
+#import
+#import
+#import
diff --git a/ios/NotificationService/NotificationService.entitlements b/ios/NotificationService/NotificationService.entitlements
index f48f06fbc..4bfdb2c2b 100644
--- a/ios/NotificationService/NotificationService.entitlements
+++ b/ios/NotificationService/NotificationService.entitlements
@@ -6,5 +6,9 @@
group.ios.chat.rocket
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)chat.rocket.reactnative
+
diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift
index bb5352a59..d2d31f720 100644
--- a/ios/NotificationService/NotificationService.swift
+++ b/ios/NotificationService/NotificationService.swift
@@ -1,155 +1,55 @@
-import CoreLocation
import UserNotifications
-struct PushResponse: Decodable {
- let success: Bool
- let data: Data
-
- struct Data: Decodable {
- let notification: Notification
-
- struct Notification: Decodable {
- let notId: Int
- let title: String
- let text: String
- let payload: Payload
-
- struct Payload: Decodable, Encodable {
- let host: String
- let rid: String?
- let type: String?
- let sender: Sender?
- let messageId: String
- let notificationType: String?
- let name: String?
- let messageType: String?
-
- struct Sender: Decodable, Encodable {
- let _id: String
- let username: String
- let name: String?
- }
- }
- }
- }
-}
-
class NotificationService: UNNotificationServiceExtension {
-
- var contentHandler: ((UNNotificationContent) -> Void)?
- var bestAttemptContent: UNMutableNotificationContent?
-
- var retryCount = 0
- var retryTimeout = [1.0, 3.0, 5.0, 10.0]
-
- override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
- self.contentHandler = contentHandler
- bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
-
- if let bestAttemptContent = bestAttemptContent {
- let ejson = (bestAttemptContent.userInfo["ejson"] as? String ?? "").data(using: .utf8)!
- guard let data = try? (JSONDecoder().decode(PushResponse.Data.Notification.Payload.self, from: ejson)) else {
- return
- }
-
- let notificationType = data.notificationType ?? ""
-
- // If the notification have the content at her payload, show it
- if notificationType != "message-id-only" {
- contentHandler(bestAttemptContent)
- return
- }
-
- let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
- let userDefaults = UserDefaults(suiteName: suiteName)
-
- var server = data.host
- if (server.last == "/") {
- server.removeLast()
- }
- let msgId = data.messageId
-
- let userId = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(server)") ?? ""
- let token = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(userId)") ?? ""
-
- if userId.isEmpty || token.isEmpty {
- contentHandler(bestAttemptContent)
- return
- }
-
- var urlComponents = URLComponents(string: "\(server)/api/v1/push.get")!
- let queryItems = [URLQueryItem(name: "id", value: msgId)]
- urlComponents.queryItems = queryItems
-
- var request = URLRequest(url: urlComponents.url!)
- request.httpMethod = "GET"
- request.addValue(userId, forHTTPHeaderField: "x-user-id")
- request.addValue(token, forHTTPHeaderField: "x-auth-token")
-
- runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler)
- }
- }
- func runRequest(request: URLRequest, bestAttemptContent: UNMutableNotificationContent, contentHandler: @escaping (UNNotificationContent) -> Void) {
- let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
-
- func retryRequest() {
- // if we can try again
- if self.retryCount < self.retryTimeout.count {
- // Try again after X seconds
- DispatchQueue.main.asyncAfter(deadline: .now() + self.retryTimeout[self.retryCount], execute: {
- self.runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler)
- self.retryCount += 1
- })
- }
- }
-
- // If some error happened
- if error != nil {
- retryRequest()
-
- // Check if the request did successfully
- } else if let response = response as? HTTPURLResponse {
- // if it not was successfully
- if response.statusCode != 200 {
- retryRequest()
-
- // If the response status is 200
- } else {
- // Process data
- if let data = data {
- // Parse data of response
- let push = try? (JSONDecoder().decode(PushResponse.self, from: data))
- if let push = push {
- if push.success {
- bestAttemptContent.title = push.data.notification.title
- bestAttemptContent.body = push.data.notification.text
-
- let payload = try? (JSONEncoder().encode(push.data.notification.payload))
- if let payload = payload {
- bestAttemptContent.userInfo["ejson"] = String(data: payload, encoding: .utf8) ?? "{}"
- }
-
- // Show notification with the content modified
- contentHandler(bestAttemptContent)
- return
- }
- }
- }
- retryRequest()
- }
+ var contentHandler: ((UNNotificationContent) -> Void)?
+ var bestAttemptContent: UNMutableNotificationContent?
+ var rocketchat: RocketChat?
+
+ override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ self.contentHandler = contentHandler
+ bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
+
+ if let bestAttemptContent = bestAttemptContent {
+ let ejson = (bestAttemptContent.userInfo["ejson"] as? String ?? "").data(using: .utf8)!
+ guard let data = try? (JSONDecoder().decode(Payload.self, from: ejson)) else {
+ return
+ }
+
+ rocketchat = RocketChat.instanceForServer(server: data.host.removeTrailingSlash())
+
+ // If the notification has the content on the payload, show it
+ if data.notificationType != .messageIdOnly {
+ self.processPayload(payload: data)
+ return
+ }
+
+ // Request the content from server
+ rocketchat?.getPushWithId(data.messageId) { notification in
+ if let notification = notification {
+ self.bestAttemptContent?.title = notification.title
+ self.bestAttemptContent?.body = notification.text
+ self.processPayload(payload: notification.payload)
+ }
+ }
+ }
+ }
+
+ func processPayload(payload: Payload) {
+ // If is a encrypted message
+ if payload.messageType == .e2e {
+ if let message = payload.msg, let rid = payload.rid {
+ if let decryptedMessage = rocketchat?.decryptMessage(rid: rid, message: message) {
+ bestAttemptContent?.body = decryptedMessage
+ if let roomType = payload.type, roomType == .group, let sender = payload.senderName {
+ bestAttemptContent?.body = "\(sender): \(decryptedMessage)"
+ }
}
}
-
- task.resume()
}
- override func serviceExtensionTimeWillExpire() {
- // Called just before the extension will be terminated by the system.
- // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
- if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
- contentHandler(bestAttemptContent)
- }
+ if let bestAttemptContent = bestAttemptContent {
+ contentHandler?(bestAttemptContent)
}
-
+ }
}
diff --git a/ios/Podfile b/ios/Podfile
index c1eca515e..930a5f78d 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -18,6 +18,11 @@ target 'ShareRocketChatRN' do
all_pods
end
+# used to get user credentials
+target 'NotificationService' do
+ all_pods
+end
+
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index c72afcec8..3d68ca9f0 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -184,6 +184,9 @@ PODS:
- libwebp/mux (1.1.0):
- libwebp/demux
- libwebp/webp (1.1.0)
+ - MMKV (1.2.1):
+ - MMKVCore (~> 1.2.1)
+ - MMKVCore (1.2.1)
- nanopb (1.30905.0):
- nanopb/decode (= 1.30905.0)
- nanopb/encode (= 1.30905.0)
@@ -365,17 +368,25 @@ PODS:
- React
- react-native-cameraroll (4.0.0):
- React
+ - react-native-cookies (4.0.0):
+ - React
- react-native-document-picker (3.5.3):
- React
- react-native-jitsi-meet (2.1.1):
- JitsiMeetSDK (= 2.8.1)
- React
+ - react-native-mmkv-storage (0.3.5):
+ - MMKV (= 1.2.1)
+ - React
- react-native-notifications (2.1.7):
- React
- react-native-orientation-locker (1.1.8):
- React
- react-native-safe-area-context (3.1.1):
- React
+ - react-native-simple-crypto (0.4.0):
+ - OpenSSL-Universal
+ - React
- react-native-slider (3.0.2):
- React
- react-native-webview (10.3.2):
@@ -456,6 +467,8 @@ PODS:
- React
- RNCMaskedView (0.1.10):
- React
+ - RNConfigReader (1.0.0):
+ - React
- RNDateTimePicker (2.6.0):
- React
- RNDeviceInfo (5.6.2):
@@ -495,8 +508,6 @@ PODS:
- React
- RNScreens (2.9.0):
- React
- - RNUserDefaults (1.8.1):
- - React
- RNVectorIcons (7.0.0):
- React
- SDWebImage (5.8.4):
@@ -580,11 +591,14 @@ DEPENDENCIES:
- react-native-appearance (from `../node_modules/react-native-appearance`)
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
+ - "react-native-cookies (from `../node_modules/@react-native-community/cookies`)"
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-jitsi-meet (from `../node_modules/react-native-jitsi-meet`)
+ - react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
- react-native-notifications (from `../node_modules/react-native-notifications`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
+ - react-native-simple-crypto (from `../node_modules/react-native-simple-crypto`)
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-webview (from `../node_modules/react-native-webview`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
@@ -605,6 +619,7 @@ DEPENDENCIES:
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
- "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
+ - RNConfigReader (from `../node_modules/react-native-config-reader`)
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- "RNFastImage (from `../node_modules/@rocket.chat/react-native-fast-image`)"
@@ -617,7 +632,6 @@ DEPENDENCIES:
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNRootView (from `../node_modules/rn-root-view`)
- RNScreens (from `../node_modules/react-native-screens`)
- - RNUserDefaults (from `../node_modules/rn-user-defaults`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
@@ -658,6 +672,8 @@ SPEC REPOS:
- GoogleUtilities
- JitsiMeetSDK
- libwebp
+ - MMKV
+ - MMKVCore
- nanopb
- OpenSSL-Universal
- PromisesObjC
@@ -729,16 +745,22 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-background-timer"
react-native-cameraroll:
:path: "../node_modules/@react-native-community/cameraroll"
+ react-native-cookies:
+ :path: "../node_modules/@react-native-community/cookies"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-jitsi-meet:
:path: "../node_modules/react-native-jitsi-meet"
+ react-native-mmkv-storage:
+ :path: "../node_modules/react-native-mmkv-storage"
react-native-notifications:
:path: "../node_modules/react-native-notifications"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
+ react-native-simple-crypto:
+ :path: "../node_modules/react-native-simple-crypto"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-webview:
@@ -779,6 +801,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/async-storage"
RNCMaskedView:
:path: "../node_modules/@react-native-community/masked-view"
+ RNConfigReader:
+ :path: "../node_modules/react-native-config-reader"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNDeviceInfo:
@@ -803,8 +827,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/rn-root-view"
RNScreens:
:path: "../node_modules/react-native-screens"
- RNUserDefaults:
- :path: "../node_modules/rn-user-defaults"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
UMAppLoader:
@@ -877,6 +899,8 @@ SPEC CHECKSUMS:
JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
+ MMKV: 67253edee25a34edf332f91d73fa94a9e038b971
+ MMKVCore: fe398984acac1fa33f92795d1b5fd0a334c944af
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
@@ -893,11 +917,14 @@ SPEC CHECKSUMS:
react-native-appearance: 0f0e5fc2fcef70e03d48c8fe6b00b9158c2ba8aa
react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4
react-native-cameraroll: ae0a7c0cc8462508855707ff623b1e789b692865
+ react-native-cookies: d79e5015a5d3a38e08f5cb39c4948532be7e9c2b
react-native-document-picker: 825552b827012282baf4b7cbf119d3b37a280c90
react-native-jitsi-meet: f89bcb2cfbd5b15403b9c40738036c4f1af45d05
+ react-native-mmkv-storage: 48729fe90e850ef2fdc9d3714b7030c7c51d82b0
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
react-native-safe-area-context: 344b969c45af3d8464d36e8dea264942992ef033
+ react-native-simple-crypto: 564740fd8124827d82e9e8ded4a0de8c695c8a4d
react-native-slider: 0221b417686c5957f6e77cd9ac22c1478a165355
react-native-webview: 679b6f400176e2ea8a785acf7ae16cf282e7d1eb
React-RCTActionSheet: 1702a1a85e550b5c36e2e03cb2bd3adea053de95
@@ -918,6 +945,7 @@ SPEC CHECKSUMS:
RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d
RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
+ RNConfigReader: 396da6a6444182a76e8ae0930b9436c7575045cb
RNDateTimePicker: e386ff4ef3300964ed0cad97ce6f206e0effbfdb
RNDeviceInfo: ed8557a8bd6443cbc0ab5d375e6808a38a279744
RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9
@@ -930,7 +958,6 @@ SPEC CHECKSUMS:
RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
- RNUserDefaults: c421fd97ad06b35c16608c5d0fe675db353f632d
RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976
SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
@@ -951,6 +978,6 @@ SPEC CHECKSUMS:
Yoga: d5bd05a2b6b94c52323745c2c2b64557c8c66f64
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: 55c04243097892160d63f79f3a23157165b7ac68
+PODFILE CHECKSUM: 19c78b598c807d2c6b988fd11b24544ac1895a35
COCOAPODS: 1.9.3
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
deleted file mode 100644
index 0d1a36f66..000000000
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file 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
-
-@class FIRAnalyticsConfiguration;
-
-@interface FIRConfiguration ()
-
-/**
- * The configuration class for Firebase Analytics. This should be removed once the logic for
- * enabling and disabling Analytics is moved to Analytics.
- */
-@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration;
-
-@end
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
deleted file mode 100644
index ac5ef2c4f..000000000
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file 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
-
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Implements the FIRCoreDiagnosticsData protocol to log diagnostics data. */
-@interface FIRDiagnosticsData : NSObject
-
-/** Inserts values into the diagnosticObjects dictionary if the value isn't nil.
- *
- * @param value The value to insert if it's not nil.
- * @param key The key to associate it with.
- */
-- (void)insertValue:(nullable id)value forKey:(NSString *)key;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
deleted file mode 100644
index 2b0eb710c..000000000
--- a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file 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
-
-#import "FIRCoreDiagnosticsData.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */
-@protocol FIRCoreDiagnosticsInterop
-
-/** Sends the given diagnostics data.
- *
- * @param diagnosticsData The diagnostics data object to send.
- */
-+ (void)sendDiagnosticsData:(id)diagnosticsData;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md b/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md
deleted file mode 100644
index 3ddc8fbd2..000000000
--- a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
-
-This repository contains a subset of the Firebase iOS SDK source. It currently
-includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase,
-FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
-FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and
-FirebaseStorage.
-
-The repository also includes GoogleUtilities source. The
-[GoogleUtilities](GoogleUtilities/README.md) pod is
-a set of utilities used by Firebase and other Google products.
-
-Firebase is an app development platform with tools to help you build, grow and
-monetize your app. More information about Firebase can be found at
-[https://firebase.google.com](https://firebase.google.com).
-
-## Installation
-
-See the three subsections for details about three different installation methods.
-1. [Standard pod install](README.md#standard-pod-install)
-1. [Installing from the GitHub repo](README.md#installing-from-github)
-1. [Experimental Carthage](README.md#carthage-ios-only)
-
-### Standard pod install
-
-Go to
-[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
-
-### Installing from GitHub
-
-For releases starting with 5.0.0, the source for each release is also deployed
-to CocoaPods master and available via standard
-[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
-
-These instructions can be used to access the Firebase repo at other branches,
-tags, or commits.
-
-#### Background
-
-See
-[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
-for instructions and options about overriding pod source locations.
-
-#### Accessing Firebase Source Snapshots
-
-All of the official releases are tagged in this repo and available via CocoaPods. To access a local
-source snapshot or unreleased branch, use Podfile directives like the following:
-
-To access FirebaseFirestore via a branch:
-```
-pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
-pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
-```
-
-To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
-
-```
-pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
-pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
-```
-
-### Carthage (iOS only)
-
-Instructions for the experimental Carthage distribution are at
-[Carthage](Carthage.md).
-
-### Rome
-
-Instructions for installing binary frameworks via
-[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
-
-## Development
-
-To develop Firebase software in this repository, ensure that you have at least
-the following software:
-
- * Xcode 10.1 (or later)
- * CocoaPods 1.7.2 (or later)
- * [CocoaPods generate](https://github.com/square/cocoapods-generate)
-
-For the pod that you want to develop:
-
-`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios`
-
-Note: If the CocoaPods cache is out of date, you may need to run
-`pod repo update` before the `pod gen` command.
-
-Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
-those platforms. Since 10.2, Xcode does not properly handle multi-platform
-CocoaPods workspaces.
-
-Firestore has a self contained Xcode project. See
-[Firestore/README.md](Firestore/README.md).
-
-### Development for Catalyst
-* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
-* Check the Mac box in the App-iOS Build Settings
-* Sign the App in the Settings Signing & Capabilities tab
-* Click Pods in the Project Manager
-* Add Signing to the iOS host app and unit test targets
-* Select the Unit-unit scheme
-* Run it to build and test
-
-### Adding a New Firebase Pod
-
-See [AddNewPod.md](AddNewPod.md).
-
-### Code Formatting
-
-To ensure that the code is formatted consistently, run the script
-[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
-before creating a PR.
-
-Travis will verify that any code changes are done in a style compliant way. Install
-`clang-format` and `swiftformat`.
-These commands will get the right versions:
-
-```
-brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb
-brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb
-```
-
-Note: if you already have a newer version of these installed you may need to
-`brew switch` to this version.
-
-To update this section, find the versions of clang-format and swiftformat.rb to
-match the versions in the CI failure logs
-[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
-
-### Running Unit Tests
-
-Select a scheme and press Command-u to build a component and run its unit tests.
-
-#### Viewing Code Coverage
-
-First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
-
-After running the `AllUnitTests_iOS` scheme in Xcode, execute
-`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
-at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
-
-### Running Sample Apps
-In order to run the sample apps and integration tests, you'll need valid
-`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
-files without real values, but can be replaced with real plist files. To get your own
-`GoogleService-Info.plist` files:
-
-1. Go to the [Firebase Console](https://console.firebase.google.com/)
-2. Create a new Firebase project, if you don't already have one
-3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
-identifier (e.g. `com.google.Database-Example`)
-4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
-(e.g. in [Example/Database/App/](Example/Database/App/));
-
-Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
-special Apple capabilities, and you will have to change the sample app to use a unique bundle
-identifier that you can control in your own Apple Developer account.
-
-## Specific Component Instructions
-See the sections below for any special instructions for those components.
-
-### Firebase Auth
-
-If you're doing specific Firebase Auth development, see
-[the Auth Sample README](Example/Auth/README.md) for instructions about
-building and running the FirebaseAuth pod along with various samples and tests.
-
-### Firebase Database
-
-To run the Database Integration tests, make your database authentication rules
-[public](https://firebase.google.com/docs/database/security/quickstart).
-
-### Firebase Storage
-
-To run the Storage Integration tests, follow the instructions in
-[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
-
-#### Push Notifications
-
-Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
-In order to actually test receiving push notifications, you will need to:
-
-1. Change the bundle identifier of the sample app to something you own in your Apple Developer
-account, and enable that App ID for push notifications.
-2. You'll also need to
-[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
-at **Project Settings > Cloud Messaging > [Your Firebase App]**.
-3. Ensure your iOS device is added to your Apple Developer portal as a test device.
-
-#### iOS Simulator
-
-The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
-In order to receive push notifications, you'll have to follow the steps above and run the app on a
-physical device.
-
-## Community Supported Efforts
-
-We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
-very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
-participate in the Firebase community.
-
-### tvOS, macOS, and Catalyst
-Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore,
-FirebaseDatabase, FirebaseMessaging, FirebaseFirestore,
-FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on
-tvOS, macOS, and Catalyst.
-
-For tvOS, checkout the [Sample](Example/tvOSSample).
-
-Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this
-repository is actively developed primarily for iOS. While we can catch basic unit test issues with
-Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you
-encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
-
-To install, add a subset of the following to the Podfile:
-
-```
-pod 'Firebase/ABTesting'
-pod 'Firebase/Auth'
-pod 'Firebase/Database'
-pod 'Firebase/Firestore'
-pod 'Firebase/Functions'
-pod 'Firebase/Messaging'
-pod 'Firebase/RemoteConfig'
-pod 'Firebase/Storage'
-```
-
-#### Additional Catalyst Notes
-
-* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability`
-to Build Settings.
-* FirebaseFirestore requires signing the
-[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681).
-
-## Roadmap
-
-See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
-plans and directions.
-
-## Contributing
-
-See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
-iOS SDK.
-
-## License
-
-The contents of this repository is licensed under the
-[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
-
-Your use of Firebase is governed by the
-[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
diff --git a/ios/Pods/Headers/Private/MMKV/MMKV.h b/ios/Pods/Headers/Private/MMKV/MMKV.h
new file mode 120000
index 000000000..4f5e989c7
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKV/MMKV.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKV/MMKVHandler.h b/ios/Pods/Headers/Private/MMKV/MMKVHandler.h
new file mode 120000
index 000000000..5a6f0ae78
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKV/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h b/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h
new file mode 120000
index 000000000..f0fa1cea6
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/AESCrypt.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/AESCrypt.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/Checksum.h b/ios/Pods/Headers/Private/MMKVCore/Checksum.h
new file mode 120000
index 000000000..2ba790413
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/Checksum.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/crc32/Checksum.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h b/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h
new file mode 120000
index 000000000..1b36d4f54
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedInputData.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedInputData.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h b/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h
new file mode 120000
index 000000000..3ecc48324
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedInputDataCrypt.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedInputDataCrypt.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h b/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h
new file mode 120000
index 000000000..1c594fb1a
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/CodedOutputData.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/CodedOutputData.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h b/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h
new file mode 120000
index 000000000..86bc340a8
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/InterProcessLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/InterProcessLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h b/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h
new file mode 120000
index 000000000..8f2cd6ba1
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/KeyValueHolder.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/KeyValueHolder.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h b/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h
new file mode 120000
index 000000000..af88e07e2
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMBuffer.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMBuffer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV.h b/ios/Pods/Headers/Private/MMKVCore/MMKV.h
new file mode 120000
index 000000000..cc0beb9ab
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h b/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h
new file mode 120000
index 000000000..a6df22a42
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVLog.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVLog.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp b/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp
new file mode 120000
index 000000000..37044f687
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVMetaInfo.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVMetaInfo.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h b/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h
new file mode 120000
index 000000000..f74a9295b
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKVPredef.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVPredef.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h b/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h
new file mode 120000
index 000000000..b18d89472
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV_IO.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV_IO.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h b/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h
new file mode 120000
index 000000000..774966c9b
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MMKV_OSX.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV_OSX.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h b/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h
new file mode 120000
index 000000000..f12f713dd
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MemoryFile.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MemoryFile.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h b/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h
new file mode 120000
index 000000000..c63b74fdd
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/MiniPBCoder.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MiniPBCoder.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp b/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp
new file mode 120000
index 000000000..f6b73e356
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/PBEncodeItem.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBEncodeItem.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/PBUtility.h b/ios/Pods/Headers/Private/MMKVCore/PBUtility.h
new file mode 120000
index 000000000..702dc11df
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/PBUtility.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBUtility.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp b/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp
new file mode 120000
index 000000000..5af1050e4
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/ScopedLock.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ScopedLock.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h b/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h
new file mode 120000
index 000000000..07de54af3
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/ThreadLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ThreadLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h b/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h
new file mode 120000
index 000000000..e0a13cf57
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_aes.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_aes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h b/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h
new file mode 120000
index 000000000..0240f69a5
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_aes_locl.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_aes_locl.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h b/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h
new file mode 120000
index 000000000..9ddb4b074
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_arm_arch.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_arm_arch.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h
new file mode 120000
index 000000000..39357984e
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md32_common.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md32_common.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h
new file mode 120000
index 000000000..a0d159cc9
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md5.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h b/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h
new file mode 120000
index 000000000..14f05fcc7
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_md5_locl.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5_locl.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h b/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h
new file mode 120000
index 000000000..872a2ff7e
--- /dev/null
+++ b/ios/Pods/Headers/Private/MMKVCore/openssl_opensslconf.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_opensslconf.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/RNConfigReader/RNConfigReader.h b/ios/Pods/Headers/Private/RNConfigReader/RNConfigReader.h
new file mode 120000
index 000000000..e2603266a
--- /dev/null
+++ b/ios/Pods/Headers/Private/RNConfigReader/RNConfigReader.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-config-reader/ios/RNConfigReader.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h b/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h
deleted file mode 120000
index 5ca38888c..000000000
--- a/ios/Pods/Headers/Private/RNUserDefaults/RNUserDefaults.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../node_modules/rn-user-defaults/ios/RNUserDefaults.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-cookies/RNCookieManagerIOS.h b/ios/Pods/Headers/Private/react-native-cookies/RNCookieManagerIOS.h
new file mode 120000
index 000000000..336944fb1
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-cookies/RNCookieManagerIOS.h
@@ -0,0 +1 @@
+../../../../../node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h
new file mode 120000
index 000000000..2033a4eb9
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/IDStore.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/IDStore.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h
new file mode 120000
index 000000000..4a9e0777c
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/MMKVStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h
new file mode 120000
index 000000000..a66b4c9a1
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/SecureStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/SecureStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h
new file mode 120000
index 000000000..9205f0b0c
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageGetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageGetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h
new file mode 120000
index 000000000..ea59219f6
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageIndexer.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageIndexer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h
new file mode 120000
index 000000000..b2a9f86a7
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-mmkv-storage/StorageSetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageSetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Aes.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Aes.h
new file mode 120000
index 000000000..d377132f8
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Aes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Aes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Hmac.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Hmac.h
new file mode 120000
index 000000000..2c2f9b320
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Hmac.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Hmac.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Pbkdf2.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Pbkdf2.h
new file mode 120000
index 000000000..7227013e1
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Pbkdf2.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Pbkdf2.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTAes.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTAes.h
new file mode 120000
index 000000000..8c99a193f
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTAes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTAes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTCrypto-Bridging-Header.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTCrypto-Bridging-Header.h
new file mode 120000
index 000000000..a9219b0d5
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTCrypto-Bridging-Header.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTCrypto-Bridging-Header.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTHmac.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTHmac.h
new file mode 120000
index 000000000..122ed0685
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTHmac.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTHmac.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTPbkdf2.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTPbkdf2.h
new file mode 120000
index 000000000..30dd7a9ac
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTPbkdf2.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTPbkdf2.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTRsa.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTRsa.h
new file mode 120000
index 000000000..4bb5d2689
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTRsa.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTRsa.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RCTSha.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTSha.h
new file mode 120000
index 000000000..516a413d3
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RCTSha.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTSha.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RNRandomBytes.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RNRandomBytes.h
new file mode 120000
index 000000000..035346223
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RNRandomBytes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RNRandomBytes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Rsa.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Rsa.h
new file mode 120000
index 000000000..cc33170b4
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Rsa.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Rsa.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/RsaFormatter.h b/ios/Pods/Headers/Private/react-native-simple-crypto/RsaFormatter.h
new file mode 120000
index 000000000..4b70d4ab2
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/RsaFormatter.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/RsaFormatter.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Sha.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Sha.h
new file mode 120000
index 000000000..e595e50bc
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Sha.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Sha.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Private/react-native-simple-crypto/Shared.h b/ios/Pods/Headers/Private/react-native-simple-crypto/Shared.h
new file mode 120000
index 000000000..dc5ce15de
--- /dev/null
+++ b/ios/Pods/Headers/Private/react-native-simple-crypto/Shared.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Shared.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKV/MMKV.h b/ios/Pods/Headers/Public/MMKV/MMKV.h
new file mode 120000
index 000000000..4f5e989c7
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKV/MMKV.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKV/MMKVHandler.h b/ios/Pods/Headers/Public/MMKV/MMKVHandler.h
new file mode 120000
index 000000000..5a6f0ae78
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKV/MMKVHandler.h
@@ -0,0 +1 @@
+../../../MMKV/iOS/MMKV/MMKV/MMKVHandler.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h b/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h
new file mode 120000
index 000000000..af88e07e2
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMBuffer.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMBuffer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKV.h b/ios/Pods/Headers/Public/MMKVCore/MMKV.h
new file mode 120000
index 000000000..cc0beb9ab
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKV.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKV.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h b/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h
new file mode 120000
index 000000000..a6df22a42
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKVLog.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVLog.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h b/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h
new file mode 120000
index 000000000..f74a9295b
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/MMKVPredef.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/MMKVPredef.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/PBUtility.h b/ios/Pods/Headers/Public/MMKVCore/PBUtility.h
new file mode 120000
index 000000000..702dc11df
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/PBUtility.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/PBUtility.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp b/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp
new file mode 120000
index 000000000..5af1050e4
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/ScopedLock.hpp
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ScopedLock.hpp
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h b/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h
new file mode 120000
index 000000000..07de54af3
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/ThreadLock.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/ThreadLock.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h b/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h
new file mode 120000
index 000000000..a0d159cc9
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/openssl_md5.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_md5.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h b/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h
new file mode 120000
index 000000000..872a2ff7e
--- /dev/null
+++ b/ios/Pods/Headers/Public/MMKVCore/openssl_opensslconf.h
@@ -0,0 +1 @@
+../../../MMKVCore/Core/aes/openssl/openssl_opensslconf.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/RNConfigReader/RNConfigReader.h b/ios/Pods/Headers/Public/RNConfigReader/RNConfigReader.h
new file mode 120000
index 000000000..e2603266a
--- /dev/null
+++ b/ios/Pods/Headers/Public/RNConfigReader/RNConfigReader.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-config-reader/ios/RNConfigReader.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h b/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h
deleted file mode 120000
index 5ca38888c..000000000
--- a/ios/Pods/Headers/Public/RNUserDefaults/RNUserDefaults.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../node_modules/rn-user-defaults/ios/RNUserDefaults.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-cookies/RNCookieManagerIOS.h b/ios/Pods/Headers/Public/react-native-cookies/RNCookieManagerIOS.h
new file mode 120000
index 000000000..336944fb1
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-cookies/RNCookieManagerIOS.h
@@ -0,0 +1 @@
+../../../../../node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h
new file mode 120000
index 000000000..2033a4eb9
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/IDStore.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/IDStore.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h
new file mode 120000
index 000000000..4a9e0777c
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/MMKVStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h
new file mode 120000
index 000000000..a66b4c9a1
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/SecureStorage.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/SecureStorage.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h
new file mode 120000
index 000000000..9205f0b0c
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageGetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageGetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h
new file mode 120000
index 000000000..ea59219f6
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageIndexer.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageIndexer.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h
new file mode 120000
index 000000000..b2a9f86a7
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-mmkv-storage/StorageSetters.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-mmkv-storage/ios/StorageSetters.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Aes.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Aes.h
new file mode 120000
index 000000000..d377132f8
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Aes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Aes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Hmac.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Hmac.h
new file mode 120000
index 000000000..2c2f9b320
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Hmac.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Hmac.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Pbkdf2.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Pbkdf2.h
new file mode 120000
index 000000000..7227013e1
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Pbkdf2.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Pbkdf2.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTAes.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTAes.h
new file mode 120000
index 000000000..8c99a193f
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTAes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTAes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTCrypto-Bridging-Header.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTCrypto-Bridging-Header.h
new file mode 120000
index 000000000..a9219b0d5
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTCrypto-Bridging-Header.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTCrypto-Bridging-Header.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTHmac.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTHmac.h
new file mode 120000
index 000000000..122ed0685
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTHmac.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTHmac.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTPbkdf2.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTPbkdf2.h
new file mode 120000
index 000000000..30dd7a9ac
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTPbkdf2.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTPbkdf2.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTRsa.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTRsa.h
new file mode 120000
index 000000000..4bb5d2689
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTRsa.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTRsa.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RCTSha.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTSha.h
new file mode 120000
index 000000000..516a413d3
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RCTSha.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RCTSha.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RNRandomBytes.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RNRandomBytes.h
new file mode 120000
index 000000000..035346223
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RNRandomBytes.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/RNRandomBytes.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Rsa.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Rsa.h
new file mode 120000
index 000000000..cc33170b4
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Rsa.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Rsa.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/RsaFormatter.h b/ios/Pods/Headers/Public/react-native-simple-crypto/RsaFormatter.h
new file mode 120000
index 000000000..4b70d4ab2
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/RsaFormatter.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/RsaFormatter.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Sha.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Sha.h
new file mode 120000
index 000000000..e595e50bc
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Sha.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Sha.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react-native-simple-crypto/Shared.h b/ios/Pods/Headers/Public/react-native-simple-crypto/Shared.h
new file mode 120000
index 000000000..dc5ce15de
--- /dev/null
+++ b/ios/Pods/Headers/Public/react-native-simple-crypto/Shared.h
@@ -0,0 +1 @@
+../../../../../node_modules/react-native-simple-crypto/ios/RCTCrypto/lib/Shared.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto-umbrella.h b/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto-umbrella.h
new file mode 120000
index 000000000..b4eb9e495
--- /dev/null
+++ b/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto-umbrella.h
@@ -0,0 +1 @@
+../../../Target Support Files/react-native-simple-crypto/react-native-simple-crypto-umbrella.h
\ No newline at end of file
diff --git a/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto.modulemap b/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto.modulemap
new file mode 120000
index 000000000..9e279741d
--- /dev/null
+++ b/ios/Pods/Headers/Public/react_native_simple_crypto/react-native-simple-crypto.modulemap
@@ -0,0 +1 @@
+../../../Target Support Files/react-native-simple-crypto/react-native-simple-crypto.modulemap
\ No newline at end of file
diff --git a/ios/Pods/Local Podspecs/RNConfigReader.podspec.json b/ios/Pods/Local Podspecs/RNConfigReader.podspec.json
new file mode 100644
index 000000000..976786357
--- /dev/null
+++ b/ios/Pods/Local Podspecs/RNConfigReader.podspec.json
@@ -0,0 +1,25 @@
+{
+ "name": "RNConfigReader",
+ "version": "1.0.0",
+ "summary": "RNConfigReader",
+ "description": "RNConfigReader",
+ "homepage": "https://github.com/csath/react-native-config-reader",
+ "license": "MIT",
+ "authors": {
+ "author": "author@domain.cn"
+ },
+ "platforms": {
+ "ios": "7.0"
+ },
+ "source": {
+ "git": "https://github.com/author/RNConfigReader.git",
+ "tag": "master"
+ },
+ "source_files": "ios/**/*",
+ "requires_arc": true,
+ "dependencies": {
+ "React": [
+
+ ]
+ }
+}
diff --git a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json b/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
deleted file mode 100644
index b54949a2b..000000000
--- a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "RNUserDefaults",
- "version": "1.8.1",
- "summary": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
- "description": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
- "license": "MIT",
- "authors": "djorkaeffalexandre",
- "homepage": "https://github.com/RocketChat/rn-user-defaults.git",
- "source": {
- "git": "https://github.com/RocketChat/rn-user-defaults.git"
- },
- "requires_arc": true,
- "platforms": {
- "ios": "7.0"
- },
- "preserve_paths": [
- "README.md",
- "package.json",
- "index.js"
- ],
- "source_files": "iOS/*.{h,m}",
- "dependencies": {
- "React": [
-
- ]
- }
-}
diff --git a/ios/Pods/Local Podspecs/react-native-cookies.podspec.json b/ios/Pods/Local Podspecs/react-native-cookies.podspec.json
new file mode 100644
index 000000000..9d1b7d762
--- /dev/null
+++ b/ios/Pods/Local Podspecs/react-native-cookies.podspec.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-native-cookies",
+ "version": "4.0.0",
+ "summary": "Cookie Manager for React Native",
+ "homepage": "https://github.com/react-native-community/cookies#readme",
+ "license": "MIT",
+ "authors": {
+ "Jason Safaiyeh": "safaiyeh@protonmail.com"
+ },
+ "source": {
+ "git": "git@github.com:react-native-community/cookies.git",
+ "tag": "v4.0.0"
+ },
+ "requires_arc": true,
+ "platforms": {
+ "ios": "7.0"
+ },
+ "preserve_paths": "*.framework",
+ "source_files": "ios/**/*.{h,m}",
+ "dependencies": {
+ "React": [
+
+ ]
+ }
+}
diff --git a/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json b/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json
new file mode 100644
index 000000000..1a6d0d811
--- /dev/null
+++ b/ios/Pods/Local Podspecs/react-native-mmkv-storage.podspec.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-native-mmkv-storage",
+ "version": "0.3.5",
+ "summary": "This library aims to provide a fast & reliable solution for you data storage needs in react-native apps. It uses [MMKV](https://github.com/Tencent/MMKV) by Tencent under the hood on Android and iOS both that is used by their WeChat app(more than 1 Billion users). Unlike other storage solutions for React Native, this library lets you store any kind of data type, in any number of database instances, with or without encryption in a very fast and efficient way.",
+ "homepage": "https://github.com/ammarahm-ed/react-native-mmkv-storage",
+ "license": "MIT",
+ "authors": "Ammar Ahmed for @ammarahm-ed",
+ "platforms": {
+ "ios": "9.0"
+ },
+ "source": {
+ "git": "https://github.com/ammarahm-ed/react-native-mmkv-storage",
+ "tag": "V0.3.5"
+ },
+ "source_files": "ios/**/*.{h,m}",
+ "requires_arc": true,
+ "dependencies": {
+ "React": [
+
+ ],
+ "MMKV": [
+ "1.2.1"
+ ]
+ }
+}
diff --git a/ios/Pods/Local Podspecs/react-native-simple-crypto.podspec.json b/ios/Pods/Local Podspecs/react-native-simple-crypto.podspec.json
new file mode 100644
index 000000000..3a07a76ca
--- /dev/null
+++ b/ios/Pods/Local Podspecs/react-native-simple-crypto.podspec.json
@@ -0,0 +1,24 @@
+{
+ "name": "react-native-simple-crypto",
+ "version": "0.4.0",
+ "summary": "A simpler React-Native crypto library",
+ "authors": "Gary Button ",
+ "license": "MIT",
+ "requires_arc": true,
+ "homepage": "https://github.com/ghbutton/react-native-simple-crypto",
+ "source": {
+ "git": "git+https://github.com/ghbutton/react-native-simple-crypto.git"
+ },
+ "platforms": {
+ "ios": "8.0"
+ },
+ "source_files": "ios/**/*.{h,m,swift}",
+ "dependencies": {
+ "React": [
+
+ ],
+ "OpenSSL-Universal": [
+
+ ]
+ }
+}
diff --git a/ios/Pods/MMKV/LICENSE.TXT b/ios/Pods/MMKV/LICENSE.TXT
new file mode 100644
index 000000000..096acfb25
--- /dev/null
+++ b/ios/Pods/MMKV/LICENSE.TXT
@@ -0,0 +1,189 @@
+Tencent is pleased to support the open source community by making MMKV available.
+Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
+If you have downloaded a copy of the MMKV binary from Tencent, please note that the MMKV binary is licensed under the BSD 3-Clause License.
+If you have downloaded a copy of the MMKV source code from Tencent, please note that MMKV source code is licensed under the BSD 3-Clause License, except for the third-party components listed below which are subject to different license terms. Your integration of MMKV into your own projects may require compliance with the BSD 3-Clause License, as well as the other licenses applicable to the third-party components included within MMKV.
+A copy of the BSD 3-Clause License is included in this file.
+
+Other dependencies and licenses:
+
+Open Source Software Licensed Under the OpenSSL License:
+----------------------------------------------------------------------------------------
+1. OpenSSL 1.1.0i
+Copyright (c) 1998-2018 The OpenSSL Project.
+All rights reserved.
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+
+Terms of the OpenSSL License:
+---------------------------------------------------
+LICENSE ISSUES:
+--------------------------------------------------------------------
+
+The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit.
+See below for the actual license texts.
+
+OpenSSL License:
+--------------------------------------------------------------------
+Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All advertising materials mentioning features or use of this software must display the following acknowledgment:
+"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+* This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com).
+
+
+Original SSLeay License:
+--------------------------------------------------------------------
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software must display the following acknowledgement:" This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)" The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]
+
+
+
+Open Source Software Licensed Under the Apache License, Version 2.0:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. MultiprocessSharedPreferences v1.0
+Copyright (C) 2014 seven456@gmail.com
+
+
+Terms of the Apache License, Version 2.0:
+--------------------------------------------------------------------
+Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”
+
+“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+d) If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file 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.
+
+
+
+Open Source Software Licensed Under the zlib License:
+The below software in this distribution may have been modified by THL A29 Limited (“Tencent Modifications”). All Tencent Modifications are Copyright (C) 2018 THL A29 Limited.
+----------------------------------------------------------------------------------------
+1. zlib v1.2.11
+Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+Terms of the zlib License:
+--------------------------------------------------------------------
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+
+
+Terms of the BSD 3-Clause License:
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of [copyright holder] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ios/Pods/MMKV/README.md b/ios/Pods/MMKV/README.md
new file mode 100644
index 000000000..36310c844
--- /dev/null
+++ b/ios/Pods/MMKV/README.md
@@ -0,0 +1,287 @@
+[![license](https://img.shields.io/badge/license-BSD_3-brightgreen.svg?style=flat)](https://github.com/Tencent/MMKV/blob/master/LICENSE.TXT)
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/MMKV/pulls)
+[![Release Version](https://img.shields.io/badge/release-1.2.1-brightgreen.svg)](https://github.com/Tencent/MMKV/releases)
+[![Platform](https://img.shields.io/badge/Platform-%20Android%20%7C%20iOS%2FmacOS%20%7C%20Win32%20%7C%20POSIX-brightgreen.svg)](https://github.com/Tencent/MMKV/wiki/home)
+
+中文版本请参看[这里](./readme_cn.md)
+
+MMKV is an **efficient**, **small**, **easy-to-use** mobile key-value storage framework used in the WeChat application. It's currently available on **Android**, **iOS/macOS**, **Win32** and **POSIX**.
+
+# MMKV for Android
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Android to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 50K in binary size**: MMKV adds about 50K per architecture on App size, and much less when zipped (apk).
+
+
+## Getting Started
+
+### Installation Via Maven
+Add the following lines to `build.gradle` on your app module:
+
+```gradle
+dependencies {
+ implementation 'com.tencent:mmkv-static:1.2.1'
+ // replace "1.2.1" with any available version
+}
+```
+
+For other installation options, see [Android Setup](https://github.com/Tencent/MMKV/wiki/android_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `apply` calls needed.
+Setup MMKV on App startup, say your `Application` class, add these lines:
+
+```Java
+public void onCreate() {
+ super.onCreate();
+
+ String rootDir = MMKV.initialize(this);
+ System.out.println("mmkv root: " + rootDir);
+ //……
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```Java
+import com.tencent.mmkv.MMKV;
+
+MMKV kv = MMKV.defaultMMKV();
+
+kv.encode("bool", true);
+boolean bValue = kv.decodeBool("bool");
+
+kv.encode("int", Integer.MIN_VALUE);
+int iValue = kv.decodeInt("int");
+
+kv.encode("string", "Hello from mmkv");
+String str = kv.decodeString("string");
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Android Tutorial](https://github.com/Tencent/MMKV/wiki/android_tutorial).
+
+## Performance
+Writing random `int` for 1000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_android_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/android_benchmark).
+
+# MMKV for iOS/macOS
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of iOS/macOS to achieve best performance.
+
+* **Easy-to-use**. You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **Less than 30K in binary size**: MMKV adds less than 30K per architecture on App size, and much less when zipped (ipa).
+
+## Getting Started
+
+### Installation Via CocoaPods:
+ 1. Install [CocoaPods](https://guides.CocoaPods.org/using/getting-started.html);
+ 2. Open terminal, `cd` to your project directory, run `pod repo update` to make CocoaPods aware of the latest available MMKV versions;
+ 3. Edit your Podfile, add `pod 'MMKV'` to your app target;
+ 4. Run `pod install`;
+ 5. Open the `.xcworkspace` file generated by CocoaPods;
+ 6. Add `#import ` to your source file and we are done.
+
+For other installation options, see [iOS/macOS Setup](https://github.com/Tencent/MMKV/wiki/iOS_setup).
+
+### Quick Tutorial
+You can use MMKV as you go, no configurations needed. All changes are saved immediately, no `synchronize` calls needed.
+Setup MMKV on App startup, in your `-[MyApp application: didFinishLaunchingWithOptions:]`, add these lines:
+
+```objective-c
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // init MMKV in the main thread
+ [MMKV initializeMMKV:nil];
+
+ //...
+ return YES;
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```objective-c
+MMKV *mmkv = [MMKV defaultMMKV];
+
+[mmkv setBool:YES forKey:@"bool"];
+BOOL bValue = [mmkv getBoolForKey:@"bool"];
+
+[mmkv setInt32:-1024 forKey:@"int32"];
+int32_t iValue = [mmkv getInt32ForKey:@"int32"];
+
+[mmkv setString:@"hello, mmkv" forKey:@"string"];
+NSString *str = [mmkv getStringForKey:@"string"];
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial).
+
+## Performance
+Writing random `int` for 10000 times, we get this chart:
+![](https://github.com/Tencent/MMKV/wiki/assets/profile_mini.png)
+For more benchmark data, please refer to [our benchmark](https://github.com/Tencent/MMKV/wiki/iOS_benchmark).
+
+
+# MMKV for Win32
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of Windows to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 10K in binary size**: MMKV adds about 10K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via Source
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+
+2. Add `Win32/MMKV/MMKV.vcxproj` to your solution;
+3. Add `MMKV` project to your project's dependencies;
+4. Add `$(OutDir)include` to your project's `C/C++` -> `General` -> `Additional Include Directories`;
+5. Add `$(OutDir)` to your project's `Linker` -> `General` -> `Additional Library Directories`;
+6. Add `MMKV.lib` to your project's `Linker` -> `Input` -> `Additional Dependencies`;
+7. Add `#include ` to your source file and we are done.
+
+
+note:
+
+1. MMKV is compiled with `MT/MTd` runtime by default. If your project uses `MD/MDd`, you should change MMKV's setting to match your project's (`C/C++` -> `Code Generation` -> `Runtime Library`), or vise versa.
+2. MMKV is developed with Visual Studio 2017, change the `Platform Toolset` if you use a different version of Visual Studio.
+
+For other installation options, see [Win32 Setup](https://github.com/Tencent/MMKV/wiki/windows_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include
+
+int main() {
+ std::wstring rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [Win32 Tutorial](https://github.com/Tencent/MMKV/wiki/windows_tutorial).
+
+# MMKV for POSIX
+
+## Features
+
+* **Efficient**. MMKV uses mmap to keep memory synced with file, and protobuf to encode/decode values, making the most of POSIX to achieve best performance.
+ * **Multi-Process concurrency**: MMKV supports concurrent read-read and read-write access between processes.
+
+* **Easy-to-use**. You can use MMKV as you go. All changes are saved immediately, no `save`, no `sync` calls needed.
+
+* **Small**.
+ * **A handful of files**: MMKV contains process locks, encode/decode helpers and mmap logics and nothing more. It's really tidy.
+ * **About 7K in binary size**: MMKV adds about 7K on application size, and much less when zipped.
+
+
+## Getting Started
+
+### Installation Via CMake
+1. Getting source code from git repository:
+
+ ```
+ git clone https://github.com/Tencent/MMKV.git
+ ```
+2. Edit your `CMakeLists.txt`, add those lines:
+
+ ```cmake
+ add_subdirectory(mmkv/POSIX/src mmkv)
+ target_link_libraries(MyApp
+ mmkv)
+ ```
+3. Add `#include "MMKV.h"` to your source file and we are done.
+
+For other installation options, see [POSIX Setup](https://github.com/Tencent/MMKV/wiki/posix_setup).
+
+### Quick Tutorial
+You can use MMKV as you go. All changes are saved immediately, no `sync`, no `save` calls needed.
+Setup MMKV on App startup, say in your `main()`, add these lines:
+
+```C++
+#include "MMKV.h"
+
+int main() {
+ std::string rootDir = getYourAppDocumentDir();
+ MMKV::initializeMMKV(rootDir);
+ //...
+}
+```
+
+MMKV has a global instance, that can be used directly:
+
+```C++
+auto mmkv = MMKV::defaultMMKV();
+
+mmkv->set(true, "bool");
+std::cout << "bool = " << mmkv->getBool("bool") << std::endl;
+
+mmkv->set(1024, "int32");
+std::cout << "int32 = " << mmkv->getInt32("int32") << std::endl;
+
+mmkv->set("Hello, MMKV for Win32", "string");
+std::string result;
+mmkv->getString("string", result);
+std::cout << "string = " << result << std::endl;
+```
+
+MMKV also supports **Multi-Process Access**. Full tutorials can be found here [POSIX Tutorial](https://github.com/Tencent/MMKV/wiki/posix_tutorial).
+
+## License
+MMKV is published under the BSD 3-Clause license. For details check out the [LICENSE.TXT](./LICENSE.TXT).
+
+## Change Log
+Check out the [CHANGELOG.md](./CHANGELOG.md) for details of change history.
+
+## Contributing
+
+If you are interested in contributing, check out the [CONTRIBUTING.md](./CONTRIBUTING.md), also join our [Tencent OpenSource Plan](https://opensource.tencent.com/contribution).
+
+To give clarity of what is expected of our members, MMKV has adopted the code of conduct defined by the Contributor Covenant, which is widely used. And we think it articulates our values well. For more, check out the [Code of Conduct](./CODE_OF_CONDUCT.md).
+
+## FAQ & Feedback
+Check out the [FAQ](https://github.com/Tencent/MMKV/wiki/FAQ) first. Should there be any questions, don't hesitate to create [issues](https://github.com/Tencent/MMKV/issues).
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h
new file mode 100644
index 000000000..cf5e2f594
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKV.h
@@ -0,0 +1,222 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 "MMKVHandler.h"
+
+typedef NS_ENUM(NSUInteger, MMKVMode) {
+ MMKVSingleProcess = 0x1,
+ MMKVMultiProcess = 0x2,
+};
+
+@interface MMKV : NSObject
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir NS_SWIFT_NAME(initialize(rootDir:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:logLevel:));
+
+/// call this in main thread, before calling any other MMKV methods
+/// @param rootDir the root dir of MMKV, passing nil defaults to {NSDocumentDirectory}/mmkv
+/// @param groupDir the root dir of multi-process MMKV, MMKV with MMKVMultiProcess mode will be stored in groupDir/mmkv
+/// @param logLevel MMKVLogInfo by default, MMKVLogNone to disable all logging
+/// @return root dir of MMKV
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel NS_SWIFT_NAME(initialize(rootDir:groupDir:logLevel:));
+
+/// a generic purpose instance (in MMKVSingleProcess mode)
++ (nullable instancetype)defaultMMKV;
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID NS_SWIFT_NAME(init(mmapID:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(init(mmapID:cryptKey:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param mode MMKVMultiProcess for multi-process MMKV
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode NS_SWIFT_NAME(init(mmapID:cryptKey:mode:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:relativePath:)) __attribute__((deprecated("use +mmkvWithID:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:rootPath:));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param relativePath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath NS_SWIFT_NAME(init(mmapID:cryptKey:relativePath:)) __attribute__((deprecated("use +mmkvWithID:cryptKey:rootPath: instead")));
+
+/// @param mmapID any unique ID (com.tencent.xin.pay, etc), if you want a per-user mmkv, you could merge user-id within mmapID
+/// @param cryptKey 16 bytes at most
+/// @param rootPath custom path of the file, `NSDocumentDirectory/mmkv` by default
++ (nullable instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey rootPath:(nullable NSString *)rootPath NS_SWIFT_NAME(init(mmapID:cryptKey:rootPath:));
+
+// you can call this on applicationWillTerminate, it's totally fine if you don't call
++ (void)onAppTerminate;
+
++ (NSString *)mmkvBasePath;
+
+// if you want to change the base path, do it BEFORE getting any MMKV instance
+// otherwise the behavior is undefined
++ (void)setMMKVBasePath:(NSString *)basePath __attribute__((deprecated("use +initializeMMKV: instead", "+initializeMMKV:")));
+
+// transform plain text into encrypted text, or vice versa by passing newKey = nil
+// you can change existing crypt key with different key
+- (BOOL)reKey:(nullable NSData *)newKey NS_SWIFT_NAME(reset(cryptKey:));
+- (nullable NSData *)cryptKey;
+
+// just reset cryptKey (will not encrypt or decrypt anything)
+// usually you should call this method after other process reKey() the multi-process mmkv
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey NS_SWIFT_NAME(checkReSet(cryptKey:));
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:));
+
+- (nullable id)getObjectOfClass:(Class)cls forKey:(NSString *)key NS_SWIFT_NAME(object(of:forKey:));
+
+- (BOOL)getBoolForKey:(NSString *)key __attribute__((swift_name("bool(forKey:)")));
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue __attribute__((swift_name("bool(forKey:defaultValue:)")));
+
+- (int32_t)getInt32ForKey:(NSString *)key NS_SWIFT_NAME(int32(forKey:));
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue NS_SWIFT_NAME(int32(forKey:defaultValue:));
+
+- (uint32_t)getUInt32ForKey:(NSString *)key NS_SWIFT_NAME(uint32(forKey:));
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue NS_SWIFT_NAME(uint32(forKey:defaultValue:));
+
+- (int64_t)getInt64ForKey:(NSString *)key NS_SWIFT_NAME(int64(forKey:));
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue NS_SWIFT_NAME(int64(forKey:defaultValue:));
+
+- (uint64_t)getUInt64ForKey:(NSString *)key NS_SWIFT_NAME(uint64(forKey:));
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue NS_SWIFT_NAME(uint64(forKey:defaultValue:));
+
+- (float)getFloatForKey:(NSString *)key NS_SWIFT_NAME(float(forKey:));
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue NS_SWIFT_NAME(float(forKey:defaultValue:));
+
+- (double)getDoubleForKey:(NSString *)key NS_SWIFT_NAME(double(forKey:));
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue NS_SWIFT_NAME(double(forKey:defaultValue:));
+
+- (nullable NSString *)getStringForKey:(NSString *)key NS_SWIFT_NAME(string(forKey:));
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue NS_SWIFT_NAME(string(forKey:defaultValue:));
+
+- (nullable NSDate *)getDateForKey:(NSString *)key NS_SWIFT_NAME(date(forKey:));
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue NS_SWIFT_NAME(date(forKey:defaultValue:));
+
+- (nullable NSData *)getDataForKey:(NSString *)key NS_SWIFT_NAME(data(forKey:));
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue NS_SWIFT_NAME(data(forKey:defaultValue:));
+
+// return the actual size consumption of the key's value
+// Note: might be a little bigger than value's length
+- (size_t)getValueSizeForKey:(NSString *)key NS_SWIFT_NAME(valueSize(forKey:));
+
+// return size written into buffer
+// return -1 on any error
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer NS_SWIFT_NAME(writeValue(forKey:buffer:));
+
+- (BOOL)containsKey:(NSString *)key NS_SWIFT_NAME(contains(key:));
+
+- (size_t)count;
+
+- (size_t)totalSize;
+
+- (size_t)actualSize;
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block;
+- (NSArray *)allKeys;
+
+- (void)removeValueForKey:(NSString *)key NS_SWIFT_NAME(removeValue(forKey:));
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys NS_SWIFT_NAME(removeValues(forKeys:));
+
+- (void)clearAll;
+
+// MMKV's size won't reduce after deleting key-values
+// call this method after lots of deleting if you care about disk usage
+// note that `clearAll` has the similar effect of `trim`
+- (void)trim;
+
+// call this method if the instance is no longer needed in the near future
+// any subsequent call to the instance is undefined behavior
+- (void)close;
+
+// call this method if you are facing memory-warning
+// any subsequent call to the instance will load all key-values from file again
+- (void)clearMemoryCache;
+
+// you don't need to call this, really, I mean it
+// unless you worry about running out of battery
+- (void)sync;
+- (void)async;
+
+// check if content changed by other process
+- (void)checkContentChanged;
+
++ (void)registerHandler:(id)handler;
++ (void)unregiserHandler;
+
+// MMKVLogInfo by default
+// MMKVLogNone to disable all logging
++ (void)setLogLevel:(MMKVLogLevel)logLevel __attribute__((deprecated("use +initializeMMKV:logLevel: instead", "initializeMMKV:nil logLevel")));
+
+// Migrate NSUserDefault data to MMKV
+// return imported count of key-values
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults NS_SWIFT_NAME(migrateFrom(userDefaults:));
+
+// for CrashProtected Only
++ (BOOL)isFileValid:(NSString *)mmapID NS_SWIFT_NAME(isFileValid(for:));
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path NS_SWIFT_NAME(isFileValid(for:rootPath:));
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h
new file mode 100644
index 000000000..80fd57c67
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/MMKVHandler.h
@@ -0,0 +1,60 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKVHandler_h
+#define MMKVHandler_h
+#import
+
+typedef NS_ENUM(NSUInteger, MMKVRecoverStrategic) {
+ MMKVOnErrorDiscard = 0,
+ MMKVOnErrorRecover,
+};
+
+typedef NS_ENUM(NSUInteger, MMKVLogLevel) {
+ MMKVLogDebug = 0, // not available for release/product build
+ MMKVLogInfo = 1, // default level
+ MMKVLogWarning,
+ MMKVLogError,
+ MMKVLogNone, // special level used to disable all log messages
+};
+
+// callback is called on the operating thread of the MMKV instance
+@protocol MMKVHandler
+@optional
+
+// by default MMKV will discard all datas on crc32-check failure
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID;
+
+// by default MMKV will discard all datas on file length mismatch
+// return `MMKVOnErrorRecover` to recover any data on the file
+- (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID;
+
+// by default MMKV will print log using NSLog
+// implement this method to redirect MMKV's log
+- (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message;
+
+// called when content is changed by other process
+// doesn't guarantee real-time notification
+- (void)onMMKVContentChange:(NSString *)mmapID;
+
+@end
+
+#endif /* MMKVHandler_h */
diff --git a/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm b/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm
new file mode 100644
index 000000000..0abce7635
--- /dev/null
+++ b/ios/Pods/MMKV/iOS/MMKV/MMKV/libMMKV.mm
@@ -0,0 +1,699 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 "MMKV.h"
+#import
+#import
+#import
+#import
+#import
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+#import
+#endif
+
+using namespace std;
+
+static NSMutableDictionary *g_instanceDic = nil;
+static mmkv::ThreadLock *g_lock;
+static id g_callbackHandler = nil;
+static bool g_isLogRedirecting = false;
+static NSString *g_basePath = nil;
+static NSString *g_groupPath = nil;
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+static BOOL g_isRunningInAppExtension = NO;
+#endif
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message);
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType);
+static void ContentChangeHandler(const string &mmapID);
+
+@implementation MMKV {
+ NSString *m_mmapID;
+ NSString *m_mmapKey;
+ mmkv::MMKV *m_mmkv;
+}
+
+#pragma mark - init
+
+// protect from some old code that don't call +initializeMMKV:
++ (void)initialize {
+ if (self == MMKV.class) {
+ g_instanceDic = [[NSMutableDictionary alloc] init];
+ g_lock = new mmkv::ThreadLock();
+ g_lock->initialize();
+
+ mmkv::MMKV::minimalInit([self mmkvBasePath].UTF8String);
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ // just in case someone forget to set the MMKV_IOS_EXTENSION macro
+ if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) {
+ g_isRunningInAppExtension = YES;
+ }
+ if (!g_isRunningInAppExtension) {
+ auto appState = [UIApplication sharedApplication].applicationState;
+ auto isInBackground = (appState == UIApplicationStateBackground);
+ mmkv::MMKV::setIsInBackground(isInBackground);
+ MMKVInfo("appState:%ld", (long) appState);
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
+ }
+#endif
+ }
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir {
+ return [MMKV initializeMMKV:rootDir logLevel:MMKVLogInfo];
+}
+
+static BOOL g_hasCalledInitializeMMKV = NO;
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir logLevel:(MMKVLogLevel)logLevel {
+ if (g_hasCalledInitializeMMKV) {
+ MMKVWarning("already called +initializeMMKV before, ignore this request");
+ return [self mmkvBasePath];
+ }
+ g_hasCalledInitializeMMKV = YES;
+
+ g_basePath = (rootDir != nil) ? rootDir : [self mmkvBasePath];
+ mmkv::MMKV::initializeMMKV(g_basePath.UTF8String, (mmkv::MMKVLogLevel) logLevel);
+
+ return [self mmkvBasePath];
+}
+
++ (NSString *)initializeMMKV:(nullable NSString *)rootDir groupDir:(NSString *)groupDir logLevel:(MMKVLogLevel)logLevel {
+ auto ret = [MMKV initializeMMKV:rootDir logLevel:logLevel];
+
+ g_groupPath = [groupDir stringByAppendingPathComponent:@"mmkv"];
+ MMKVInfo("groupDir: %@", g_groupPath);
+
+ return ret;
+}
+
+// a generic purpose instance
++ (instancetype)defaultMMKV {
+ return [MMKV mmkvWithID:(@"" DEFAULT_MMAP_ID) cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
+// any unique ID (com.tencent.xin.pay, etc)
++ (instancetype)mmkvWithID:(NSString *)mmapID {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:nil mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey mode:(MMKVMode)mode {
+ auto rootPath = (mode == MMKVSingleProcess) ? nil : g_groupPath;
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:nil rootPath:relativePath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:rootPath mode:MMKVSingleProcess];
+}
+
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(nullable NSData *)cryptKey relativePath:(nullable NSString *)relativePath {
+ return [MMKV mmkvWithID:mmapID cryptKey:cryptKey rootPath:relativePath mode:MMKVSingleProcess];
+}
+
+// relatePath and MMKVMultiProcess mode can't be set at the same time, so we hide this method from public
++ (instancetype)mmkvWithID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(nullable NSString *)rootPath mode:(MMKVMode)mode {
+ if (!g_hasCalledInitializeMMKV) {
+ MMKVError("MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods");
+ }
+ if (mmapID.length <= 0) {
+ return nil;
+ }
+
+ SCOPED_LOCK(g_lock);
+
+ if (mode == MMKVMultiProcess) {
+ if (!rootPath) {
+ rootPath = g_groupPath;
+ }
+ if (!rootPath) {
+ MMKVError("Getting a multi-process MMKV [%@] without setting groupDir makes no sense", mmapID);
+ MMKV_ASSERT(0);
+ }
+ }
+ NSString *kvKey = [MMKV mmapKeyWithMMapID:mmapID rootPath:rootPath];
+ MMKV *kv = [g_instanceDic objectForKey:kvKey];
+ if (kv == nil) {
+ kv = [[MMKV alloc] initWithMMapID:mmapID cryptKey:cryptKey rootPath:rootPath mode:mode];
+ if (!kv->m_mmkv) {
+ return nil;
+ }
+ kv->m_mmapKey = kvKey;
+ [g_instanceDic setObject:kv forKey:kvKey];
+ }
+ return kv;
+}
+
+- (instancetype)initWithMMapID:(NSString *)mmapID cryptKey:(NSData *)cryptKey rootPath:(NSString *)rootPath mode:(MMKVMode)mode {
+ if (self = [super init]) {
+ string pathTmp;
+ if (rootPath.length > 0) {
+ pathTmp = rootPath.UTF8String;
+ }
+ string cryptKeyTmp;
+ if (cryptKey.length > 0) {
+ cryptKeyTmp = string((char *) cryptKey.bytes, cryptKey.length);
+ }
+ string *rootPathPtr = pathTmp.empty() ? nullptr : &pathTmp;
+ string *cryptKeyPtr = cryptKeyTmp.empty() ? nullptr : &cryptKeyTmp;
+ m_mmkv = mmkv::MMKV::mmkvWithID(mmapID.UTF8String, (mmkv::MMKVMode) mode, cryptKeyPtr, rootPathPtr);
+ if (!m_mmkv) {
+ return self;
+ }
+ m_mmapID = [NSString stringWithUTF8String:m_mmkv->mmapID().c_str()];
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+ if (!g_isRunningInAppExtension) {
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onMemoryWarning)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+ }
+#endif
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self clearMemoryCache];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark - Application state
+
+#if defined(MMKV_IOS) && !defined(MMKV_IOS_EXTENSION)
+- (void)onMemoryWarning {
+ MMKVInfo("cleaning on memory warning %@", m_mmapID);
+
+ [self clearMemoryCache];
+}
+
++ (void)didEnterBackground {
+ mmkv::MMKV::setIsInBackground(true);
+ MMKVInfo("isInBackground:%d", true);
+}
+
++ (void)didBecomeActive {
+ mmkv::MMKV::setIsInBackground(false);
+ MMKVInfo("isInBackground:%d", false);
+}
+#endif
+
+- (void)clearAll {
+ m_mmkv->clearAll();
+}
+
+- (void)clearMemoryCache {
+ if (m_mmkv) {
+ m_mmkv->clearMemoryCache();
+ }
+}
+
+- (void)close {
+ SCOPED_LOCK(g_lock);
+ MMKVInfo("closing %@", m_mmapID);
+
+ m_mmkv->close();
+ m_mmkv = nullptr;
+
+ [g_instanceDic removeObjectForKey:m_mmapKey];
+}
+
+- (void)trim {
+ m_mmkv->trim();
+}
+
+#pragma mark - encryption & decryption
+
+#ifndef MMKV_DISABLE_CRYPT
+
+- (nullable NSData *)cryptKey {
+ auto str = m_mmkv->cryptKey();
+ if (str.length() > 0) {
+ return [NSData dataWithBytes:str.data() length:str.length()];
+ }
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ string key;
+ if (newKey.length > 0) {
+ key = string((char *) newKey.bytes, newKey.length);
+ }
+ return m_mmkv->reKey(key);
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+ if (cryptKey.length > 0) {
+ string key = string((char *) cryptKey.bytes, cryptKey.length);
+ m_mmkv->checkReSetCryptKey(&key);
+ } else {
+ m_mmkv->checkReSetCryptKey(nullptr);
+ }
+}
+
+#else
+
+- (nullable NSData *)cryptKey {
+ return nil;
+}
+
+- (BOOL)reKey:(nullable NSData *)newKey {
+ return NO;
+}
+
+- (void)checkReSetCryptKey:(nullable NSData *)cryptKey {
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+#pragma mark - set & get
+
+- (BOOL)setObject:(nullable NSObject *)object forKey:(NSString *)key {
+ return m_mmkv->set(object, key);
+}
+
+- (BOOL)setBool:(BOOL)value forKey:(NSString *)key {
+ return m_mmkv->set((bool) value, key);
+}
+
+- (BOOL)setInt32:(int32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt32:(uint32_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setInt64:(int64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setUInt64:(uint64_t)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setFloat:(float)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setDouble:(double)value forKey:(NSString *)key {
+ return m_mmkv->set(value, key);
+}
+
+- (BOOL)setString:(NSString *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setDate:(NSDate *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (BOOL)setData:(NSData *)value forKey:(NSString *)key {
+ return [self setObject:value forKey:key];
+}
+
+- (id)getObjectOfClass:(Class)cls forKey:(NSString *)key {
+ return m_mmkv->getObject(key, cls);
+}
+
+- (BOOL)getBoolForKey:(NSString *)key {
+ return [self getBoolForKey:key defaultValue:FALSE];
+}
+- (BOOL)getBoolForKey:(NSString *)key defaultValue:(BOOL)defaultValue {
+ return m_mmkv->getBool(key, defaultValue);
+}
+
+- (int32_t)getInt32ForKey:(NSString *)key {
+ return [self getInt32ForKey:key defaultValue:0];
+}
+- (int32_t)getInt32ForKey:(NSString *)key defaultValue:(int32_t)defaultValue {
+ return m_mmkv->getInt32(key, defaultValue);
+}
+
+- (uint32_t)getUInt32ForKey:(NSString *)key {
+ return [self getUInt32ForKey:key defaultValue:0];
+}
+- (uint32_t)getUInt32ForKey:(NSString *)key defaultValue:(uint32_t)defaultValue {
+ return m_mmkv->getUInt32(key, defaultValue);
+}
+
+- (int64_t)getInt64ForKey:(NSString *)key {
+ return [self getInt64ForKey:key defaultValue:0];
+}
+- (int64_t)getInt64ForKey:(NSString *)key defaultValue:(int64_t)defaultValue {
+ return m_mmkv->getInt64(key, defaultValue);
+}
+
+- (uint64_t)getUInt64ForKey:(NSString *)key {
+ return [self getUInt64ForKey:key defaultValue:0];
+}
+- (uint64_t)getUInt64ForKey:(NSString *)key defaultValue:(uint64_t)defaultValue {
+ return m_mmkv->getUInt64(key, defaultValue);
+}
+
+- (float)getFloatForKey:(NSString *)key {
+ return [self getFloatForKey:key defaultValue:0];
+}
+- (float)getFloatForKey:(NSString *)key defaultValue:(float)defaultValue {
+ return m_mmkv->getFloat(key, defaultValue);
+}
+
+- (double)getDoubleForKey:(NSString *)key {
+ return [self getDoubleForKey:key defaultValue:0];
+}
+- (double)getDoubleForKey:(NSString *)key defaultValue:(double)defaultValue {
+ return m_mmkv->getDouble(key, defaultValue);
+}
+
+- (nullable NSString *)getStringForKey:(NSString *)key {
+ return [self getStringForKey:key defaultValue:nil];
+}
+- (nullable NSString *)getStringForKey:(NSString *)key defaultValue:(nullable NSString *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSString *valueString = [self getObjectOfClass:NSString.class forKey:key];
+ if (!valueString) {
+ valueString = defaultValue;
+ }
+ return valueString;
+}
+
+- (nullable NSDate *)getDateForKey:(NSString *)key {
+ return [self getDateForKey:key defaultValue:nil];
+}
+- (nullable NSDate *)getDateForKey:(NSString *)key defaultValue:(nullable NSDate *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSDate *valueDate = [self getObjectOfClass:NSDate.class forKey:key];
+ if (!valueDate) {
+ valueDate = defaultValue;
+ }
+ return valueDate;
+}
+
+- (nullable NSData *)getDataForKey:(NSString *)key {
+ return [self getDataForKey:key defaultValue:nil];
+}
+- (nullable NSData *)getDataForKey:(NSString *)key defaultValue:(nullable NSData *)defaultValue {
+ if (key.length <= 0) {
+ return defaultValue;
+ }
+ NSData *valueData = [self getObjectOfClass:NSData.class forKey:key];
+ if (!valueData) {
+ valueData = defaultValue;
+ }
+ return valueData;
+}
+
+- (size_t)getValueSizeForKey:(NSString *)key {
+ return m_mmkv->getValueSize(key, false);
+}
+
+- (int32_t)writeValueForKey:(NSString *)key toBuffer:(NSMutableData *)buffer {
+ return m_mmkv->writeValueToBuffer(key, buffer.mutableBytes, static_cast(buffer.length));
+}
+
+#pragma mark - enumerate
+
+- (BOOL)containsKey:(NSString *)key {
+ return m_mmkv->containsKey(key);
+}
+
+- (size_t)count {
+ return m_mmkv->count();
+}
+
+- (size_t)totalSize {
+ return m_mmkv->totalSize();
+}
+
+- (size_t)actualSize {
+ return m_mmkv->actualSize();
+}
+
+- (void)enumerateKeys:(void (^)(NSString *key, BOOL *stop))block {
+ m_mmkv->enumerateKeys(block);
+}
+
+- (NSArray *)allKeys {
+ return m_mmkv->allKeys();
+}
+
+- (void)removeValueForKey:(NSString *)key {
+ m_mmkv->removeValueForKey(key);
+}
+
+- (void)removeValuesForKeys:(NSArray *)arrKeys {
+ m_mmkv->removeValuesForKeys(arrKeys);
+}
+
+#pragma mark - Boring stuff
+
+- (void)sync {
+ m_mmkv->sync(mmkv::MMKV_SYNC);
+}
+
+- (void)async {
+ m_mmkv->sync(mmkv::MMKV_ASYNC);
+}
+
+- (void)checkContentChanged {
+ m_mmkv->checkContentChanged();
+}
+
++ (void)onAppTerminate {
+ SCOPED_LOCK(g_lock);
+
+ [g_instanceDic removeAllObjects];
+
+ mmkv::MMKV::onExit();
+}
+
++ (NSString *)mmkvBasePath {
+ if (g_basePath.length > 0) {
+ return g_basePath;
+ }
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentPath = (NSString *) [paths firstObject];
+ if ([documentPath length] > 0) {
+ g_basePath = [documentPath stringByAppendingPathComponent:@"mmkv"];
+ return g_basePath;
+ } else {
+ return @"";
+ }
+}
+
++ (void)setMMKVBasePath:(NSString *)basePath {
+ if (basePath.length > 0) {
+ g_basePath = basePath;
+ [MMKV initializeMMKV:basePath];
+
+ // still warn about it
+ g_hasCalledInitializeMMKV = NO;
+
+ MMKVInfo("set MMKV base path to: %@", g_basePath);
+ }
+}
+
+static NSString *md5(NSString *value) {
+ uint8_t md[MD5_DIGEST_LENGTH] = {};
+ char tmp[3] = {}, buf[33] = {};
+ auto data = [value dataUsingEncoding:NSUTF8StringEncoding];
+ openssl::MD5((uint8_t *) data.bytes, data.length, md);
+ for (auto ch : md) {
+ snprintf(tmp, sizeof(tmp), "%2.2x", ch);
+ strcat(buf, tmp);
+ }
+ return [NSString stringWithCString:buf encoding:NSASCIIStringEncoding];
+}
+
++ (NSString *)mmapKeyWithMMapID:(NSString *)mmapID rootPath:(nullable NSString *)rootPath {
+ NSString *string = nil;
+ if ([rootPath length] > 0 && [rootPath isEqualToString:[MMKV mmkvBasePath]] == NO) {
+ string = md5([rootPath stringByAppendingPathComponent:mmapID]);
+ } else {
+ string = mmapID;
+ }
+ MMKVDebug("mmapKey: %@", string);
+ return string;
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID {
+ return [self isFileValid:mmapID rootPath:nil];
+}
+
++ (BOOL)isFileValid:(NSString *)mmapID rootPath:(nullable NSString *)path {
+ if (mmapID.length > 0) {
+ if (path.length > 0) {
+ string rootPath(path.UTF8String);
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, &rootPath);
+ } else {
+ return mmkv::MMKV::isFileValid(mmapID.UTF8String, nullptr);
+ }
+ }
+ return NO;
+}
+
++ (void)registerHandler:(id)handler {
+ SCOPED_LOCK(g_lock);
+ g_callbackHandler = handler;
+
+ if ([g_callbackHandler respondsToSelector:@selector(mmkvLogWithLevel:file:line:func:message:)]) {
+ g_isLogRedirecting = true;
+ mmkv::MMKV::registerLogHandler(LogHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)] ||
+ [g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ mmkv::MMKV::registerErrorHandler(ErrorHandler);
+ }
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ mmkv::MMKV::registerContentChangeHandler(ContentChangeHandler);
+ }
+}
+
++ (void)unregiserHandler {
+ SCOPED_LOCK(g_lock);
+
+ g_isLogRedirecting = false;
+ g_callbackHandler = nil;
+
+ mmkv::MMKV::unRegisterLogHandler();
+ mmkv::MMKV::unRegisterErrorHandler();
+ mmkv::MMKV::unRegisterContentChangeHandler();
+}
+
++ (void)setLogLevel:(MMKVLogLevel)logLevel {
+ mmkv::MMKV::setLogLevel((mmkv::MMKVLogLevel) logLevel);
+}
+
+- (uint32_t)migrateFromUserDefaults:(NSUserDefaults *)userDaults {
+ NSDictionary *dic = [userDaults dictionaryRepresentation];
+ if (dic.count <= 0) {
+ MMKVInfo("migrate data fail, userDaults is nil or empty");
+ return 0;
+ }
+ __block uint32_t count = 0;
+ [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
+ if ([key isKindOfClass:[NSString class]]) {
+ NSString *stringKey = key;
+ if ([MMKV tranlateData:obj key:stringKey kv:self]) {
+ count++;
+ }
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ }];
+ return count;
+}
+
++ (BOOL)tranlateData:(id)obj key:(NSString *)key kv:(MMKV *)kv {
+ if ([obj isKindOfClass:[NSString class]]) {
+ return [kv setString:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSData class]]) {
+ return [kv setData:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSDate class]]) {
+ return [kv setDate:obj forKey:key];
+ } else if ([obj isKindOfClass:[NSNumber class]]) {
+ NSNumber *num = obj;
+ CFNumberType numberType = CFNumberGetType((CFNumberRef) obj);
+ switch (numberType) {
+ case kCFNumberCharType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberIntType:
+ case kCFNumberShortType:
+ return [kv setInt32:num.intValue forKey:key];
+ case kCFNumberSInt64Type:
+ case kCFNumberLongType:
+ case kCFNumberNSIntegerType:
+ case kCFNumberLongLongType:
+ return [kv setInt64:num.longLongValue forKey:key];
+ case kCFNumberFloat32Type:
+ return [kv setFloat:num.floatValue forKey:key];
+ case kCFNumberFloat64Type:
+ case kCFNumberDoubleType:
+ return [kv setDouble:num.doubleValue forKey:key];
+ default:
+ MMKVWarning("unknown number type:%ld, key:%@", (long) numberType, key);
+ return NO;
+ }
+ } else if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
+ return [kv setObject:obj forKey:key];
+ } else {
+ MMKVWarning("unknown type of key:%@", key);
+ }
+ return NO;
+}
+
+@end
+
+#pragma makr - callbacks
+
+static void LogHandler(mmkv::MMKVLogLevel level, const char *file, int line, const char *function, NSString *message) {
+ [g_callbackHandler mmkvLogWithLevel:(MMKVLogLevel) level file:file line:line func:function message:message];
+}
+
+static mmkv::MMKVRecoverStrategic ErrorHandler(const string &mmapID, mmkv::MMKVErrorType errorType) {
+ if (errorType == mmkv::MMKVCRCCheckFail) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVCRCCheckFail:)]) {
+ auto ret = [g_callbackHandler onMMKVCRCCheckFail:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ } else {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVFileLengthError:)]) {
+ auto ret = [g_callbackHandler onMMKVFileLengthError:[NSString stringWithUTF8String:mmapID.c_str()]];
+ return (mmkv::MMKVRecoverStrategic) ret;
+ }
+ }
+ return mmkv::OnErrorDiscard;
+}
+
+static void ContentChangeHandler(const string &mmapID) {
+ if ([g_callbackHandler respondsToSelector:@selector(onMMKVContentChange:)]) {
+ [g_callbackHandler onMMKVContentChange:[NSString stringWithUTF8String:mmapID.c_str()]];
+ }
+}
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData.cpp b/ios/Pods/MMKVCore/Core/CodedInputData.cpp
new file mode 100644
index 000000000..9f8c161e5
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData.cpp
@@ -0,0 +1,228 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "CodedInputData.h"
+#include "PBUtility.h"
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+CodedInputData::CodedInputData(const void *oData, size_t length)
+ : m_ptr((uint8_t *) oData), m_size(length), m_position(0) {
+ MMKV_ASSERT(m_ptr);
+}
+
+void CodedInputData::seek(size_t addedSize) {
+ if (m_position + addedSize > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+ m_position += addedSize;
+}
+
+double CodedInputData::readDouble() {
+ return Int64ToFloat64(this->readRawLittleEndian64());
+}
+
+float CodedInputData::readFloat() {
+ return Int32ToFloat32(this->readRawLittleEndian32());
+}
+
+int64_t CodedInputData::readInt64() {
+ int32_t shift = 0;
+ int64_t result = 0;
+ while (shift < 64) {
+ int8_t b = this->readRawByte();
+ result |= (int64_t)(b & 0x7f) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ shift += 7;
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformedInt64");
+}
+
+uint64_t CodedInputData::readUInt64() {
+ return static_cast(readInt64());
+}
+
+int32_t CodedInputData::readInt32() {
+ return this->readRawVarint32();
+}
+
+uint32_t CodedInputData::readUInt32() {
+ return static_cast(readRawVarint32());
+}
+
+bool CodedInputData::readBool() {
+ return this->readRawVarint32() != 0;
+}
+
+#ifndef MMKV_APPLE
+
+string CodedInputData::readString() {
+ int32_t size = readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ string result((char *) (m_ptr + m_position), s_size);
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+string CodedInputData::readString(KeyValueHolder &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_ptr + m_position;
+ string result((char *) (m_ptr + m_position), s_size);
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+#endif
+
+MMBuffer CodedInputData::readData() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ MMBuffer data(((int8_t *) m_ptr) + m_position, s_size);
+ m_position += s_size;
+ return data;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+void CodedInputData::readData(KeyValueHolder &kvHolder) {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.computedKVSize = static_cast(m_position - kvHolder.offset);
+ kvHolder.valueSize = static_cast(s_size);
+
+ m_position += s_size;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+int32_t CodedInputData::readRawVarint32() {
+ int8_t tmp = this->readRawByte();
+ if (tmp >= 0) {
+ return tmp;
+ }
+ int32_t result = tmp & 0x7f;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 7;
+ } else {
+ result |= (tmp & 0x7f) << 7;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 14;
+ } else {
+ result |= (tmp & 0x7f) << 14;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 21;
+ } else {
+ result |= (tmp & 0x7f) << 21;
+ result |= (tmp = this->readRawByte()) << 28;
+ if (tmp < 0) {
+ // discard upper 32 bits
+ for (int i = 0; i < 5; i++) {
+ if (this->readRawByte() >= 0) {
+ return result;
+ }
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformed varint32");
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int32_t CodedInputData::readRawLittleEndian32() {
+ int8_t b1 = this->readRawByte();
+ int8_t b2 = this->readRawByte();
+ int8_t b3 = this->readRawByte();
+ int8_t b4 = this->readRawByte();
+ return (((int32_t) b1 & 0xff)) | (((int32_t) b2 & 0xff) << 8) | (((int32_t) b3 & 0xff) << 16) |
+ (((int32_t) b4 & 0xff) << 24);
+}
+
+int64_t CodedInputData::readRawLittleEndian64() {
+ int8_t b1 = this->readRawByte();
+ int8_t b2 = this->readRawByte();
+ int8_t b3 = this->readRawByte();
+ int8_t b4 = this->readRawByte();
+ int8_t b5 = this->readRawByte();
+ int8_t b6 = this->readRawByte();
+ int8_t b7 = this->readRawByte();
+ int8_t b8 = this->readRawByte();
+ return (((int64_t) b1 & 0xff)) | (((int64_t) b2 & 0xff) << 8) | (((int64_t) b3 & 0xff) << 16) |
+ (((int64_t) b4 & 0xff) << 24) | (((int64_t) b5 & 0xff) << 32) | (((int64_t) b6 & 0xff) << 40) |
+ (((int64_t) b7 & 0xff) << 48) | (((int64_t) b8 & 0xff) << 56);
+}
+
+int8_t CodedInputData::readRawByte() {
+ if (m_position == m_size) {
+ auto msg = "reach end, m_position: " + to_string(m_position) + ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ auto *bytes = (int8_t *) m_ptr;
+ return bytes[m_position++];
+}
+
+#ifdef MMKV_APPLE
+#endif // MMKV_APPLE
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData.h b/ios/Pods/MMKVCore/Core/CodedInputData.h
new file mode 100644
index 000000000..b0987567e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData.h
@@ -0,0 +1,83 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_CODEDINPUTDATA_H
+#define MMKV_CODEDINPUTDATA_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+
+class CodedInputData {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+
+ int8_t readRawByte();
+
+ int32_t readRawVarint32();
+
+ int32_t readRawLittleEndian32();
+
+ int64_t readRawLittleEndian64();
+
+public:
+ CodedInputData(const void *oData, size_t length);
+
+ bool isAtEnd() const { return m_position == m_size; };
+
+ void seek(size_t addedSize);
+
+ bool readBool();
+
+ double readDouble();
+
+ float readFloat();
+
+ int64_t readInt64();
+
+ uint64_t readUInt64();
+
+ int32_t readInt32();
+
+ uint32_t readUInt32();
+
+ MMBuffer readData();
+ void readData(KeyValueHolder &kvHolder);
+
+#ifndef MMKV_APPLE
+ std::string readString();
+ std::string readString(KeyValueHolder &kvHolder);
+#else
+ NSString *readString();
+ NSString *readString(KeyValueHolder &kvHolder);
+ NSData *readNSData();
+#endif
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_CODEDINPUTDATA_H
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp
new file mode 100644
index 000000000..71a83e074
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.cpp
@@ -0,0 +1,278 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* 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.
+*/
+
+#include "CodedInputDataCrypt.h"
+#include "PBUtility.h"
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+#ifndef MMKV_DISABLE_CRYPT
+
+using namespace std;
+
+namespace mmkv {
+
+CodedInputDataCrypt::CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt)
+ : m_ptr((uint8_t *) oData), m_size(length), m_position(0), m_decryptPosition(0), m_decrypter(crypt) {
+ m_decryptBufferSize = AES_KEY_LEN * 2;
+ m_decryptBufferPosition = static_cast(crypt.m_number);
+ m_decryptBufferDiscardPosition = m_decryptBufferPosition;
+ m_decryptBufferDecryptLength = m_decryptBufferPosition;
+
+ m_decryptBuffer = (uint8_t *) malloc(m_decryptBufferSize);
+ if (!m_decryptBuffer) {
+ throw runtime_error(strerror(errno));
+ }
+}
+
+CodedInputDataCrypt::~CodedInputDataCrypt() {
+ if (m_decryptBuffer) {
+ free(m_decryptBuffer);
+ }
+}
+
+void CodedInputDataCrypt::seek(size_t addedSize) {
+ m_position += addedSize;
+ m_decryptPosition += addedSize;
+
+ if (m_position > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+ assert(m_position % AES_KEY_LEN == m_decrypter.m_number);
+}
+
+void CodedInputDataCrypt::consumeBytes(size_t length, bool discardPreData) {
+ if (discardPreData) {
+ m_decryptBufferDiscardPosition = m_decryptBufferPosition;
+ }
+ auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ if (decryptedBytesLeft >= length) {
+ return;
+ }
+ length -= decryptedBytesLeft;
+
+ // if there's some data left inside m_decrypter.m_vector, use them first
+ // it will be faster when always decrypt with (n * AES_KEY_LEN) bytes
+ if (m_decrypter.m_number != 0) {
+ auto alignDecrypter = AES_KEY_LEN - m_decrypter.m_number;
+ // make sure no data left inside m_decrypter.m_vector after decrypt
+ if (length < alignDecrypter) {
+ length = alignDecrypter;
+ } else {
+ length -= alignDecrypter;
+ length = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ length += alignDecrypter;
+ }
+ } else {
+ length = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ }
+ auto bytesLeftInSrc = m_size - m_decryptPosition;
+ length = min(bytesLeftInSrc, length);
+
+ auto bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;
+ // try move some space
+ if (bytesLeftInBuffer < length && m_decryptBufferDiscardPosition > 0) {
+ auto posToMove = (m_decryptBufferDiscardPosition / AES_KEY_LEN) * AES_KEY_LEN;
+ if (posToMove) {
+ auto sizeToMove = m_decryptBufferDecryptLength - posToMove;
+ memmove(m_decryptBuffer, m_decryptBuffer + posToMove, sizeToMove);
+ m_decryptBufferPosition -= posToMove;
+ m_decryptBufferDecryptLength -= posToMove;
+ m_decryptBufferDiscardPosition = 0;
+ bytesLeftInBuffer = m_decryptBufferSize - m_decryptBufferDecryptLength;
+ }
+ }
+ // still no enough sapce, try realloc()
+ if (bytesLeftInBuffer < length) {
+ auto newSize = m_decryptBufferSize + length;
+ auto newBuffer = realloc(m_decryptBuffer, newSize);
+ if (!newBuffer) {
+ throw runtime_error(strerror(errno));
+ }
+ m_decryptBuffer = (uint8_t *) newBuffer;
+ m_decryptBufferSize = newSize;
+ }
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength, length);
+ m_decryptPosition += length;
+ m_decryptBufferDecryptLength += length;
+ assert(m_decryptPosition == m_size || m_decrypter.m_number == 0);
+}
+
+void CodedInputDataCrypt::skipBytes(size_t length) {
+ m_position += length;
+
+ auto decryptedBytesLeft = m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ if (decryptedBytesLeft >= length) {
+ m_decryptBufferPosition += length;
+ return;
+ }
+ length -= decryptedBytesLeft;
+ // if this happens, we need optimization like the alignDecrypter above
+ assert(m_decrypter.m_number == 0);
+
+ size_t alignSize = ((length + AES_KEY_LEN - 1) / AES_KEY_LEN) * AES_KEY_LEN;
+ auto bytesLeftInSrc = m_size - m_decryptPosition;
+ auto size = min(alignSize, bytesLeftInSrc);
+ decryptedBytesLeft = size - length;
+ for (size_t index = 0, round = size / AES_KEY_LEN; index < round; index++) {
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, AES_KEY_LEN);
+ m_decryptPosition += AES_KEY_LEN;
+ size -= AES_KEY_LEN;
+ }
+ if (size) {
+ m_decrypter.decrypt(m_ptr + m_decryptPosition, m_decryptBuffer, size);
+ m_decryptPosition += size;
+ m_decryptBufferPosition = size - decryptedBytesLeft;
+ m_decryptBufferDecryptLength = size;
+ } else {
+ m_decryptBufferPosition = AES_KEY_LEN - decryptedBytesLeft;
+ m_decryptBufferDecryptLength = AES_KEY_LEN;
+ }
+ assert(m_decryptBufferPosition <= m_decryptBufferDecryptLength);
+ assert(m_decryptPosition - m_decryptBufferDecryptLength + m_decryptBufferPosition == m_position);
+}
+
+inline void CodedInputDataCrypt::statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status) {
+ rollbackSize += m_decryptBufferDecryptLength - m_decryptBufferPosition;
+ m_decrypter.statusBeforeDecrypt(m_ptr + m_decryptPosition, m_decryptBuffer + m_decryptBufferDecryptLength,
+ rollbackSize, status);
+}
+
+int8_t CodedInputDataCrypt::readRawByte() {
+ assert(m_position <= m_decryptPosition);
+ if (m_position == m_size) {
+ auto msg = "reach end, m_position: " + to_string(m_position) + ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ m_position++;
+
+ assert(m_decryptBufferPosition < m_decryptBufferSize);
+ auto *bytes = (int8_t *) m_decryptBuffer;
+ return bytes[m_decryptBufferPosition++];
+}
+
+int32_t CodedInputDataCrypt::readRawVarint32(bool discardPreData) {
+ consumeBytes(10, discardPreData);
+
+ int8_t tmp = this->readRawByte();
+ if (tmp >= 0) {
+ return tmp;
+ }
+ int32_t result = tmp & 0x7f;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 7;
+ } else {
+ result |= (tmp & 0x7f) << 7;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 14;
+ } else {
+ result |= (tmp & 0x7f) << 14;
+ if ((tmp = this->readRawByte()) >= 0) {
+ result |= tmp << 21;
+ } else {
+ result |= (tmp & 0x7f) << 21;
+ result |= (tmp = this->readRawByte()) << 28;
+ if (tmp < 0) {
+ // discard upper 32 bits
+ for (int i = 0; i < 5; i++) {
+ if (this->readRawByte() >= 0) {
+ return result;
+ }
+ }
+ throw invalid_argument("InvalidProtocolBuffer malformed varint32");
+ }
+ }
+ }
+ }
+ return result;
+}
+
+int32_t CodedInputDataCrypt::readInt32() {
+ return this->readRawVarint32();
+}
+
+# ifndef MMKV_APPLE
+
+string CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32(true);
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ consumeBytes(s_size);
+
+ kvHolder.keySize = static_cast(s_size);
+
+ string result((char *) (m_decryptBuffer + m_decryptBufferPosition), s_size);
+ m_position += s_size;
+ m_decryptBufferPosition += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+# endif
+
+void CodedInputDataCrypt::readData(KeyValueHolderCrypt &kvHolder) {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(s_size)) {
+ kvHolder.type = KeyValueHolderType_Offset;
+ kvHolder.valueSize = static_cast(s_size);
+ kvHolder.pbKeyValueSize =
+ static_cast(pbRawVarint32Size(kvHolder.valueSize) + pbRawVarint32Size(kvHolder.keySize));
+
+ size_t rollbackSize = kvHolder.pbKeyValueSize + kvHolder.keySize;
+ statusBeforeDecrypt(rollbackSize, kvHolder.cryptStatus);
+
+ skipBytes(s_size);
+ } else {
+ consumeBytes(s_size);
+
+ kvHolder.type = KeyValueHolderType_Direct;
+ kvHolder = KeyValueHolderCrypt(m_decryptBuffer + m_decryptBufferPosition, s_size);
+ m_decryptBufferPosition += s_size;
+ m_position += s_size;
+ }
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_DISABLE_CRYPT
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h
new file mode 100644
index 000000000..fac32e62c
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt.h
@@ -0,0 +1,87 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* 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.
+*/
+
+#ifndef CodedInputDataCrypt_h
+#define CodedInputDataCrypt_h
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "aes/AESCrypt.h"
+#include
+
+#ifdef MMKV_DISABLE_CRYPT
+
+namespace mmkv {
+class CodedInputDataCrypt;
+}
+
+#else
+
+namespace mmkv {
+
+class CodedInputDataCrypt {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+ size_t m_decryptPosition; // position of text that has beed decrypted
+
+ AESCrypt &m_decrypter;
+ uint8_t *m_decryptBuffer; // internal decrypt buffer, grows by (n * AES_KEY_LEN) bytes
+ size_t m_decryptBufferSize;
+ size_t m_decryptBufferPosition; // reader position in the buffer, synced with m_position
+ size_t m_decryptBufferDecryptLength; // length of the buffer that has been used
+ size_t m_decryptBufferDiscardPosition; // recycle position, any data before that can be discarded
+
+ void consumeBytes(size_t length, bool discardPreData = false);
+ void skipBytes(size_t length);
+ void statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status);
+
+ int8_t readRawByte();
+
+ int32_t readRawVarint32(bool discardPreData = false);
+
+public:
+ CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt);
+
+ ~CodedInputDataCrypt();
+
+ bool isAtEnd() { return m_position == m_size; };
+
+ void seek(size_t addedSize);
+
+ int32_t readInt32();
+
+ void readData(KeyValueHolderCrypt &kvHolder);
+
+#ifndef MMKV_APPLE
+ std::string readString(KeyValueHolderCrypt &kvHolder);
+#else
+ NSString *readString(KeyValueHolderCrypt &kvHolder);
+#endif
+};
+
+} // namespace mmkv
+
+#endif // MMKV_DISABLE_CRYPT
+#endif // __cplusplus
+#endif /* CodedInputDataCrypt_h */
diff --git a/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp
new file mode 100644
index 000000000..5d19203a8
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputDataCrypt_OSX.cpp
@@ -0,0 +1,62 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* 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.
+*/
+
+#include "CodedInputDataCrypt.h"
+
+#if defined(MMKV_APPLE) && !defined(MMKV_DISABLE_CRYPT)
+
+# include "PBUtility.h"
+# include
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+
+namespace mmkv {
+
+NSString *CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32(true);
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ consumeBytes(s_size);
+
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_decryptBuffer + m_decryptBufferPosition;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ m_decryptBufferPosition += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_APPLE && !MMKV_DISABLE_CRYPT
diff --git a/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp b/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp
new file mode 100644
index 000000000..41bd58aed
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedInputData_OSX.cpp
@@ -0,0 +1,92 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "CodedInputData.h"
+
+#ifdef MMKV_APPLE
+
+# include "PBUtility.h"
+# include
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+
+namespace mmkv {
+
+NSString *CodedInputData::readString() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ auto ptr = m_ptr + m_position;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+NSString *CodedInputData::readString(KeyValueHolder &kvHolder) {
+ kvHolder.offset = static_cast(m_position);
+
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ kvHolder.keySize = static_cast(s_size);
+
+ auto ptr = m_ptr + m_position;
+ NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding];
+ m_position += s_size;
+ return [result autorelease];
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+NSData *CodedInputData::readNSData() {
+ int32_t size = this->readRawVarint32();
+ if (size < 0) {
+ throw length_error("InvalidProtocolBuffer negativeSize");
+ }
+
+ auto s_size = static_cast(size);
+ if (s_size <= m_size - m_position) {
+ NSData *result = [NSData dataWithBytes:(m_ptr + m_position) length:s_size];
+ m_position += s_size;
+ return result;
+ } else {
+ throw out_of_range("InvalidProtocolBuffer truncatedMessage");
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_APPLE
diff --git a/ios/Pods/MMKVCore/Core/CodedOutputData.cpp b/ios/Pods/MMKVCore/Core/CodedOutputData.cpp
new file mode 100644
index 000000000..96d37dee5
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedOutputData.cpp
@@ -0,0 +1,174 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "CodedOutputData.h"
+#include "PBUtility.h"
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+CodedOutputData::CodedOutputData(void *ptr, size_t len) : m_ptr((uint8_t *) ptr), m_size(len), m_position(0) {
+ MMKV_ASSERT(m_ptr);
+}
+
+uint8_t *CodedOutputData::curWritePointer() {
+ return m_ptr + m_position;
+}
+
+void CodedOutputData::writeDouble(double value) {
+ this->writeRawLittleEndian64(Float64ToInt64(value));
+}
+
+void CodedOutputData::writeFloat(float value) {
+ this->writeRawLittleEndian32(Float32ToInt32(value));
+}
+
+void CodedOutputData::writeInt64(int64_t value) {
+ this->writeRawVarint64(value);
+}
+
+void CodedOutputData::writeUInt64(uint64_t value) {
+ writeRawVarint64(static_cast(value));
+}
+
+void CodedOutputData::writeInt32(int32_t value) {
+ if (value >= 0) {
+ this->writeRawVarint32(value);
+ } else {
+ this->writeRawVarint64(value);
+ }
+}
+
+void CodedOutputData::writeUInt32(uint32_t value) {
+ writeRawVarint32(static_cast(value));
+}
+
+void CodedOutputData::writeBool(bool value) {
+ this->writeRawByte(static_cast(value ? 1 : 0));
+}
+
+void CodedOutputData::writeData(const MMBuffer &value) {
+ this->writeRawVarint32((int32_t) value.length());
+ this->writeRawData(value);
+}
+
+#ifndef MMKV_APPLE
+
+void CodedOutputData::writeString(const string &value) {
+ size_t numberOfBytes = value.size();
+ this->writeRawVarint32((int32_t) numberOfBytes);
+ if (m_position + numberOfBytes > m_size) {
+ auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) +
+ ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ memcpy(m_ptr + m_position, ((uint8_t *) value.data()), numberOfBytes);
+ m_position += numberOfBytes;
+}
+
+#endif // MMKV_APPLE
+
+size_t CodedOutputData::spaceLeft() {
+ if (m_size <= m_position) {
+ return 0;
+ }
+ return m_size - m_position;
+}
+
+void CodedOutputData::seek(size_t addedSize) {
+ m_position += addedSize;
+
+ if (m_position > m_size) {
+ throw out_of_range("OutOfSpace");
+ }
+}
+
+void CodedOutputData::writeRawByte(uint8_t value) {
+ if (m_position == m_size) {
+ throw out_of_range("m_position: " + to_string(m_position) + " m_size: " + to_string(m_size));
+ return;
+ }
+
+ m_ptr[m_position++] = value;
+}
+
+void CodedOutputData::writeRawData(const MMBuffer &data) {
+ size_t numberOfBytes = data.length();
+ if (m_position + numberOfBytes > m_size) {
+ auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) +
+ ", m_size: " + to_string(m_size);
+ throw out_of_range(msg);
+ }
+ memcpy(m_ptr + m_position, data.getPtr(), numberOfBytes);
+ m_position += numberOfBytes;
+}
+
+void CodedOutputData::writeRawVarint32(int32_t value) {
+ while (true) {
+ if ((value & ~0x7f) == 0) {
+ this->writeRawByte(static_cast(value));
+ return;
+ } else {
+ this->writeRawByte(static_cast((value & 0x7F) | 0x80));
+ value = logicalRightShift32(value, 7);
+ }
+ }
+}
+
+void CodedOutputData::writeRawVarint64(int64_t value) {
+ while (true) {
+ if ((value & ~0x7f) == 0) {
+ this->writeRawByte(static_cast(value));
+ return;
+ } else {
+ this->writeRawByte(static_cast((value & 0x7f) | 0x80));
+ value = logicalRightShift64(value, 7);
+ }
+ }
+}
+
+void CodedOutputData::writeRawLittleEndian32(int32_t value) {
+ this->writeRawByte(static_cast((value) &0xff));
+ this->writeRawByte(static_cast((value >> 8) & 0xff));
+ this->writeRawByte(static_cast((value >> 16) & 0xff));
+ this->writeRawByte(static_cast((value >> 24) & 0xff));
+}
+
+void CodedOutputData::writeRawLittleEndian64(int64_t value) {
+ this->writeRawByte(static_cast((value) &0xff));
+ this->writeRawByte(static_cast((value >> 8) & 0xff));
+ this->writeRawByte(static_cast((value >> 16) & 0xff));
+ this->writeRawByte(static_cast((value >> 24) & 0xff));
+ this->writeRawByte(static_cast((value >> 32) & 0xff));
+ this->writeRawByte(static_cast((value >> 40) & 0xff));
+ this->writeRawByte(static_cast((value >> 48) & 0xff));
+ this->writeRawByte(static_cast((value >> 56) & 0xff));
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/CodedOutputData.h b/ios/Pods/MMKVCore/Core/CodedOutputData.h
new file mode 100644
index 000000000..be452bfe3
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/CodedOutputData.h
@@ -0,0 +1,82 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_CODEDOUTPUTDATA_H
+#define MMKV_CODEDOUTPUTDATA_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+
+class CodedOutputData {
+ uint8_t *const m_ptr;
+ size_t m_size;
+ size_t m_position;
+
+public:
+ CodedOutputData(void *ptr, size_t len);
+
+ size_t spaceLeft();
+
+ uint8_t *curWritePointer();
+
+ void seek(size_t addedSize);
+
+ void writeRawByte(uint8_t value);
+
+ void writeRawLittleEndian32(int32_t value);
+
+ void writeRawLittleEndian64(int64_t value);
+
+ void writeRawVarint32(int32_t value);
+
+ void writeRawVarint64(int64_t value);
+
+ void writeRawData(const MMBuffer &data);
+
+ void writeDouble(double value);
+
+ void writeFloat(float value);
+
+ void writeInt64(int64_t value);
+
+ void writeUInt64(uint64_t value);
+
+ void writeInt32(int32_t value);
+
+ void writeUInt32(uint32_t value);
+
+ void writeBool(bool value);
+
+ void writeData(const MMBuffer &value);
+
+#ifndef MMKV_APPLE
+ void writeString(const std::string &value);
+#endif
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_CODEDOUTPUTDATA_H
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock.cpp
new file mode 100644
index 000000000..7db7228f8
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock.cpp
@@ -0,0 +1,181 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "InterProcessLock.h"
+#include "MMKVLog.h"
+
+#ifndef MMKV_WIN32
+# include
+#endif
+
+namespace mmkv {
+
+bool FileLock::lock(LockType lockType) {
+ return doLock(lockType, true);
+}
+
+bool FileLock::try_lock(LockType lockType) {
+ return doLock(lockType, false);
+}
+
+bool FileLock::doLock(LockType lockType, bool wait) {
+ if (!isFileLockValid()) {
+ return false;
+ }
+ bool unLockFirstIfNeeded = false;
+
+ if (lockType == SharedLockType) {
+ // don't want shared-lock to break any existing locks
+ if (m_sharedLockCount > 0 || m_exclusiveLockCount > 0) {
+ m_sharedLockCount++;
+ return true;
+ }
+ } else {
+ // don't want exclusive-lock to break existing exclusive-locks
+ if (m_exclusiveLockCount > 0) {
+ m_exclusiveLockCount++;
+ return true;
+ }
+ // prevent deadlock
+ if (m_sharedLockCount > 0) {
+ unLockFirstIfNeeded = true;
+ }
+ }
+
+ auto ret = platformLock(lockType, wait, unLockFirstIfNeeded);
+ if (ret) {
+ if (lockType == SharedLockType) {
+ m_sharedLockCount++;
+ } else {
+ m_exclusiveLockCount++;
+ }
+ }
+ return ret;
+}
+
+#ifndef MMKV_WIN32
+
+static int32_t LockType2FlockType(LockType lockType) {
+ switch (lockType) {
+ case SharedLockType:
+ return LOCK_SH;
+ case ExclusiveLockType:
+ return LOCK_EX;
+ }
+ return LOCK_EX;
+}
+
+bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+# ifdef MMKV_ANDROID
+ if (m_isAshmem) {
+ return ashmemLock(lockType, wait, unLockFirstIfNeeded);
+ }
+# endif
+ auto realLockType = LockType2FlockType(lockType);
+ auto cmd = wait ? realLockType : (realLockType | LOCK_NB);
+ if (unLockFirstIfNeeded) {
+ // try lock
+ auto ret = flock(m_fd, realLockType | LOCK_NB);
+ if (ret == 0) {
+ return true;
+ }
+ // let's be gentleman: unlock my shared-lock to prevent deadlock
+ ret = flock(m_fd, LOCK_UN);
+ if (ret != 0) {
+ MMKVError("fail to try unlock first fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+
+ auto ret = flock(m_fd, cmd);
+ if (ret != 0) {
+ MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ ret = flock(m_fd, LockType2FlockType(SharedLockType));
+ if (ret != 0) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::platformUnLock(bool unlockToSharedLock) {
+# ifdef MMKV_ANDROID
+ if (m_isAshmem) {
+ return ashmemUnLock(unlockToSharedLock);
+ }
+# endif
+ int cmd = unlockToSharedLock ? LOCK_SH : LOCK_UN;
+ auto ret = flock(m_fd, cmd);
+ if (ret != 0) {
+ MMKVError("fail to unlock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ return false;
+ } else {
+ return true;
+ }
+}
+
+#endif // MMKV_WIN32
+
+bool FileLock::unlock(LockType lockType) {
+ if (!isFileLockValid()) {
+ return false;
+ }
+ bool unlockToSharedLock = false;
+
+ if (lockType == SharedLockType) {
+ if (m_sharedLockCount == 0) {
+ return false;
+ }
+ // don't want shared-lock to break any existing locks
+ if (m_sharedLockCount > 1 || m_exclusiveLockCount > 0) {
+ m_sharedLockCount--;
+ return true;
+ }
+ } else {
+ if (m_exclusiveLockCount == 0) {
+ return false;
+ }
+ if (m_exclusiveLockCount > 1) {
+ m_exclusiveLockCount--;
+ return true;
+ }
+ // restore shared-lock when all exclusive-locks are done
+ if (m_sharedLockCount > 0) {
+ unlockToSharedLock = true;
+ }
+ }
+
+ auto ret = platformUnLock(unlockToSharedLock);
+ if (ret) {
+ if (lockType == SharedLockType) {
+ m_sharedLockCount--;
+ } else {
+ m_exclusiveLockCount--;
+ }
+ }
+ return ret;
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock.h b/ios/Pods/MMKVCore/Core/InterProcessLock.h
new file mode 100644
index 000000000..2da539b08
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock.h
@@ -0,0 +1,119 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_INTERPROCESSLOCK_H
+#define MMKV_INTERPROCESSLOCK_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+
+namespace mmkv {
+
+enum LockType {
+ SharedLockType,
+ ExclusiveLockType,
+};
+
+// a recursive POSIX file-lock wrapper
+// handles lock upgrade & downgrade correctly
+class FileLock {
+ MMKVFileHandle_t m_fd;
+ size_t m_sharedLockCount;
+ size_t m_exclusiveLockCount;
+
+ bool doLock(LockType lockType, bool wait);
+ bool platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded);
+ bool platformUnLock(bool unLockFirstIfNeeded);
+
+#ifndef MMKV_WIN32
+ bool isFileLockValid() { return m_fd >= 0; }
+# ifdef MMKV_ANDROID
+ const bool m_isAshmem;
+ struct flock m_lockInfo;
+ bool ashmemLock(LockType lockType, bool wait, bool unLockFirstIfNeeded);
+ bool ashmemUnLock(bool unLockFirstIfNeeded);
+# endif
+
+#else // defined(MMKV_WIN32)
+ OVERLAPPED m_overLapped;
+
+ bool isFileLockValid() { return m_fd != INVALID_HANDLE_VALUE; }
+#endif // MMKV_WIN32
+
+public:
+#ifndef MMKV_WIN32
+# ifndef MMKV_ANDROID
+ explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0) {}
+# else
+ explicit FileLock(MMKVFileHandle_t fd, bool isAshmem = false);
+# endif // MMKV_ANDROID
+#else // defined(MMKV_WIN32)
+ explicit FileLock(MMKVFileHandle_t fd) : m_fd(fd), m_overLapped{}, m_sharedLockCount(0), m_exclusiveLockCount(0) {}
+#endif // MMKV_WIN32
+
+ bool lock(LockType lockType);
+
+ bool try_lock(LockType lockType);
+
+ bool unlock(LockType lockType);
+
+ // just forbid it for possibly misuse
+ explicit FileLock(const FileLock &other) = delete;
+ FileLock &operator=(const FileLock &other) = delete;
+};
+
+class InterProcessLock {
+ FileLock *m_fileLock;
+ LockType m_lockType;
+
+public:
+ InterProcessLock(FileLock *fileLock, LockType lockType)
+ : m_fileLock(fileLock), m_lockType(lockType), m_enable(true) {
+ MMKV_ASSERT(m_fileLock);
+ }
+
+ bool m_enable;
+
+ void lock() {
+ if (m_enable) {
+ m_fileLock->lock(m_lockType);
+ }
+ }
+
+ bool try_lock() {
+ if (m_enable) {
+ return m_fileLock->try_lock(m_lockType);
+ }
+ return false;
+ }
+
+ void unlock() {
+ if (m_enable) {
+ m_fileLock->unlock(m_lockType);
+ }
+ }
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_INTERPROCESSLOCK_H
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp
new file mode 100644
index 000000000..5a06103f5
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock_Android.cpp
@@ -0,0 +1,98 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "InterProcessLock.h"
+
+#ifdef MMKV_ANDROID
+# include "MMKVLog.h"
+# include
+# include
+
+namespace mmkv {
+
+FileLock::FileLock(MMKVFileHandle_t fd, bool isAshmem)
+ : m_fd(fd), m_sharedLockCount(0), m_exclusiveLockCount(0), m_isAshmem(isAshmem) {
+ m_lockInfo.l_type = F_WRLCK;
+ m_lockInfo.l_start = 0;
+ m_lockInfo.l_whence = SEEK_SET;
+ m_lockInfo.l_len = 0;
+ m_lockInfo.l_pid = 0;
+}
+
+static short LockType2FlockType(LockType lockType) {
+ switch (lockType) {
+ case SharedLockType:
+ return F_RDLCK;
+ case ExclusiveLockType:
+ return F_WRLCK;
+ }
+}
+
+bool FileLock::ashmemLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+ m_lockInfo.l_type = LockType2FlockType(lockType);
+ if (unLockFirstIfNeeded) {
+ // try lock
+ auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret == 0) {
+ return true;
+ }
+ // lets be gentleman: unlock my shared-lock to prevent deadlock
+ auto type = m_lockInfo.l_type;
+ m_lockInfo.l_type = F_UNLCK;
+ ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to try unlock first fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ m_lockInfo.l_type = type;
+ }
+
+ int cmd = wait ? F_SETLKW : F_SETLK;
+ auto ret = fcntl(m_fd, cmd, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ m_lockInfo.l_type = LockType2FlockType(SharedLockType);
+ ret = fcntl(m_fd, cmd, &m_lockInfo);
+ if (ret != 0) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::ashmemUnLock(bool unlockToSharedLock) {
+ m_lockInfo.l_type = static_cast(unlockToSharedLock ? F_RDLCK : F_UNLCK);
+ auto ret = fcntl(m_fd, F_SETLK, &m_lockInfo);
+ if (ret != 0) {
+ MMKVError("fail to unlock fd=%d, ret=%d, error:%s", m_fd, ret, strerror(errno));
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp b/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp
new file mode 100644
index 000000000..fe2c619ba
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/InterProcessLock_Win32.cpp
@@ -0,0 +1,98 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "InterProcessLock.h"
+
+#ifdef MMKV_WIN32
+# include "MMKVLog.h"
+
+namespace mmkv {
+
+static DWORD LockType2Flag(LockType lockType) {
+ DWORD flag = 0;
+ switch (lockType) {
+ case SharedLockType:
+ flag = 0;
+ break;
+ case ExclusiveLockType:
+ flag = LOCKFILE_EXCLUSIVE_LOCK;
+ break;
+ }
+ return flag;
+}
+
+bool FileLock::platformLock(LockType lockType, bool wait, bool unLockFirstIfNeeded) {
+ auto realLockType = LockType2Flag(lockType);
+ auto flag = wait ? realLockType : (realLockType | LOCKFILE_FAIL_IMMEDIATELY);
+ if (unLockFirstIfNeeded) {
+ /* try exclusive-lock above shared-lock will always fail in Win32
+ auto ret = LockFileEx(m_fd, realLockType | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &m_overLapped);
+ if (ret) {
+ return true;
+ }*/
+ // let's be gentleman: unlock my shared-lock to prevent deadlock
+ auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to try unlock first fd=%p, error:%d", m_fd, GetLastError());
+ }
+ }
+
+ auto ret = LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to lock fd=%p, error:%d", m_fd, GetLastError());
+ // try recover my shared-lock
+ if (unLockFirstIfNeeded) {
+ ret = LockFileEx(m_fd, LockType2Flag(SharedLockType), 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ // let's hope this never happen
+ MMKVError("fail to recover shared-lock fd=%p, error:%d", m_fd, GetLastError());
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool FileLock::platformUnLock(bool unlockToSharedLock) {
+ /* quote from MSDN:
+ * If the same range is locked with an exclusive and a shared lock,
+ * two unlock operations are necessary to unlock the region;
+ * the first unlock operation unlocks the exclusive lock,
+ * the second unlock operation unlocks the shared lock.
+ */
+ if (unlockToSharedLock) {
+ auto flag = LockType2Flag(SharedLockType);
+ if (!LockFileEx(m_fd, flag, 0, 1, 0, &m_overLapped)) {
+ MMKVError("fail to roll back to shared-lock, error:%d", GetLastError());
+ }
+ }
+ auto ret = UnlockFileEx(m_fd, 0, 1, 0, &m_overLapped);
+ if (!ret) {
+ MMKVError("fail to unlock fd=%p, error:%d", m_fd, GetLastError());
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp b/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp
new file mode 100644
index 000000000..9694348c4
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/KeyValueHolder.cpp
@@ -0,0 +1,216 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "KeyValueHolder.h"
+#include "PBUtility.h"
+#include "aes/AESCrypt.h"
+#include
+#include
+#include
+
+namespace mmkv {
+
+KeyValueHolder::KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t off)
+ : keySize(static_cast(keyLength)), valueSize(valueLength), offset(off) {
+ computedKVSize = keySize + static_cast(pbRawVarint32Size(keySize));
+ computedKVSize += static_cast(pbRawVarint32Size(valueSize));
+}
+
+MMBuffer KeyValueHolder::toMMBuffer(const void *basePtr) const {
+ auto realPtr = (uint8_t *) basePtr + offset;
+ realPtr += computedKVSize;
+ return MMBuffer(realPtr, valueSize, MMBufferNoCopy);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(const void *src, size_t length) {
+ if (length <= SmallBufferSize()) {
+ type = KeyValueHolderType_Direct;
+ paddedSize = static_cast(length);
+ memcpy(paddedValue, src, length);
+ } else {
+ type = KeyValueHolderType_Memory;
+ memSize = static_cast(length);
+ memPtr = malloc(length);
+ if (!memPtr) {
+ throw std::runtime_error(strerror(errno));
+ }
+ memcpy(memPtr, src, memSize);
+ }
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(MMBuffer &&data) {
+ if (data.type == MMBuffer::MMBufferType_Small) {
+ static_assert(SmallBufferSize() >= MMBuffer::SmallBufferSize(), "KeyValueHolderCrypt can't hold MMBuffer");
+
+ type = KeyValueHolderType_Direct;
+ paddedSize = static_cast(data.length());
+ memcpy(paddedValue, data.getPtr(), data.length());
+ } else {
+# ifdef MMKV_APPLE
+ assert(data.m_data == nil);
+# endif
+ type = KeyValueHolderType_Memory;
+ memSize = static_cast(data.length());
+ memPtr = data.getPtr();
+
+ data.detach();
+ }
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t off)
+ : type(KeyValueHolderType_Offset), keySize(static_cast(keyLength)), valueSize(valueLength), offset(off) {
+
+ pbKeyValueSize = static_cast(pbRawVarint32Size(keySize) + pbRawVarint32Size(valueSize));
+}
+
+KeyValueHolderCrypt::KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept {
+ this->move(std::move(other));
+}
+
+KeyValueHolderCrypt &KeyValueHolderCrypt::operator=(KeyValueHolderCrypt &&other) noexcept {
+ if (type == KeyValueHolderType_Memory && memPtr) {
+ free(memPtr);
+ }
+ this->move(std::move(other));
+ return *this;
+}
+
+void KeyValueHolderCrypt::move(KeyValueHolderCrypt &&other) noexcept {
+ if (other.type == KeyValueHolderType_Direct || other.type == KeyValueHolderType_Offset) {
+ memcpy(this, &other, sizeof(other));
+ } else if (other.type == KeyValueHolderType_Memory) {
+ type = KeyValueHolderType_Memory;
+ memSize = other.memSize;
+ memPtr = other.memPtr;
+ other.memPtr = nullptr;
+ }
+}
+
+KeyValueHolderCrypt::~KeyValueHolderCrypt() {
+ if (type == KeyValueHolderType_Memory && memPtr) {
+ free(memPtr);
+ }
+}
+
+// get decrypt data with [position, -1)
+static MMBuffer decryptBuffer(AESCrypt &crypter, const MMBuffer &inputBuffer, size_t position) {
+ static uint8_t smallBuffer[16];
+ auto basePtr = (uint8_t *) inputBuffer.getPtr();
+ auto ptr = basePtr;
+ for (size_t index = sizeof(smallBuffer); index < position; index += sizeof(smallBuffer)) {
+ crypter.decrypt(ptr, smallBuffer, sizeof(smallBuffer));
+ ptr += sizeof(smallBuffer);
+ }
+ if (ptr < basePtr + position) {
+ crypter.decrypt(ptr, smallBuffer, static_cast(basePtr + position - ptr));
+ ptr = basePtr + position;
+ }
+ size_t length = inputBuffer.length() - position;
+ MMBuffer tmp(length);
+
+ auto input = ptr;
+ auto output = tmp.getPtr();
+ crypter.decrypt(input, output, length);
+
+ return tmp;
+}
+
+MMBuffer KeyValueHolderCrypt::toMMBuffer(const void *basePtr, const AESCrypt *crypter) const {
+ if (type == KeyValueHolderType_Direct) {
+ return MMBuffer((void *) paddedValue, paddedSize, MMBufferNoCopy);
+ } else if (type == KeyValueHolderType_Memory) {
+ return MMBuffer(memPtr, memSize, MMBufferNoCopy);
+ } else {
+ auto realPtr = (uint8_t *) basePtr + offset;
+ auto position = static_cast(pbKeyValueSize + keySize);
+ auto realSize = position + valueSize;
+ auto kvBuffer = MMBuffer(realPtr, realSize, MMBufferNoCopy);
+ auto decrypter = crypter->cloneWithStatus(cryptStatus);
+ return decryptBuffer(decrypter, kvBuffer, position);
+ }
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+} // namespace mmkv
+
+#if !defined(MMKV_DISABLE_CRYPT) && !defined(NDEBUG)
+# include "CodedInputData.h"
+# include "CodedOutputData.h"
+# include "MMKVLog.h"
+# include
+
+using namespace std;
+
+namespace mmkv {
+
+void KeyValueHolderCrypt::testAESToMMBuffer() {
+ const uint8_t plainText[] = "Hello, OpenSSL-mmkv::KeyValueHolderCrypt::testAESToMMBuffer() with AES CFB 128.";
+ constexpr size_t textLength = sizeof(plainText) - 1;
+
+ const uint8_t key[] = "TheAESKey";
+ constexpr size_t keyLength = sizeof(key) - 1;
+
+ uint8_t iv[AES_KEY_LEN];
+ srand((unsigned) time(nullptr));
+ for (uint32_t i = 0; i < AES_KEY_LEN; i++) {
+ iv[i] = (uint8_t) rand();
+ }
+ AESCrypt crypt1(key, keyLength, iv, sizeof(iv));
+
+ auto encryptText = new uint8_t[DEFAULT_MMAP_SIZE];
+ memset(encryptText, 0, DEFAULT_MMAP_SIZE);
+ CodedOutputData output(encryptText, DEFAULT_MMAP_SIZE);
+ output.writeData(MMBuffer((void *) key, keyLength, MMBufferNoCopy));
+ auto lengthOfValue = textLength + pbRawVarint32Size((uint32_t) textLength);
+ output.writeRawVarint32((int32_t) lengthOfValue);
+ output.writeData(MMBuffer((void *) plainText, textLength, MMBufferNoCopy));
+ crypt1.encrypt(encryptText, encryptText, (size_t)(output.curWritePointer() - encryptText));
+
+ AESCrypt decrypt(key, keyLength, iv, sizeof(iv));
+ uint8_t smallBuffer[32];
+ decrypt.decrypt(encryptText, smallBuffer, 5);
+ auto keySize = CodedInputData(smallBuffer, 5).readUInt32();
+ auto sizeOfKeySize = pbRawVarint32Size(keySize);
+ auto position = sizeOfKeySize;
+ decrypt.decrypt(encryptText + 5, smallBuffer + 5, static_cast(sizeOfKeySize + keySize - 5));
+ position += keySize;
+ decrypt.decrypt(encryptText + position, smallBuffer + position, 5);
+ auto valueSize = CodedInputData(smallBuffer + position, 5).readUInt32();
+ // auto sizeOfValueSize = pbRawVarint32Size(valueSize);
+ KeyValueHolderCrypt kvHolder(keySize, valueSize, 0);
+ auto rollbackSize = position + 5;
+ decrypt.statusBeforeDecrypt(encryptText + rollbackSize, smallBuffer + rollbackSize, rollbackSize,
+ kvHolder.cryptStatus);
+ auto value = kvHolder.toMMBuffer(encryptText, &decrypt);
+# ifdef MMKV_APPLE
+ MMKVInfo("testAESToMMBuffer: %@", CodedInputData((char *) value.getPtr(), value.length()).readString());
+# else
+ MMKVInfo("testAESToMMBuffer: %s", CodedInputData((char *) value.getPtr(), value.length()).readString().c_str());
+# endif
+ MMKVInfo("MMBuffer::SmallBufferSize() = %u, KeyValueHolderCrypt::SmallBufferSize() = %u",
+ MMBuffer::SmallBufferSize(), KeyValueHolderCrypt::SmallBufferSize());
+}
+
+} // namespace mmkv
+
+#endif
diff --git a/ios/Pods/MMKVCore/Core/KeyValueHolder.h b/ios/Pods/MMKVCore/Core/KeyValueHolder.h
new file mode 100644
index 000000000..355ee8905
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/KeyValueHolder.h
@@ -0,0 +1,116 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2020 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef KeyValueHolder_hpp
+#define KeyValueHolder_hpp
+#ifdef __cplusplus
+
+#include "MMBuffer.h"
+#include "aes/AESCrypt.h"
+
+namespace mmkv {
+
+#pragma pack(push, 1)
+
+struct KeyValueHolder {
+ uint16_t computedKVSize; // internal use only
+ uint16_t keySize;
+ uint32_t valueSize;
+ uint32_t offset;
+
+ KeyValueHolder() = default;
+ KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
+
+ MMBuffer toMMBuffer(const void *basePtr) const;
+};
+
+#ifndef MMKV_DISABLE_CRYPT
+
+enum KeyValueHolderType : uint8_t {
+ KeyValueHolderType_Direct, // store value directly
+ KeyValueHolderType_Memory, // store value in the heap memory
+ KeyValueHolderType_Offset, // store value by offset
+};
+
+// kv holder for encrypted mmkv
+struct KeyValueHolderCrypt {
+ KeyValueHolderType type = KeyValueHolderType_Direct;
+
+ union {
+ // store value by offset
+ struct {
+ uint8_t pbKeyValueSize; // size needed to encode keySize & valueSize
+ uint16_t keySize;
+ uint32_t valueSize;
+ uint32_t offset;
+ AESCryptStatus cryptStatus;
+ };
+ // store value directly
+ struct {
+ uint8_t paddedSize;
+ uint8_t paddedValue[1];
+ };
+ // store value in the heap memory
+ struct {
+ uint32_t memSize;
+ void *memPtr;
+ };
+ };
+
+ static constexpr size_t SmallBufferSize() {
+ return sizeof(KeyValueHolderCrypt) - offsetof(KeyValueHolderCrypt, paddedValue);
+ }
+
+ static bool isValueStoredAsOffset(size_t valueSize) { return valueSize >= 256; }
+
+ KeyValueHolderCrypt() = default;
+ KeyValueHolderCrypt(const void *valuePtr, size_t valueLength);
+ explicit KeyValueHolderCrypt(MMBuffer &&data);
+ KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
+
+ KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept;
+ KeyValueHolderCrypt &operator=(KeyValueHolderCrypt &&other) noexcept;
+ void move(KeyValueHolderCrypt &&other) noexcept;
+
+ ~KeyValueHolderCrypt();
+
+ MMBuffer toMMBuffer(const void *basePtr, const AESCrypt *crypter) const;
+
+ std::tuple toTuple() {
+ return std::make_tuple(offset, pbKeyValueSize + keySize + valueSize, &cryptStatus);
+ }
+
+ // those are expensive, just forbid it for possibly misuse
+ explicit KeyValueHolderCrypt(const KeyValueHolderCrypt &other) = delete;
+ KeyValueHolderCrypt &operator=(const KeyValueHolderCrypt &other) = delete;
+
+#ifndef NDEBUG
+ static void testAESToMMBuffer();
+#endif
+};
+
+#endif // MMKV_DISABLE_CRYPT
+
+#pragma pack(pop)
+
+} // namespace mmkv
+
+#endif
+#endif /* KeyValueHolder_hpp */
diff --git a/ios/Pods/MMKVCore/Core/MMBuffer.cpp b/ios/Pods/MMKVCore/Core/MMBuffer.cpp
new file mode 100644
index 000000000..6986959d4
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMBuffer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MMBuffer.h"
+#include
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif
+
+using namespace std;
+
+namespace mmkv {
+
+MMBuffer::MMBuffer(size_t length) {
+ if (length > SmallBufferSize()) {
+ type = MMBufferType_Normal;
+ isNoCopy = MMBufferCopy;
+ size = length;
+ ptr = malloc(size);
+ if (!ptr) {
+ throw std::runtime_error(strerror(errno));
+ }
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ } else {
+ type = MMBufferType_Small;
+ paddedSize = static_cast(length);
+ }
+}
+
+MMBuffer::MMBuffer(void *source, size_t length, MMBufferCopyFlag flag) : isNoCopy(flag) {
+ if (isNoCopy == MMBufferCopy) {
+ if (length > SmallBufferSize()) {
+ type = MMBufferType_Normal;
+ size = length;
+ ptr = malloc(size);
+ if (!ptr) {
+ throw std::runtime_error(strerror(errno));
+ }
+ memcpy(ptr, source, size);
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ } else {
+ type = MMBufferType_Small;
+ paddedSize = static_cast(length);
+ memcpy(paddedBuffer, source, length);
+ }
+ } else {
+ type = MMBufferType_Normal;
+ size = length;
+ ptr = source;
+#ifdef MMKV_APPLE
+ m_data = nil;
+#endif
+ }
+}
+
+#ifdef MMKV_APPLE
+MMBuffer::MMBuffer(NSData *data, MMBufferCopyFlag flag)
+ : type(MMBufferType_Normal), ptr((void *) data.bytes), size(data.length), isNoCopy(flag) {
+ if (isNoCopy == MMBufferCopy) {
+ m_data = [data retain];
+ } else {
+ m_data = data;
+ }
+}
+#endif
+
+MMBuffer::MMBuffer(MMBuffer &&other) noexcept : type(other.type) {
+ if (type == MMBufferType_Normal) {
+ size = other.size;
+ ptr = other.ptr;
+ isNoCopy = other.isNoCopy;
+#ifdef MMKV_APPLE
+ m_data = other.m_data;
+#endif
+ other.detach();
+ } else {
+ paddedSize = other.paddedSize;
+ memcpy(paddedBuffer, other.paddedBuffer, paddedSize);
+ }
+}
+
+MMBuffer &MMBuffer::operator=(MMBuffer &&other) noexcept {
+ if (type == MMBufferType_Normal) {
+ if (other.type == MMBufferType_Normal) {
+ std::swap(isNoCopy, other.isNoCopy);
+ std::swap(size, other.size);
+ std::swap(ptr, other.ptr);
+#ifdef MMKV_APPLE
+ std::swap(m_data, other.m_data);
+#endif
+ } else {
+ type = MMBufferType_Small;
+ if (isNoCopy == MMBufferCopy) {
+#ifdef MMKV_APPLE
+ if (m_data) {
+ [m_data release];
+ } else if (ptr) {
+ free(ptr);
+ }
+#else
+ if (ptr) {
+ free(ptr);
+ }
+#endif
+ }
+ paddedSize = other.paddedSize;
+ memcpy(paddedBuffer, other.paddedBuffer, paddedSize);
+ }
+ } else {
+ if (other.type == MMBufferType_Normal) {
+ type = MMBufferType_Normal;
+ isNoCopy = other.isNoCopy;
+ size = other.size;
+ ptr = other.ptr;
+#ifdef MMKV_APPLE
+ m_data = other.m_data;
+#endif
+ other.detach();
+ } else {
+ uint8_t tmp[SmallBufferSize()];
+ memcpy(tmp, other.paddedBuffer, other.paddedSize);
+ memcpy(other.paddedBuffer, paddedBuffer, paddedSize);
+ memcpy(paddedBuffer, tmp, other.paddedSize);
+ std::swap(paddedSize, other.paddedSize);
+ }
+ }
+
+ return *this;
+}
+
+MMBuffer::~MMBuffer() {
+ if (type == MMBufferType_Small) {
+ return;
+ }
+
+#ifdef MMKV_APPLE
+ if (m_data) {
+ if (isNoCopy == MMBufferCopy) {
+ [m_data release];
+ }
+ return;
+ }
+#endif
+
+ if (isNoCopy == MMBufferCopy && ptr) {
+ free(ptr);
+ }
+}
+
+void MMBuffer::detach() {
+ // type = MMBufferType_Small;
+ // paddedSize = 0;
+ auto memsetPtr = (size_t *) &type;
+ *memsetPtr = 0;
+}
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/MMBuffer.h b/ios/Pods/MMKVCore/Core/MMBuffer.h
new file mode 100644
index 000000000..c3268f2e9
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMBuffer.h
@@ -0,0 +1,104 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MMBUFFER_H
+#define MMKV_MMBUFFER_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+#include
+
+namespace mmkv {
+
+enum MMBufferCopyFlag : bool {
+ MMBufferCopy = false,
+ MMBufferNoCopy = true,
+};
+
+#pragma pack(push, 1)
+
+#ifndef MMKV_DISABLE_CRYPT
+struct KeyValueHolderCrypt;
+#endif
+
+class MMBuffer {
+ enum MMBufferType : uint8_t {
+ MMBufferType_Small, // store small buffer in stack memory
+ MMBufferType_Normal, // store in heap memory
+ };
+ MMBufferType type;
+
+ union {
+ struct {
+ MMBufferCopyFlag isNoCopy;
+ size_t size;
+ void *ptr;
+#ifdef MMKV_APPLE
+ NSData *m_data;
+#endif
+ };
+ struct {
+ uint8_t paddedSize;
+ // make at least 10 bytes to hold all primitive types (negative int32, int64, double etc) on 32 bit device
+ // on 64 bit device it's guaranteed larger than 10 bytes
+ uint8_t paddedBuffer[10];
+ };
+ };
+
+ static constexpr size_t SmallBufferSize() {
+ return sizeof(MMBuffer) - offsetof(MMBuffer, paddedBuffer);
+ }
+
+public:
+ explicit MMBuffer(size_t length = 0);
+ MMBuffer(void *source, size_t length, MMBufferCopyFlag flag = MMBufferCopy);
+#ifdef MMKV_APPLE
+ explicit MMBuffer(NSData *data, MMBufferCopyFlag flag = MMBufferCopy);
+#endif
+
+ MMBuffer(MMBuffer &&other) noexcept;
+ MMBuffer &operator=(MMBuffer &&other) noexcept;
+
+ ~MMBuffer();
+
+ void *getPtr() const { return (type == MMBufferType_Small) ? (void *) paddedBuffer : ptr; }
+
+ size_t length() const { return (type == MMBufferType_Small) ? paddedSize : size; }
+
+ // transfer ownership to others
+ void detach();
+
+ // those are expensive, just forbid it for possibly misuse
+ explicit MMBuffer(const MMBuffer &other) = delete;
+ MMBuffer &operator=(const MMBuffer &other) = delete;
+
+#ifndef MMKV_DISABLE_CRYPT
+ friend KeyValueHolderCrypt;
+#endif
+};
+
+#pragma pack(pop)
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MMBUFFER_H
diff --git a/ios/Pods/MMKVCore/Core/MMKV.cpp b/ios/Pods/MMKVCore/Core/MMKV.cpp
new file mode 100644
index 000000000..febcf7245
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV.cpp
@@ -0,0 +1,958 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "CodedInputData.h"
+#include "CodedOutputData.h"
+#include "InterProcessLock.h"
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "MMKVMetaInfo.hpp"
+#include "MMKV_IO.h"
+#include "MemoryFile.h"
+#include "MiniPBCoder.h"
+#include "PBUtility.h"
+#include "ScopedLock.hpp"
+#include "ThreadLock.h"
+#include "aes/AESCrypt.h"
+#include "aes/openssl/openssl_aes.h"
+#include "aes/openssl/openssl_md5.h"
+#include "crc32/Checksum.h"
+#include
+#include
+#include
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+using namespace mmkv;
+
+unordered_map *g_instanceDic;
+ThreadLock *g_instanceLock;
+MMKVPath_t g_rootDir;
+static mmkv::ErrorHandler g_errorHandler;
+size_t mmkv::DEFAULT_MMAP_SIZE;
+
+#ifndef MMKV_WIN32
+constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = "specialCharacter";
+#else
+constexpr auto SPECIAL_CHARACTER_DIRECTORY_NAME = L"specialCharacter";
+#endif
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+MMKV_NAMESPACE_BEGIN
+
+#ifndef MMKV_ANDROID
+MMKV::MMKV(const std::string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath)
+ : m_mmapID(mmapID)
+ , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
+ , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(m_path))
+ , m_metaFile(new MemoryFile(m_crcPath))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd()))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0) {
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else {
+ m_dic = new MMKVMap();
+ }
+# else
+ m_dic = new MMKVMap();
+# endif
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_lock->initialize();
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+#endif
+
+MMKV::~MMKV() {
+ clearMemoryCache();
+
+ delete m_dic;
+#ifndef MMKV_DISABLE_CRYPT
+ delete m_dicCrypt;
+ delete m_crypter;
+#endif
+ delete m_file;
+ delete m_metaFile;
+ delete m_metaInfo;
+ delete m_lock;
+ delete m_fileLock;
+ delete m_sharedProcessLock;
+ delete m_exclusiveProcessLock;
+}
+
+MMKV *MMKV::defaultMMKV(MMKVMode mode, string *cryptKey) {
+#ifndef MMKV_ANDROID
+ return mmkvWithID(DEFAULT_MMAP_ID, mode, cryptKey);
+#else
+ return mmkvWithID(DEFAULT_MMAP_ID, DEFAULT_MMAP_SIZE, mode, cryptKey);
+#endif
+}
+
+void initialize() {
+ g_instanceDic = new unordered_map;
+ g_instanceLock = new ThreadLock();
+ g_instanceLock->initialize();
+
+ mmkv::DEFAULT_MMAP_SIZE = mmkv::getPageSize();
+ MMKVInfo("version %s page size:%d", MMKV_VERSION, DEFAULT_MMAP_SIZE);
+#if !defined(NDEBUG) && !defined(MMKV_DISABLE_CRYPT)
+ AESCrypt::testAESCrypt();
+ KeyValueHolderCrypt::testAESToMMBuffer();
+#endif
+}
+
+ThreadOnceToken_t once_control = ThreadOnceUninitialized;
+
+void MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel) {
+ g_currentLogLevel = logLevel;
+
+ ThreadLock::ThreadOnce(&once_control, initialize);
+
+ g_rootDir = rootDir;
+ mkPath(g_rootDir);
+
+ MMKVInfo("root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
+}
+
+#ifndef MMKV_ANDROID
+MMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath) {
+
+ if (mmapID.empty()) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
+ auto itr = g_instanceDic->find(mmapKey);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+ return kv;
+ }
+
+ if (rootPath) {
+ MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
+ if (!isFileExist(specialPath)) {
+ mkPath(specialPath);
+ }
+ MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
+ }
+
+ auto kv = new MMKV(mmapID, mode, cryptKey, rootPath);
+ kv->m_mmapKey = mmapKey;
+ (*g_instanceDic)[mmapKey] = kv;
+ return kv;
+}
+#endif
+
+void MMKV::onExit() {
+ SCOPED_LOCK(g_instanceLock);
+
+ for (auto &pair : *g_instanceDic) {
+ MMKV *kv = pair.second;
+ kv->sync();
+ kv->clearMemoryCache();
+ delete kv;
+ pair.second = nullptr;
+ }
+
+ delete g_instanceDic;
+ g_instanceDic = nullptr;
+}
+
+const string &MMKV::mmapID() {
+ return m_mmapID;
+}
+
+mmkv::ContentChangeHandler g_contentChangeHandler = nullptr;
+
+void MMKV::notifyContentChanged() {
+ if (g_contentChangeHandler) {
+ g_contentChangeHandler(m_mmapID);
+ }
+}
+
+void MMKV::checkContentChanged() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+}
+
+void MMKV::registerContentChangeHandler(mmkv::ContentChangeHandler handler) {
+ g_contentChangeHandler = handler;
+}
+
+void MMKV::unRegisterContentChangeHandler() {
+ g_contentChangeHandler = nullptr;
+}
+
+void MMKV::clearMemoryCache() {
+ MMKVInfo("clearMemoryCache [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(m_lock);
+ if (m_needLoadFromFile) {
+ return;
+ }
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ clearDictionary(m_dic);
+#ifndef MMKV_DISABLE_CRYPT
+ clearDictionary(m_dicCrypt);
+ if (m_crypter) {
+ if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
+ m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
+ } else {
+ m_crypter->resetIV();
+ }
+ }
+#endif
+
+ delete m_output;
+ m_output = nullptr;
+
+ m_file->clearMemoryCache();
+ m_actualSize = 0;
+ m_metaInfo->m_crcDigest = 0;
+}
+
+void MMKV::close() {
+ MMKVInfo("close [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(g_instanceLock);
+ m_lock->lock();
+
+#ifndef MMKV_ANDROID
+ auto itr = g_instanceDic->find(m_mmapKey);
+#else
+ auto itr = g_instanceDic->find(m_mmapID);
+#endif
+ if (itr != g_instanceDic->end()) {
+ g_instanceDic->erase(itr);
+ }
+ delete this;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+string MMKV::cryptKey() {
+ SCOPED_LOCK(m_lock);
+
+ if (m_crypter) {
+ char key[AES_KEY_LEN];
+ m_crypter->getKey(key);
+ return string(key, strnlen(key, AES_KEY_LEN));
+ }
+ return "";
+}
+
+void MMKV::checkReSetCryptKey(const string *cryptKey) {
+ SCOPED_LOCK(m_lock);
+
+ if (m_crypter) {
+ if (cryptKey && cryptKey->length() > 0) {
+ string oldKey = this->cryptKey();
+ if (oldKey != *cryptKey) {
+ MMKVInfo("setting new aes key");
+ delete m_crypter;
+ auto ptr = cryptKey->data();
+ m_crypter = new AESCrypt(ptr, cryptKey->length());
+
+ checkLoadData();
+ } else {
+ // nothing to do
+ }
+ } else {
+ MMKVInfo("reset aes key");
+ delete m_crypter;
+ m_crypter = nullptr;
+
+ checkLoadData();
+ }
+ } else {
+ if (cryptKey && cryptKey->length() > 0) {
+ MMKVInfo("setting new aes key");
+ auto ptr = cryptKey->data();
+ m_crypter = new AESCrypt(ptr, cryptKey->length());
+
+ checkLoadData();
+ } else {
+ // nothing to do
+ }
+ }
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::isFileValid() {
+ return m_file->isFileValid();
+}
+
+// crc
+
+// assuming m_file is valid
+bool MMKV::checkFileCRCValid(size_t actualSize, uint32_t crcDigest) {
+ auto ptr = (uint8_t *) m_file->getMemory();
+ if (ptr) {
+ m_crcDigest = (uint32_t) CRC32(0, (const uint8_t *) ptr + Fixed32Size, (uint32_t) actualSize);
+
+ if (m_crcDigest == crcDigest) {
+ return true;
+ }
+ MMKVError("check crc [%s] fail, crc32:%u, m_crcDigest:%u", m_mmapID.c_str(), crcDigest, m_crcDigest);
+ }
+ return false;
+}
+
+void MMKV::recaculateCRCDigestWithIV(const void *iv) {
+ auto ptr = (const uint8_t *) m_file->getMemory();
+ if (ptr) {
+ m_crcDigest = 0;
+ m_crcDigest = (uint32_t) CRC32(0, ptr + Fixed32Size, (uint32_t) m_actualSize);
+ writeActualSize(m_actualSize, m_crcDigest, iv, IncreaseSequence);
+ }
+}
+
+void MMKV::updateCRCDigest(const uint8_t *ptr, size_t length) {
+ if (ptr == nullptr) {
+ return;
+ }
+ m_crcDigest = (uint32_t) CRC32(m_crcDigest, ptr, (uint32_t) length);
+
+ writeActualSize(m_actualSize, m_crcDigest, nullptr, KeepSequence);
+}
+
+// set & get
+
+bool MMKV::set(bool value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbBoolSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeBool(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(int32_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbInt32Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeInt32(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(uint32_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbUInt32Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeUInt32(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(int64_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbInt64Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeInt64(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(uint64_t value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbUInt64Size(value);
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeUInt64(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(float value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbFloatSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeFloat(value);
+
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::set(double value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ size_t size = pbDoubleSize();
+ MMBuffer data(size);
+ CodedOutputData output(data.getPtr(), size);
+ output.writeDouble(value);
+
+ return setDataForKey(move(data), key);
+}
+
+#ifndef MMKV_APPLE
+
+bool MMKV::set(const char *value, MMKVKey_t key) {
+ if (!value) {
+ removeValueForKey(key);
+ return true;
+ }
+ return setDataForKey(MMBuffer((void *) value, strlen(value), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const string &value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ return setDataForKey(MMBuffer((void *) value.data(), value.length(), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const MMBuffer &value, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ // delay write the size needed for encoding value
+ // avoid memory copying
+ return setDataForKey(MMBuffer(value.getPtr(), value.length(), MMBufferNoCopy), key, true);
+}
+
+bool MMKV::set(const vector &v, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ auto data = MiniPBCoder::encodeDataWithObject(v);
+ return setDataForKey(move(data), key);
+}
+
+bool MMKV::getString(MMKVKey_t key, string &result) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ result = input.readString();
+ return true;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return false;
+}
+
+MMBuffer MMKV::getBytes(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return MMBuffer();
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readData();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return MMBuffer();
+}
+
+bool MMKV::getVector(MMKVKey_t key, vector &result) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ result = MiniPBCoder::decodeVector(data);
+ return true;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return false;
+}
+
+#endif // MMKV_APPLE
+
+bool MMKV::getBool(MMKVKey_t key, bool defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readBool();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+int32_t MMKV::getInt32(MMKVKey_t key, int32_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readInt32();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+uint32_t MMKV::getUInt32(MMKVKey_t key, uint32_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readUInt32();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+int64_t MMKV::getInt64(MMKVKey_t key, int64_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readInt64();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+uint64_t MMKV::getUInt64(MMKVKey_t key, uint64_t defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readUInt64();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+float MMKV::getFloat(MMKVKey_t key, float defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readFloat();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+double MMKV::getDouble(MMKVKey_t key, double defaultValue) {
+ if (isKeyEmpty(key)) {
+ return defaultValue;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ return input.readDouble();
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return defaultValue;
+}
+
+size_t MMKV::getValueSize(MMKVKey_t key, bool actualSize) {
+ if (isKeyEmpty(key)) {
+ return 0;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (actualSize) {
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ auto length = input.readInt32();
+ if (length >= 0) {
+ auto s_length = static_cast(length);
+ if (pbRawVarint32Size(length) + s_length == data.length()) {
+ return s_length;
+ }
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+ return data.length();
+}
+
+int32_t MMKV::writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size) {
+ if (isKeyEmpty(key) || size < 0) {
+ return -1;
+ }
+ auto s_size = static_cast(size);
+
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ try {
+ CodedInputData input(data.getPtr(), data.length());
+ auto length = input.readInt32();
+ auto offset = pbRawVarint32Size(length);
+ if (length >= 0) {
+ auto s_length = static_cast(length);
+ if (offset + s_length == data.length()) {
+ if (s_length <= s_size) {
+ memcpy(ptr, (uint8_t *) data.getPtr() + offset, s_length);
+ return length;
+ }
+ } else {
+ if (data.length() <= s_size) {
+ memcpy(ptr, data.getPtr(), data.length());
+ return static_cast(data.length());
+ }
+ }
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ return -1;
+}
+
+// enumerate
+
+bool MMKV::containsKey(MMKVKey_t key) {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ if (m_crypter) {
+ return m_dicCrypt->find(key) != m_dicCrypt->end();
+ } else {
+ return m_dic->find(key) != m_dic->end();
+ }
+}
+
+size_t MMKV::count() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ if (m_crypter) {
+ return m_dicCrypt->size();
+ } else {
+ return m_dic->size();
+ }
+}
+
+size_t MMKV::totalSize() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ return m_file->getFileSize();
+}
+
+size_t MMKV::actualSize() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+ return m_actualSize;
+}
+
+void MMKV::removeValueForKey(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return;
+ }
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ removeDataForKey(key);
+}
+
+#ifndef MMKV_APPLE
+
+vector MMKV::allKeys() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ vector keys;
+ if (m_crypter) {
+ for (const auto &itr : *m_dicCrypt) {
+ keys.push_back(itr.first);
+ }
+ } else {
+ for (const auto &itr : *m_dic) {
+ keys.push_back(itr.first);
+ }
+ }
+ return keys;
+}
+
+void MMKV::removeValuesForKeys(const vector &arrKeys) {
+ if (arrKeys.empty()) {
+ return;
+ }
+ if (arrKeys.size() == 1) {
+ return removeValueForKey(arrKeys[0]);
+ }
+
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ size_t deleteCount = 0;
+ if (m_crypter) {
+ for (const auto &key : arrKeys) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ m_dicCrypt->erase(itr);
+ deleteCount++;
+ }
+ }
+ } else {
+ for (const auto &key : arrKeys) {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ m_dic->erase(itr);
+ deleteCount++;
+ }
+ }
+ }
+ if (deleteCount > 0) {
+ m_hasFullWriteback = false;
+
+ fullWriteback();
+ }
+}
+
+#endif // MMKV_APPLE
+
+// file
+
+void MMKV::sync(SyncFlag flag) {
+ SCOPED_LOCK(m_lock);
+ if (m_needLoadFromFile || !isFileValid()) {
+ return;
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ m_file->msync(flag);
+ m_metaFile->msync(flag);
+}
+
+void MMKV::lock() {
+ m_exclusiveProcessLock->lock();
+}
+void MMKV::unlock() {
+ m_exclusiveProcessLock->unlock();
+}
+bool MMKV::try_lock() {
+ return m_exclusiveProcessLock->try_lock();
+}
+
+void MMKV::registerErrorHandler(ErrorHandler handler) {
+ SCOPED_LOCK(g_instanceLock);
+ g_errorHandler = handler;
+}
+
+void MMKV::unRegisterErrorHandler() {
+ SCOPED_LOCK(g_instanceLock);
+ g_errorHandler = nullptr;
+}
+
+void MMKV::registerLogHandler(LogHandler handler) {
+ SCOPED_LOCK(g_instanceLock);
+ g_logHandler = handler;
+}
+
+void MMKV::unRegisterLogHandler() {
+ SCOPED_LOCK(g_instanceLock);
+ g_logHandler = nullptr;
+}
+
+void MMKV::setLogLevel(MMKVLogLevel level) {
+ SCOPED_LOCK(g_instanceLock);
+ g_currentLogLevel = level;
+}
+
+static void mkSpecialCharacterFileDirectory() {
+ MMKVPath_t path = g_rootDir + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;
+ mkPath(path);
+}
+
+template
+static string md5(const basic_string &value) {
+ uint8_t md[MD5_DIGEST_LENGTH] = {};
+ char tmp[3] = {}, buf[33] = {};
+ openssl::MD5((const uint8_t *) value.c_str(), value.size() * (sizeof(T) / sizeof(uint8_t)), md);
+ for (auto ch : md) {
+ snprintf(tmp, sizeof(tmp), "%2.2x", ch);
+ strcat(buf, tmp);
+ }
+ return string(buf);
+}
+
+static MMKVPath_t encodeFilePath(const string &mmapID) {
+ const char *specialCharacters = "\\/:*?\"<>|";
+ string encodedID;
+ bool hasSpecialCharacter = false;
+ for (auto ch : mmapID) {
+ if (strchr(specialCharacters, ch) != nullptr) {
+ encodedID = md5(mmapID);
+ hasSpecialCharacter = true;
+ break;
+ }
+ }
+ if (hasSpecialCharacter) {
+ static ThreadOnceToken_t once_control = ThreadOnceUninitialized;
+ ThreadLock::ThreadOnce(&once_control, mkSpecialCharacterFileDirectory);
+ return MMKVPath_t(SPECIAL_CHARACTER_DIRECTORY_NAME) + MMKV_PATH_SLASH + string2MMKVPath_t(encodedID);
+ } else {
+ return string2MMKVPath_t(mmapID);
+ }
+}
+
+string mmapedKVKey(const string &mmapID, MMKVPath_t *rootPath) {
+ if (rootPath && g_rootDir != (*rootPath)) {
+ return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));
+ }
+ return mmapID;
+}
+
+MMKVPath_t mappedKVPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
+#ifndef MMKV_ANDROID
+ if (rootPath) {
+#else
+ if (mode & MMKV_ASHMEM) {
+ return ashmemMMKVPathWithID(encodeFilePath(mmapID));
+ } else if (rootPath) {
+#endif
+ return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID);
+ }
+ return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID);
+}
+
+#ifndef MMKV_WIN32
+constexpr auto CRC_SUFFIX = ".crc";
+#else
+constexpr auto CRC_SUFFIX = L".crc";
+#endif
+
+MMKVPath_t crcPathWithID(const string &mmapID, MMKVMode mode, MMKVPath_t *rootPath) {
+#ifndef MMKV_ANDROID
+ if (rootPath) {
+#else
+ if (mode & MMKV_ASHMEM) {
+ return ashmemMMKVPathWithID(encodeFilePath(mmapID)) + CRC_SUFFIX;
+ } else if (rootPath) {
+#endif
+ return *rootPath + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
+ }
+ return g_rootDir + MMKV_PATH_SLASH + encodeFilePath(mmapID) + CRC_SUFFIX;
+}
+
+MMKVRecoverStrategic onMMKVCRCCheckFail(const string &mmapID) {
+ if (g_errorHandler) {
+ return g_errorHandler(mmapID, MMKVErrorType::MMKVCRCCheckFail);
+ }
+ return OnErrorDiscard;
+}
+
+MMKVRecoverStrategic onMMKVFileLengthError(const string &mmapID) {
+ if (g_errorHandler) {
+ return g_errorHandler(mmapID, MMKVErrorType::MMKVFileLength);
+ }
+ return OnErrorDiscard;
+}
+
+MMKV_NAMESPACE_END
diff --git a/ios/Pods/MMKVCore/Core/MMKV.h b/ios/Pods/MMKVCore/Core/MMKV.h
new file mode 100644
index 000000000..fb5057449
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV.h
@@ -0,0 +1,349 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MMKV_H
+#define MMKV_MMKV_H
+#ifdef __cplusplus
+
+#include "MMBuffer.h"
+#include
+
+namespace mmkv {
+class CodedOutputData;
+class MemoryFile;
+class AESCrypt;
+struct MMKVMetaInfo;
+class FileLock;
+class InterProcessLock;
+class ThreadLock;
+} // namespace mmkv
+
+MMKV_NAMESPACE_BEGIN
+
+enum MMKVMode : uint32_t {
+ MMKV_SINGLE_PROCESS = 0x1,
+ MMKV_MULTI_PROCESS = 0x2,
+#ifdef MMKV_ANDROID
+ CONTEXT_MODE_MULTI_PROCESS = 0x4, // in case someone mistakenly pass Context.MODE_MULTI_PROCESS
+ MMKV_ASHMEM = 0x8,
+#endif
+};
+
+class MMKV {
+#ifndef MMKV_ANDROID
+ std::string m_mmapKey;
+ MMKV(const std::string &mmapID, MMKVMode mode, std::string *cryptKey, MMKVPath_t *rootPath);
+#else // defined(MMKV_ANDROID)
+ MMKV(const std::string &mmapID, int size, MMKVMode mode, std::string *cryptKey, MMKVPath_t *rootPath);
+
+ MMKV(const std::string &mmapID, int ashmemFD, int ashmemMetaFd, std::string *cryptKey = nullptr);
+#endif
+
+ ~MMKV();
+
+ std::string m_mmapID;
+ MMKVPath_t m_path;
+ MMKVPath_t m_crcPath;
+ mmkv::MMKVMap *m_dic;
+ mmkv::MMKVMapCrypt *m_dicCrypt;
+
+ mmkv::MemoryFile *m_file;
+ size_t m_actualSize;
+ mmkv::CodedOutputData *m_output;
+
+ bool m_needLoadFromFile;
+ bool m_hasFullWriteback;
+
+ uint32_t m_crcDigest;
+ mmkv::MemoryFile *m_metaFile;
+ mmkv::MMKVMetaInfo *m_metaInfo;
+
+ mmkv::AESCrypt *m_crypter;
+
+ mmkv::ThreadLock *m_lock;
+ mmkv::FileLock *m_fileLock;
+ mmkv::InterProcessLock *m_sharedProcessLock;
+ mmkv::InterProcessLock *m_exclusiveProcessLock;
+
+#ifdef MMKV_APPLE
+ using MMKVKey_t = NSString *__unsafe_unretained;
+ static bool isKeyEmpty(MMKVKey_t key) { return key.length <= 0; }
+#else
+ using MMKVKey_t = const std::string &;
+ static bool isKeyEmpty(MMKVKey_t key) { return key.empty(); }
+#endif
+
+ void loadFromFile();
+
+ void partialLoadFromFile();
+
+ void checkDataValid(bool &loadFromFile, bool &needFullWriteback);
+
+ void checkLoadData();
+
+ bool isFileValid();
+
+ bool checkFileCRCValid(size_t actualSize, uint32_t crcDigest);
+
+ void recaculateCRCDigestWithIV(const void *iv);
+
+ void updateCRCDigest(const uint8_t *ptr, size_t length);
+
+ size_t readActualSize();
+
+ void oldStyleWriteActualSize(size_t actualSize);
+
+ bool writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence);
+
+ bool ensureMemorySize(size_t newSize);
+
+ bool fullWriteback(mmkv::AESCrypt *newCrypter = nullptr);
+
+ bool doFullWriteBack(std::pair preparedData, mmkv::AESCrypt *newCrypter);
+
+ mmkv::MMBuffer getDataForKey(MMKVKey_t key);
+
+ // isDataHolder: avoid memory copying
+ bool setDataForKey(mmkv::MMBuffer &&data, MMKVKey_t key, bool isDataHolder = false);
+
+ bool removeDataForKey(MMKVKey_t key);
+
+ using KVHolderRet_t = std::pair;
+ // isDataHolder: avoid memory copying
+ KVHolderRet_t doAppendDataWithKey(const mmkv::MMBuffer &data, const mmkv::MMBuffer &key, bool isDataHolder, uint32_t keyLength);
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, MMKVKey_t key, bool isDataHolder = false);
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data, const mmkv::KeyValueHolder &kvHolder, bool isDataHolder = false);
+#ifdef MMKV_APPLE
+ KVHolderRet_t appendDataWithKey(const mmkv::MMBuffer &data,
+ MMKVKey_t key,
+ const mmkv::KeyValueHolderCrypt &kvHolder,
+ bool isDataHolder = false);
+#endif
+
+ void notifyContentChanged();
+
+#if defined(MMKV_ANDROID) && !defined(MMKV_DISABLE_CRYPT)
+ void checkReSetCryptKey(int fd, int metaFD, std::string *cryptKey);
+#endif
+
+public:
+ // call this before getting any MMKV instance
+ static void initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel = MMKVLogInfo);
+
+#ifdef MMKV_APPLE
+ // protect from some old code that don't call initializeMMKV()
+ static void minimalInit(MMKVPath_t defaultRootDir);
+#endif
+
+ // a generic purpose instance
+ static MMKV *defaultMMKV(MMKVMode mode = MMKV_SINGLE_PROCESS, std::string *cryptKey = nullptr);
+
+#ifndef MMKV_ANDROID
+
+ // mmapID: any unique ID (com.tencent.xin.pay, etc)
+ // if you want a per-user mmkv, you could merge user-id within mmapID
+ // cryptKey: 16 bytes at most
+ static MMKV *mmkvWithID(const std::string &mmapID,
+ MMKVMode mode = MMKV_SINGLE_PROCESS,
+ std::string *cryptKey = nullptr,
+ MMKVPath_t *rootPath = nullptr);
+
+#else // defined(MMKV_ANDROID)
+
+ // mmapID: any unique ID (com.tencent.xin.pay, etc)
+ // if you want a per-user mmkv, you could merge user-id within mmapID
+ // cryptKey: 16 bytes at most
+ static MMKV *mmkvWithID(const std::string &mmapID,
+ int size = mmkv::DEFAULT_MMAP_SIZE,
+ MMKVMode mode = MMKV_SINGLE_PROCESS,
+ std::string *cryptKey = nullptr,
+ MMKVPath_t *rootPath = nullptr);
+
+ static MMKV *mmkvWithAshmemFD(const std::string &mmapID, int fd, int metaFD, std::string *cryptKey = nullptr);
+
+ int ashmemFD();
+
+ int ashmemMetaFD();
+
+#endif // MMKV_ANDROID
+
+ // you can call this on application termination, it's totally fine if you don't call
+ static void onExit();
+
+ const std::string &mmapID();
+
+ const bool m_isInterProcess;
+
+#ifndef MMKV_DISABLE_CRYPT
+ std::string cryptKey();
+
+ // transform plain text into encrypted text, or vice versa with empty cryptKey
+ // you can change existing crypt key with different cryptKey
+ bool reKey(const std::string &cryptKey);
+
+ // just reset cryptKey (will not encrypt or decrypt anything)
+ // usually you should call this method after other process reKey() the multi-process mmkv
+ void checkReSetCryptKey(const std::string *cryptKey);
+#endif
+
+ bool set(bool value, MMKVKey_t key);
+
+ bool set(int32_t value, MMKVKey_t key);
+
+ bool set(uint32_t value, MMKVKey_t key);
+
+ bool set(int64_t value, MMKVKey_t key);
+
+ bool set(uint64_t value, MMKVKey_t key);
+
+ bool set(float value, MMKVKey_t key);
+
+ bool set(double value, MMKVKey_t key);
+
+ // avoid unexpected type conversion (pointer to bool, etc)
+ template
+ bool set(T value, MMKVKey_t key) = delete;
+
+#ifdef MMKV_APPLE
+ bool set(NSObject *__unsafe_unretained obj, MMKVKey_t key);
+
+ NSObject *getObject(MMKVKey_t key, Class cls);
+#else // !defined(MMKV_APPLE)
+ bool set(const char *value, MMKVKey_t key);
+
+ bool set(const std::string &value, MMKVKey_t key);
+
+ bool set(const mmkv::MMBuffer &value, MMKVKey_t key);
+
+ bool set(const std::vector &vector, MMKVKey_t key);
+
+ bool getString(MMKVKey_t key, std::string &result);
+
+ mmkv::MMBuffer getBytes(MMKVKey_t key);
+
+ bool getVector(MMKVKey_t key, std::vector &result);
+#endif // MMKV_APPLE
+
+ bool getBool(MMKVKey_t key, bool defaultValue = false);
+
+ int32_t getInt32(MMKVKey_t key, int32_t defaultValue = 0);
+
+ uint32_t getUInt32(MMKVKey_t key, uint32_t defaultValue = 0);
+
+ int64_t getInt64(MMKVKey_t key, int64_t defaultValue = 0);
+
+ uint64_t getUInt64(MMKVKey_t key, uint64_t defaultValue = 0);
+
+ float getFloat(MMKVKey_t key, float defaultValue = 0);
+
+ double getDouble(MMKVKey_t key, double defaultValue = 0);
+
+ // return the actual size consumption of the key's value
+ // pass actualSize = true to get value's length
+ size_t getValueSize(MMKVKey_t key, bool actualSize);
+
+ // return size written into buffer
+ // return -1 on any error
+ int32_t writeValueToBuffer(MMKVKey_t key, void *ptr, int32_t size);
+
+ bool containsKey(MMKVKey_t key);
+
+ size_t count();
+
+ size_t totalSize();
+
+ size_t actualSize();
+
+#ifdef MMKV_APPLE
+ NSArray *allKeys();
+
+ void removeValuesForKeys(NSArray *arrKeys);
+
+ typedef void (^EnumerateBlock)(NSString *key, BOOL *stop);
+ void enumerateKeys(EnumerateBlock block);
+
+# ifdef MMKV_IOS
+ static void setIsInBackground(bool isInBackground);
+# endif
+#else // !defined(MMKV_APPLE)
+ std::vector allKeys();
+
+ void removeValuesForKeys(const std::vector &arrKeys);
+#endif // MMKV_APPLE
+
+ void removeValueForKey(MMKVKey_t key);
+
+ void clearAll();
+
+ // MMKV's size won't reduce after deleting key-values
+ // call this method after lots of deleting if you care about disk usage
+ // note that `clearAll` has the similar effect of `trim`
+ void trim();
+
+ // call this method if the instance is no longer needed in the near future
+ // any subsequent call to the instance is undefined behavior
+ void close();
+
+ // call this method if you are facing memory-warning
+ // any subsequent call to the instance will load all key-values from file again
+ void clearMemoryCache();
+
+ // you don't need to call this, really, I mean it
+ // unless you worry about running out of battery
+ void sync(SyncFlag flag = MMKV_SYNC);
+
+ // get exclusive access
+ void lock();
+ void unlock();
+ bool try_lock();
+
+ // check if content changed by other process
+ void checkContentChanged();
+
+ // called when content is changed by other process
+ // doesn't guarantee real-time notification
+ static void registerContentChangeHandler(mmkv::ContentChangeHandler handler);
+ static void unRegisterContentChangeHandler();
+
+ // by default MMKV will discard all datas on failure
+ // return `OnErrorRecover` to recover any data from file
+ static void registerErrorHandler(mmkv::ErrorHandler handler);
+ static void unRegisterErrorHandler();
+
+ // MMKVLogInfo by default
+ // pass MMKVLogNone to disable all logging
+ static void setLogLevel(MMKVLogLevel level);
+
+ // by default MMKV will print log to the console
+ // implement this method to redirect MMKV's log
+ static void registerLogHandler(mmkv::LogHandler handler);
+ static void unRegisterLogHandler();
+
+ static bool isFileValid(const std::string &mmapID, MMKVPath_t *relatePath = nullptr);
+
+ // just forbid it for possibly misuse
+ explicit MMKV(const MMKV &other) = delete;
+ MMKV &operator=(const MMKV &other) = delete;
+};
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif // MMKV_MMKV_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog.cpp b/ios/Pods/MMKVCore/Core/MMKVLog.cpp
new file mode 100644
index 000000000..5747958a1
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog.cpp
@@ -0,0 +1,128 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MMKVLog.h"
+
+MMKV_NAMESPACE_BEGIN
+
+#ifndef NDEBUG
+MMKVLogLevel g_currentLogLevel = MMKVLogDebug;
+#else
+MMKVLogLevel g_currentLogLevel = MMKVLogInfo;
+#endif
+
+mmkv::LogHandler g_logHandler;
+
+MMKV_NAMESPACE_END
+
+#ifdef ENABLE_MMKV_LOG
+# include
+# include
+
+using namespace mmkv;
+
+const char *_getFileName(const char *path) {
+ const char *ptr = strrchr(path, '/');
+ if (!ptr) {
+ ptr = strrchr(path, '\\');
+ }
+ if (ptr) {
+ return ptr + 1;
+ } else {
+ return path;
+ }
+}
+
+# ifndef MMKV_ANDROID
+
+static const char *MMKVLogLevelDesc(MMKVLogLevel level) {
+ switch (level) {
+ case MMKVLogDebug:
+ return "D";
+ case MMKVLogInfo:
+ return "I";
+ case MMKVLogWarning:
+ return "W";
+ case MMKVLogError:
+ return "E";
+ default:
+ return "N";
+ }
+}
+
+# ifdef MMKV_APPLE
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ NSString *nsFormat = [NSString stringWithUTF8String:format];
+ va_list argList;
+ va_start(argList, format);
+ NSString *message = [[NSString alloc] initWithFormat:nsFormat arguments:argList];
+ va_end(argList);
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ NSLog(@"[%s] <%s:%d::%s> %@", MMKVLogLevelDesc(level), filename, line, func, message);
+ }
+ }
+}
+
+# else
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ std::string message;
+ char buffer[16];
+
+ va_list args;
+ va_start(args, format);
+ auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if (length < 0) { // something wrong
+ message = {};
+ } else if (length < sizeof(buffer)) {
+ message = std::string(buffer, static_cast(length));
+ } else {
+ message.resize(static_cast(length), '\0');
+ va_start(args, format);
+ std::vsnprintf(const_cast(message.data()), static_cast(length) + 1, format, args);
+ va_end(args);
+ }
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ printf("[%s] <%s:%d::%s> %s\n", MMKVLogLevelDesc(level), filename, line, func, message.c_str());
+ //fflush(stdout);
+ }
+ }
+}
+
+# endif // MMKV_APPLE
+
+# endif // MMKV_ANDROID
+
+#endif // ENABLE_MMKV_LOG
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog.h b/ios/Pods/MMKVCore/Core/MMKVLog.h
new file mode 100644
index 000000000..f55f0ee9d
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog.h
@@ -0,0 +1,75 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MMKVLOG_H
+#define MMKV_MMKVLOG_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include
+#include
+#include
+
+void _MMKVLogWithLevel(
+ MMKV_NAMESPACE_PREFIX::MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...);
+
+MMKV_NAMESPACE_BEGIN
+
+extern MMKVLogLevel g_currentLogLevel;
+extern mmkv::LogHandler g_logHandler;
+
+// enable logging
+#define ENABLE_MMKV_LOG
+
+#ifdef ENABLE_MMKV_LOG
+
+# define MMKVError(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogError, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# define MMKVWarning(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogWarning, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# define MMKVInfo(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogInfo, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+
+# ifndef NDEBUG
+# define MMKVDebug(format, ...) \
+ _MMKVLogWithLevel(MMKV_NAMESPACE_PREFIX::MMKVLogDebug, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__)
+# else
+# define MMKVDebug(format, ...) \
+ {}
+# endif
+
+#else
+
+# define MMKVError(format, ...) \
+ {}
+# define MMKVWarning(format, ...) \
+ {}
+# define MMKVInfo(format, ...) \
+ {}
+# define MMKVDebug(format, ...) \
+ {}
+
+#endif
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif //MMKV_MMKVLOG_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp b/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp
new file mode 100644
index 000000000..ee2435f91
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVLog_Android.cpp
@@ -0,0 +1,83 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MMKVLog.h"
+
+#ifdef ENABLE_MMKV_LOG
+# ifdef MMKV_ANDROID
+# include
+# include
+# include
+
+using namespace std;
+
+constexpr auto APP_NAME = "MMKV";
+
+extern const char *_getFileName(const char *path);
+
+static android_LogPriority MMKVLogLevelDesc(MMKVLogLevel level) {
+ switch (level) {
+ case MMKVLogDebug:
+ return ANDROID_LOG_DEBUG;
+ case MMKVLogInfo:
+ return ANDROID_LOG_INFO;
+ case MMKVLogWarning:
+ return ANDROID_LOG_WARN;
+ case MMKVLogError:
+ return ANDROID_LOG_ERROR;
+ default:
+ return ANDROID_LOG_UNKNOWN;
+ }
+}
+
+void _MMKVLogWithLevel(MMKVLogLevel level, const char *file, const char *func, int line, const char *format, ...) {
+ if (level >= g_currentLogLevel) {
+ string message;
+ char buffer[16];
+
+ va_list args;
+ va_start(args, format);
+ auto length = std::vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if (length < 0) { // something wrong
+ message = {};
+ } else if (length < sizeof(buffer)) {
+ message = string(buffer, static_cast(length));
+ } else {
+ message.resize(static_cast(length), '\0');
+ va_start(args, format);
+ std::vsnprintf(const_cast(message.data()), static_cast(length) + 1, format, args);
+ va_end(args);
+ }
+
+ auto filename = _getFileName(file);
+
+ if (g_logHandler) {
+ g_logHandler(level, filename, line, func, message);
+ } else {
+ auto desc = MMKVLogLevelDesc(level);
+ __android_log_print(desc, APP_NAME, "<%s:%d::%s> %s", filename, line, func, message.c_str());
+ }
+ }
+}
+# endif // MMKV_ANDROID
+
+#endif // ENABLE_MMKV_LOG
diff --git a/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp b/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp
new file mode 100644
index 000000000..2aad778b6
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVMetaInfo.hpp
@@ -0,0 +1,81 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MMKVMETAINFO_H
+#define MMKV_MMKVMETAINFO_H
+#ifdef __cplusplus
+
+#include "aes/AESCrypt.h"
+#include
+#include
+
+namespace mmkv {
+
+enum MMKVVersion : uint32_t {
+ MMKVVersionDefault = 0,
+
+ // record full write back count
+ MMKVVersionSequence = 1,
+
+ // store random iv for encryption
+ MMKVVersionRandomIV = 2,
+
+ // store actual size together with crc checksum, try to reduce file corruption
+ MMKVVersionActualSize = 3,
+};
+
+struct MMKVMetaInfo {
+ uint32_t m_crcDigest = 0;
+ uint32_t m_version = MMKVVersionSequence;
+ uint32_t m_sequence = 0; // full write-back count
+ uint8_t m_vector[AES_KEY_LEN] = {};
+ uint32_t m_actualSize = 0;
+
+ // confirmed info: it's been synced to file
+ struct {
+ uint32_t lastActualSize = 0;
+ uint32_t lastCRCDigest = 0;
+ uint32_t _reserved[16] = {};
+ } m_lastConfirmedMetaInfo;
+
+ void write(void *ptr) const {
+ MMKV_ASSERT(ptr);
+ memcpy(ptr, this, sizeof(MMKVMetaInfo));
+ }
+
+ void writeCRCAndActualSizeOnly(void *ptr) const {
+ MMKV_ASSERT(ptr);
+ auto other = (MMKVMetaInfo *) ptr;
+ other->m_crcDigest = m_crcDigest;
+ other->m_actualSize = m_actualSize;
+ }
+
+ void read(const void *ptr) {
+ MMKV_ASSERT(ptr);
+ memcpy(this, ptr, sizeof(MMKVMetaInfo));
+ }
+};
+
+static_assert(sizeof(MMKVMetaInfo) <= (4 * 1024), "MMKVMetaInfo lager than one pagesize");
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MMKVMETAINFO_H
diff --git a/ios/Pods/MMKVCore/Core/MMKVPredef.h b/ios/Pods/MMKVCore/Core/MMKVPredef.h
new file mode 100755
index 000000000..dad0a1fb6
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKVPredef.h
@@ -0,0 +1,198 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_SRC_MMKVPREDEF_H
+#define MMKV_SRC_MMKVPREDEF_H
+
+// disable encryption & decryption to reduce some code
+//#define MMKV_DISABLE_CRYPT
+
+// using POSIX implementation
+//#define FORCE_POSIX
+
+#ifdef __cplusplus
+
+#include
+#include
+#include
+
+constexpr auto MMKV_VERSION = "v1.2.1";
+
+#ifdef __ANDROID__
+# define MMKV_ANDROID
+#elif __APPLE__
+# ifdef FORCE_POSIX
+# define MMKV_POSIX
+# else
+# define MMKV_APPLE
+# ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
+# define MMKV_IOS
+# elif __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__
+# define MMKV_WATCH
+# else
+# define MMKV_MAC
+# endif
+# endif // FORCE_POSIX
+#elif __linux__ || __unix__
+# define MMKV_POSIX
+# if __linux__
+# define MMKV_LINUX
+# endif
+#elif _WIN32
+# define MMKV_WIN32
+#endif
+
+#ifdef MMKV_WIN32
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT _WIN32_WINNT_WINXP
+# endif
+
+# include
+// Exclude rarely-used stuff from Windows headers
+# define WIN32_LEAN_AND_MEAN
+// Windows Header Files
+# include
+
+constexpr auto MMKV_PATH_SLASH = L"\\";
+# define MMKV_PATH_FORMAT "%ws"
+using MMKVFileHandle_t = HANDLE;
+using MMKVPath_t = std::wstring;
+extern MMKVPath_t string2MMKVPath_t(const std::string &str);
+
+# ifndef MMKV_EMBED_ZLIB
+# define MMKV_EMBED_ZLIB 1
+# endif
+
+#else // MMKV_WIN32
+
+constexpr auto MMKV_PATH_SLASH = "/";
+# define MMKV_PATH_FORMAT "%s"
+using MMKVFileHandle_t = int;
+using MMKVPath_t = std::string;
+# define string2MMKVPath_t(str) (str)
+
+# ifndef MMKV_EMBED_ZLIB
+# define MMKV_EMBED_ZLIB 0
+# endif
+
+#endif // MMKV_WIN32
+
+#ifdef MMKV_APPLE
+# import
+# define MMKV_NAMESPACE_BEGIN namespace mmkv {
+# define MMKV_NAMESPACE_END }
+# define MMKV_NAMESPACE_PREFIX mmkv
+using MMKVLog_t = NSString *;
+#else
+# define MMKV_NAMESPACE_BEGIN
+# define MMKV_NAMESPACE_END
+# define MMKV_NAMESPACE_PREFIX
+using MMKVLog_t = const std::string &;
+#endif // MMKV_APPLE
+
+MMKV_NAMESPACE_BEGIN
+
+enum MMKVLogLevel : int {
+ MMKVLogDebug = 0, // not available for release/product build
+ MMKVLogInfo = 1, // default level
+ MMKVLogWarning,
+ MMKVLogError,
+ MMKVLogNone, // special level used to disable all log messages
+};
+
+enum MMKVRecoverStrategic : int {
+ OnErrorDiscard = 0,
+ OnErrorRecover,
+};
+
+enum MMKVErrorType : int {
+ MMKVCRCCheckFail = 0,
+ MMKVFileLength,
+};
+
+enum SyncFlag : bool { MMKV_SYNC = true, MMKV_ASYNC = false };
+
+MMKV_NAMESPACE_END
+
+namespace mmkv {
+
+typedef void (*LogHandler)(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message);
+
+// by default MMKV will discard all datas on failure
+// return `OnErrorRecover` to recover any data from file
+typedef MMKVRecoverStrategic (*ErrorHandler)(const std::string &mmapID, MMKVErrorType errorType);
+
+// called when content is changed by other process
+// doesn't guarantee real-time notification
+typedef void (*ContentChangeHandler)(const std::string &mmapID);
+
+extern size_t DEFAULT_MMAP_SIZE;
+#define DEFAULT_MMAP_ID "mmkv.default"
+
+class MMBuffer;
+struct KeyValueHolder;
+
+#ifdef MMKV_DISABLE_CRYPT
+using KeyValueHolderCrypt = KeyValueHolder;
+#else
+struct KeyValueHolderCrypt;
+#endif
+
+#ifdef MMKV_APPLE
+struct KeyHasher {
+ size_t operator()(NSString *key) const { return key.hash; }
+};
+
+struct KeyEqualer {
+ bool operator()(NSString *left, NSString *right) const {
+ if (left == right) {
+ return true;
+ }
+ return ([left isEqualToString:right] == YES);
+ }
+};
+
+using MMKVVector = std::vector>;
+using MMKVMap = std::unordered_map;
+using MMKVMapCrypt = std::unordered_map;
+#else
+using MMKVVector = std::vector>;
+using MMKVMap = std::unordered_map;
+using MMKVMapCrypt = std::unordered_map;
+#endif // MMKV_APPLE
+
+template
+void unused(const T &) {}
+
+constexpr size_t AES_KEY_LEN = 16;
+constexpr size_t AES_KEY_BITSET_LEN = 128;
+
+} // namespace mmkv
+
+#ifndef NDEBUG
+# include
+# define MMKV_ASSERT(var) assert(var)
+#else
+# define MMKV_ASSERT(var) mmkv::unused(var)
+#endif
+
+#endif //cplus-plus
+
+#endif //MMKV_SRC_MMKVPREDEF_H
diff --git a/ios/Pods/MMKVCore/Core/MMKV_Android.cpp b/ios/Pods/MMKVCore/Core/MMKV_Android.cpp
new file mode 100644
index 000000000..b57089fb7
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_Android.cpp
@@ -0,0 +1,202 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MMKV.h"
+
+#ifdef MMKV_ANDROID
+
+# include "InterProcessLock.h"
+# include "KeyValueHolder.h"
+# include "MMKVLog.h"
+# include "MMKVMetaInfo.hpp"
+# include "MemoryFile.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+# include
+
+using namespace std;
+using namespace mmkv;
+
+extern unordered_map *g_instanceDic;
+extern ThreadLock *g_instanceLock;
+
+extern string mmapedKVKey(const string &mmapID, string *rootPath);
+extern string mappedKVPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
+extern string crcPathWithID(const string &mmapID, MMKVMode mode, string *rootPath);
+
+MMKV::MMKV(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath)
+ : m_mmapID(mmapedKVKey(mmapID, rootPath)) // historically Android mistakenly use mmapKey as mmapID
+ , m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))
+ , m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(m_path, size, (mode & MMKV_ASHMEM) ? MMFILE_TYPE_ASHMEM : MMFILE_TYPE_FILE))
+ , m_metaFile(new MemoryFile(m_crcPath, DEFAULT_MMAP_SIZE, m_file->m_fileType))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd(), (mode & MMKV_ASHMEM)))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0 || (mode & CONTEXT_MODE_MULTI_PROCESS) != 0) {
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else
+# endif
+ {
+ m_dic = new MMKVMap();
+ }
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+
+MMKV::MMKV(const string &mmapID, int ashmemFD, int ashmemMetaFD, string *cryptKey)
+ : m_mmapID(mmapID)
+ , m_path(mappedKVPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
+ , m_crcPath(crcPathWithID(m_mmapID, MMKV_ASHMEM, nullptr))
+ , m_dic(nullptr)
+ , m_dicCrypt(nullptr)
+ , m_file(new MemoryFile(ashmemFD))
+ , m_metaFile(new MemoryFile(ashmemMetaFD))
+ , m_metaInfo(new MMKVMetaInfo())
+ , m_crypter(nullptr)
+ , m_lock(new ThreadLock())
+ , m_fileLock(new FileLock(m_metaFile->getFd(), true))
+ , m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))
+ , m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))
+ , m_isInterProcess(true) {
+
+ m_actualSize = 0;
+ m_output = nullptr;
+
+# ifndef MMKV_DISABLE_CRYPT
+ if (cryptKey && cryptKey->length() > 0) {
+ m_dicCrypt = new MMKVMapCrypt();
+ m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());
+ } else
+# endif
+ {
+ m_dic = new MMKVMap();
+ }
+
+ m_needLoadFromFile = true;
+ m_hasFullWriteback = false;
+
+ m_crcDigest = 0;
+
+ m_sharedProcessLock->m_enable = m_isInterProcess;
+ m_exclusiveProcessLock->m_enable = m_isInterProcess;
+
+ // sensitive zone
+ {
+ SCOPED_LOCK(m_sharedProcessLock);
+ loadFromFile();
+ }
+}
+
+MMKV *MMKV::mmkvWithID(const string &mmapID, int size, MMKVMode mode, string *cryptKey, string *rootPath) {
+
+ if (mmapID.empty()) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto mmapKey = mmapedKVKey(mmapID, rootPath);
+ auto itr = g_instanceDic->find(mmapKey);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+ return kv;
+ }
+ if (rootPath) {
+ if (!isFileExist(*rootPath)) {
+ if (!mkPath(*rootPath)) {
+ return nullptr;
+ }
+ }
+ MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());
+ }
+ auto kv = new MMKV(mmapID, size, mode, cryptKey, rootPath);
+ (*g_instanceDic)[mmapKey] = kv;
+ return kv;
+}
+
+MMKV *MMKV::mmkvWithAshmemFD(const string &mmapID, int fd, int metaFD, string *cryptKey) {
+
+ if (fd < 0) {
+ return nullptr;
+ }
+ SCOPED_LOCK(g_instanceLock);
+
+ auto itr = g_instanceDic->find(mmapID);
+ if (itr != g_instanceDic->end()) {
+ MMKV *kv = itr->second;
+# ifndef MMKV_DISABLE_CRYPT
+ kv->checkReSetCryptKey(fd, metaFD, cryptKey);
+# endif
+ return kv;
+ }
+ auto kv = new MMKV(mmapID, fd, metaFD, cryptKey);
+ (*g_instanceDic)[mmapID] = kv;
+ return kv;
+}
+
+int MMKV::ashmemFD() {
+ return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_file->getFd() : -1;
+}
+
+int MMKV::ashmemMetaFD() {
+ return (m_file->m_fileType & mmkv::MMFILE_TYPE_ASHMEM) ? m_metaFile->getFd() : -1;
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+void MMKV::checkReSetCryptKey(int fd, int metaFD, string *cryptKey) {
+ SCOPED_LOCK(m_lock);
+
+ checkReSetCryptKey(cryptKey);
+
+ if (m_file->m_fileType & MMFILE_TYPE_ASHMEM) {
+ if (m_file->getFd() != fd) {
+ ::close(fd);
+ }
+ if (m_metaFile->getFd() != metaFD) {
+ ::close(metaFD);
+ }
+ }
+}
+# endif // MMKV_DISABLE_CRYPT
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/MMKV_IO.cpp b/ios/Pods/MMKVCore/Core/MMKV_IO.cpp
new file mode 100644
index 000000000..7cf9d58d7
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_IO.cpp
@@ -0,0 +1,1114 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* 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.
+*/
+
+#include "MMKV_IO.h"
+#include "CodedInputData.h"
+#include "CodedOutputData.h"
+#include "InterProcessLock.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "MMKVMetaInfo.hpp"
+#include "MemoryFile.h"
+#include "MiniPBCoder.h"
+#include "PBUtility.h"
+#include "ScopedLock.hpp"
+#include "ThreadLock.h"
+#include "aes/AESCrypt.h"
+#include "aes/openssl/openssl_aes.h"
+#include "aes/openssl/openssl_md5.h"
+#include "crc32/Checksum.h"
+#include
+#include
+
+#ifdef MMKV_IOS
+# include "MMKV_OSX.h"
+#endif
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+using namespace mmkv;
+using KVHolderRet_t = std::pair;
+
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+MMKV_NAMESPACE_BEGIN
+
+void MMKV::loadFromFile() {
+ if (m_metaFile->isFileValid()) {
+ m_metaInfo->read(m_metaFile->getMemory());
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (m_metaInfo->m_version >= MMKVVersionRandomIV) {
+ m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));
+ }
+ }
+#endif
+ if (!m_file->isFileValid()) {
+ m_file->reloadFromFile();
+ }
+ if (!m_file->isFileValid()) {
+ MMKVError("file [%s] not valid", m_path.c_str());
+ } else {
+ // error checking
+ bool loadFromFile = false, needFullWriteback = false;
+ checkDataValid(loadFromFile, needFullWriteback);
+ MMKVInfo("loading [%s] with %zu actual size, file size %zu, InterProcess %d, meta info "
+ "version:%u",
+ m_mmapID.c_str(), m_actualSize, m_file->getFileSize(), m_isInterProcess, m_metaInfo->m_version);
+ auto ptr = (uint8_t *) m_file->getMemory();
+ // loading
+ if (loadFromFile && m_actualSize > 0) {
+ MMKVInfo("loading [%s] with crc %u sequence %u version %u", m_mmapID.c_str(), m_metaInfo->m_crcDigest,
+ m_metaInfo->m_sequence, m_metaInfo->m_version);
+ MMBuffer inputBuffer(ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);
+ if (m_crypter) {
+ clearDictionary(m_dicCrypt);
+ } else {
+ clearDictionary(m_dic);
+ }
+ if (needFullWriteback) {
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter);
+ } else
+#endif
+ {
+ MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer);
+ }
+ } else {
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::decodeMap(*m_dicCrypt, inputBuffer, m_crypter);
+ } else
+#endif
+ {
+ MiniPBCoder::decodeMap(*m_dic, inputBuffer);
+ }
+ }
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+ m_output->seek(m_actualSize);
+ if (needFullWriteback) {
+ fullWriteback();
+ }
+ } else {
+ // file not valid or empty, discard everything
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+ if (m_actualSize > 0) {
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
+ sync(MMKV_SYNC);
+ } else {
+ writeActualSize(0, 0, nullptr, KeepSequence);
+ }
+ }
+ if (m_crypter) {
+ MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dicCrypt->size());
+ } else {
+ MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic->size());
+ }
+ }
+
+ m_needLoadFromFile = false;
+}
+
+// read from last m_position
+void MMKV::partialLoadFromFile() {
+ m_metaInfo->read(m_metaFile->getMemory());
+
+ size_t oldActualSize = m_actualSize;
+ m_actualSize = readActualSize();
+ auto fileSize = m_file->getFileSize();
+ MMKVDebug("loading [%s] with file size %zu, oldActualSize %zu, newActualSize %zu", m_mmapID.c_str(), fileSize,
+ oldActualSize, m_actualSize);
+
+ if (m_actualSize > 0) {
+ if (m_actualSize < fileSize && m_actualSize + Fixed32Size <= fileSize) {
+ if (m_actualSize > oldActualSize) {
+ auto position = oldActualSize;
+ size_t addedSize = m_actualSize - position;
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ // incremental update crc digest
+ m_crcDigest = (uint32_t) CRC32(m_crcDigest, basePtr + position, addedSize);
+ if (m_crcDigest == m_metaInfo->m_crcDigest) {
+ MMBuffer inputBuffer(basePtr, m_actualSize, MMBufferNoCopy);
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter, position);
+ } else
+#endif
+ {
+ MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer, position);
+ }
+ m_output->seek(addedSize);
+ m_hasFullWriteback = false;
+
+ if (m_crypter) {
+ MMKVDebug("partial loaded [%s] with %zu values", m_mmapID.c_str(), m_dicCrypt->size());
+ } else {
+ MMKVDebug("partial loaded [%s] with %zu values", m_mmapID.c_str(), m_dic->size());
+ }
+ return;
+ } else {
+ MMKVError("m_crcDigest[%u] != m_metaInfo->m_crcDigest[%u]", m_crcDigest, m_metaInfo->m_crcDigest);
+ }
+ }
+ }
+ }
+ // something is wrong, do a full load
+ clearMemoryCache();
+ loadFromFile();
+}
+
+void MMKV::checkDataValid(bool &loadFromFile, bool &needFullWriteback) {
+ // try auto recover from last confirmed location
+ auto fileSize = m_file->getFileSize();
+ auto checkLastConfirmedInfo = [&] {
+ if (m_metaInfo->m_version >= MMKVVersionActualSize) {
+ // downgrade & upgrade support
+ uint32_t oldStyleActualSize = 0;
+ memcpy(&oldStyleActualSize, m_file->getMemory(), Fixed32Size);
+ if (oldStyleActualSize != m_actualSize) {
+ MMKVWarning("oldStyleActualSize %u not equal to meta actual size %lu", oldStyleActualSize,
+ m_actualSize);
+ if (oldStyleActualSize < fileSize && (oldStyleActualSize + Fixed32Size) <= fileSize) {
+ if (checkFileCRCValid(oldStyleActualSize, m_metaInfo->m_crcDigest)) {
+ MMKVInfo("looks like [%s] been downgrade & upgrade again", m_mmapID.c_str());
+ loadFromFile = true;
+ writeActualSize(oldStyleActualSize, m_metaInfo->m_crcDigest, nullptr, KeepSequence);
+ return;
+ }
+ } else {
+ MMKVWarning("oldStyleActualSize %u greater than file size %lu", oldStyleActualSize, fileSize);
+ }
+ }
+
+ auto lastActualSize = m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize;
+ if (lastActualSize < fileSize && (lastActualSize + Fixed32Size) <= fileSize) {
+ auto lastCRCDigest = m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest;
+ if (checkFileCRCValid(lastActualSize, lastCRCDigest)) {
+ loadFromFile = true;
+ writeActualSize(lastActualSize, lastCRCDigest, nullptr, KeepSequence);
+ } else {
+ MMKVError("check [%s] error: lastActualSize %u, lastActualCRC %u", m_mmapID.c_str(), lastActualSize,
+ lastCRCDigest);
+ }
+ } else {
+ MMKVError("check [%s] error: lastActualSize %u, file size is %u", m_mmapID.c_str(), lastActualSize,
+ fileSize);
+ }
+ }
+ };
+
+ m_actualSize = readActualSize();
+
+ if (m_actualSize < fileSize && (m_actualSize + Fixed32Size) <= fileSize) {
+ if (checkFileCRCValid(m_actualSize, m_metaInfo->m_crcDigest)) {
+ loadFromFile = true;
+ } else {
+ checkLastConfirmedInfo();
+
+ if (!loadFromFile) {
+ auto strategic = onMMKVCRCCheckFail(m_mmapID);
+ if (strategic == OnErrorRecover) {
+ loadFromFile = true;
+ needFullWriteback = true;
+ }
+ MMKVInfo("recover strategic for [%s] is %d", m_mmapID.c_str(), strategic);
+ }
+ }
+ } else {
+ MMKVError("check [%s] error: %zu size in total, file size is %zu", m_mmapID.c_str(), m_actualSize, fileSize);
+
+ checkLastConfirmedInfo();
+
+ if (!loadFromFile) {
+ auto strategic = onMMKVFileLengthError(m_mmapID);
+ if (strategic == OnErrorRecover) {
+ // make sure we don't over read the file
+ m_actualSize = fileSize - Fixed32Size;
+ loadFromFile = true;
+ needFullWriteback = true;
+ }
+ MMKVInfo("recover strategic for [%s] is %d", m_mmapID.c_str(), strategic);
+ }
+ }
+}
+
+void MMKV::checkLoadData() {
+ if (m_needLoadFromFile) {
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ m_needLoadFromFile = false;
+ loadFromFile();
+ return;
+ }
+ if (!m_isInterProcess) {
+ return;
+ }
+
+ if (!m_metaFile->isFileValid()) {
+ return;
+ }
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ MMKVMetaInfo metaInfo;
+ metaInfo.read(m_metaFile->getMemory());
+ if (m_metaInfo->m_sequence != metaInfo.m_sequence) {
+ MMKVInfo("[%s] oldSeq %u, newSeq %u", m_mmapID.c_str(), m_metaInfo->m_sequence, metaInfo.m_sequence);
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ clearMemoryCache();
+ loadFromFile();
+ notifyContentChanged();
+ } else if (m_metaInfo->m_crcDigest != metaInfo.m_crcDigest) {
+ MMKVDebug("[%s] oldCrc %u, newCrc %u, new actualSize %u", m_mmapID.c_str(), m_metaInfo->m_crcDigest,
+ metaInfo.m_crcDigest, metaInfo.m_actualSize);
+ SCOPED_LOCK(m_sharedProcessLock);
+
+ size_t fileSize = m_file->getActualFileSize();
+ if (m_file->getFileSize() != fileSize) {
+ MMKVInfo("file size has changed [%s] from %zu to %zu", m_mmapID.c_str(), m_file->getFileSize(), fileSize);
+ clearMemoryCache();
+ loadFromFile();
+ } else {
+ partialLoadFromFile();
+ }
+ notifyContentChanged();
+ }
+}
+
+constexpr uint32_t ItemSizeHolder = 0x00ffffff;
+constexpr uint32_t ItemSizeHolderSize = 4;
+
+static pair prepareEncode(const mmkv::MMKVMap &dic) {
+ // make some room for placeholder
+ size_t totalSize = ItemSizeHolderSize;
+ for (auto &itr : dic) {
+ auto &kvHolder = itr.second;
+ totalSize += kvHolder.computedKVSize + kvHolder.valueSize;
+ }
+ return make_pair(MMBuffer(), totalSize);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+static pair prepareEncode(const mmkv::MMKVMapCrypt &dic) {
+ MMKVVector vec;
+ size_t totalSize = 0;
+ // make some room for placeholder
+ uint32_t smallestOffet = 5 + 1; // 5 is the largest size needed to encode varint32
+ for (auto &itr : dic) {
+ auto &kvHolder = itr.second;
+ if (kvHolder.type == KeyValueHolderType_Offset) {
+ totalSize += kvHolder.pbKeyValueSize + kvHolder.keySize + kvHolder.valueSize;
+ smallestOffet = min(smallestOffet, kvHolder.offset);
+ } else {
+ vec.emplace_back(itr.first, kvHolder.toMMBuffer(nullptr, nullptr));
+ }
+ }
+ if (smallestOffet > 5) {
+ smallestOffet = ItemSizeHolderSize;
+ }
+ totalSize += smallestOffet;
+ if (vec.empty()) {
+ return make_pair(MMBuffer(), totalSize);
+ }
+ auto buffer = MiniPBCoder::encodeDataWithObject(vec);
+ // skip the pb size of buffer
+ auto sizeOfMap = CodedInputData(buffer.getPtr(), buffer.length()).readUInt32();
+ totalSize += sizeOfMap;
+ return make_pair(move(buffer), totalSize);
+}
+#endif
+
+// since we use append mode, when -[setData: forKey:] many times, space may not be enough
+// try a full rewrite to make space
+bool MMKV::ensureMemorySize(size_t newSize) {
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+
+ if (newSize >= m_output->spaceLeft() || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {
+ // try a full rewrite to make space
+ auto fileSize = m_file->getFileSize();
+ auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
+ auto sizeOfDic = preparedData.second;
+ size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;
+ size_t dicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();
+ size_t avgItemSize = lenNeeded / std::max(1, dicCount);
+ size_t futureUsage = avgItemSize * std::max(8, (dicCount + 1) / 2);
+ // 1. no space for a full rewrite, double it
+ // 2. or space is not large enough for future usage, double it to avoid frequently full rewrite
+ if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {
+ size_t oldSize = fileSize;
+ do {
+ fileSize *= 2;
+ } while (lenNeeded + futureUsage >= fileSize);
+ MMKVInfo("extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu", m_mmapID.c_str(),
+ oldSize, fileSize, newSize, futureUsage);
+
+ // if we can't extend size, rollback to old state
+ if (!m_file->truncate(fileSize)) {
+ return false;
+ }
+
+ // check if we fail to make more space
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+ }
+ return doFullWriteBack(move(preparedData), nullptr);
+ }
+ return true;
+}
+
+size_t MMKV::readActualSize() {
+ MMKV_ASSERT(m_file->getMemory());
+ MMKV_ASSERT(m_metaFile->isFileValid());
+
+ uint32_t actualSize = 0;
+ memcpy(&actualSize, m_file->getMemory(), Fixed32Size);
+
+ if (m_metaInfo->m_version >= MMKVVersionActualSize) {
+ if (m_metaInfo->m_actualSize != actualSize) {
+ MMKVWarning("[%s] actual size %u, meta actual size %u", m_mmapID.c_str(), actualSize,
+ m_metaInfo->m_actualSize);
+ }
+ return m_metaInfo->m_actualSize;
+ } else {
+ return actualSize;
+ }
+}
+
+void MMKV::oldStyleWriteActualSize(size_t actualSize) {
+ MMKV_ASSERT(m_file->getMemory());
+
+ m_actualSize = actualSize;
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_file->getMemory(), Fixed32Size);
+ if (!ret.first) {
+ return;
+ }
+#endif
+ memcpy(m_file->getMemory(), &actualSize, Fixed32Size);
+}
+
+bool MMKV::writeActualSize(size_t size, uint32_t crcDigest, const void *iv, bool increaseSequence) {
+ // backward compatibility
+ oldStyleWriteActualSize(size);
+
+ if (!m_metaFile->isFileValid()) {
+ return false;
+ }
+
+ bool needsFullWrite = false;
+ m_actualSize = size;
+ m_metaInfo->m_actualSize = static_cast(size);
+ m_crcDigest = crcDigest;
+ m_metaInfo->m_crcDigest = crcDigest;
+ if (m_metaInfo->m_version < MMKVVersionSequence) {
+ m_metaInfo->m_version = MMKVVersionSequence;
+ needsFullWrite = true;
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (unlikely(iv)) {
+ memcpy(m_metaInfo->m_vector, iv, sizeof(m_metaInfo->m_vector));
+ if (m_metaInfo->m_version < MMKVVersionRandomIV) {
+ m_metaInfo->m_version = MMKVVersionRandomIV;
+ }
+ needsFullWrite = true;
+ }
+#endif
+ if (unlikely(increaseSequence)) {
+ m_metaInfo->m_sequence++;
+ m_metaInfo->m_lastConfirmedMetaInfo.lastActualSize = static_cast(size);
+ m_metaInfo->m_lastConfirmedMetaInfo.lastCRCDigest = crcDigest;
+ if (m_metaInfo->m_version < MMKVVersionActualSize) {
+ m_metaInfo->m_version = MMKVVersionActualSize;
+ }
+ needsFullWrite = true;
+ }
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_metaFile->getMemory(), sizeof(MMKVMetaInfo));
+ if (!ret.first) {
+ return false;
+ }
+#endif
+ if (unlikely(needsFullWrite)) {
+ m_metaInfo->write(m_metaFile->getMemory());
+ } else {
+ m_metaInfo->writeCRCAndActualSizeOnly(m_metaFile->getMemory());
+ }
+ return true;
+}
+
+MMBuffer MMKV::getDataForKey(MMKVKey_t key) {
+ checkLoadData();
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
+ return itr->second.toMMBuffer(basePtr, m_crypter);
+ }
+ } else
+#endif
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;
+ return itr->second.toMMBuffer(basePtr);
+ }
+ }
+ MMBuffer nan;
+ return nan;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+// for Apple watch simulator
+# if defined(TARGET_OS_SIMULATOR) && defined(TARGET_CPU_X86)
+static AESCryptStatus t_status;
+# else
+thread_local AESCryptStatus t_status;
+# endif
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {
+ if ((!isDataHolder && data.length() == 0) || isKeyEmpty(key)) {
+ return false;
+ }
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (isDataHolder) {
+ auto sizeNeededForData = pbRawVarint32Size((uint32_t) data.length()) + data.length();
+ if (!KeyValueHolderCrypt::isValueStoredAsOffset(sizeNeededForData)) {
+ data = MiniPBCoder::encodeDataWithObject(data);
+ isDataHolder = false;
+ }
+ }
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+# ifdef MMKV_APPLE
+ auto ret = appendDataWithKey(data, key, itr->second, isDataHolder);
+# else
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+# endif
+ if (!ret.first) {
+ return false;
+ }
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {
+ KeyValueHolderCrypt kvHolder(ret.second.keySize, ret.second.valueSize, ret.second.offset);
+ memcpy(&kvHolder.cryptStatus, &t_status, sizeof(t_status));
+ itr->second = move(kvHolder);
+ } else {
+ itr->second = KeyValueHolderCrypt(move(data));
+ }
+ } else {
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {
+ auto r = m_dicCrypt->emplace(
+ key, KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset));
+ if (r.second) {
+ memcpy(&(r.first->second.cryptStatus), &t_status, sizeof(t_status));
+ }
+ } else {
+ m_dicCrypt->emplace(key, KeyValueHolderCrypt(move(data)));
+ }
+ }
+ } else
+#endif // MMKV_DISABLE_CRYPT
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ auto ret = appendDataWithKey(data, itr->second, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ itr->second = std::move(ret.second);
+ } else {
+ auto ret = appendDataWithKey(data, key, isDataHolder);
+ if (!ret.first) {
+ return false;
+ }
+ m_dic->emplace(key, std::move(ret.second));
+ }
+ }
+ m_hasFullWriteback = false;
+#ifdef MMKV_APPLE
+ [key retain];
+#endif
+ return true;
+}
+
+bool MMKV::removeDataForKey(MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ m_hasFullWriteback = false;
+ static MMBuffer nan;
+# ifdef MMKV_APPLE
+ auto ret = appendDataWithKey(nan, key, itr->second);
+# else
+ auto ret = appendDataWithKey(nan, key);
+# endif
+ if (ret.first) {
+# ifdef MMKV_APPLE
+ [itr->first release];
+# endif
+ m_dicCrypt->erase(itr);
+ }
+ return ret.first;
+ }
+ } else
+#endif // MMKV_DISABLE_CRYPT
+ {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ m_hasFullWriteback = false;
+ static MMBuffer nan;
+ auto ret = appendDataWithKey(nan, itr->second);
+ if (ret.first) {
+#ifdef MMKV_APPLE
+ [itr->first release];
+#endif
+ m_dic->erase(itr);
+ }
+ return ret.first;
+ }
+ }
+
+ return false;
+}
+
+KVHolderRet_t
+MMKV::doAppendDataWithKey(const MMBuffer &data, const MMBuffer &keyData, bool isDataHolder, uint32_t originKeyLength) {
+ auto isKeyEncoded = (originKeyLength < keyData.length());
+ auto keyLength = static_cast(keyData.length());
+ auto valueLength = static_cast(data.length());
+ if (isDataHolder) {
+ valueLength += pbRawVarint32Size(valueLength);
+ }
+ // size needed to encode the key
+ size_t size = isKeyEncoded ? keyLength : (keyLength + pbRawVarint32Size(keyLength));
+ // size needed to encode the value
+ size += valueLength + pbRawVarint32Size(valueLength);
+
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ bool hasEnoughSize = ensureMemorySize(size);
+ if (!hasEnoughSize || !isFileValid()) {
+ return make_pair(false, KeyValueHolder());
+ }
+
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(m_output->curWritePointer(), size);
+ if (!ret.first) {
+ return make_pair(false, KeyValueHolder());
+ }
+#endif
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ if (KeyValueHolderCrypt::isValueStoredAsOffset(valueLength)) {
+ m_crypter->getCurStatus(t_status);
+ }
+ }
+#endif
+ try {
+ if (isKeyEncoded) {
+ m_output->writeRawData(keyData);
+ } else {
+ m_output->writeData(keyData);
+ }
+ if (isDataHolder) {
+ m_output->writeRawVarint32((int32_t) valueLength);
+ }
+ m_output->writeData(data); // note: write size of data
+ } catch (std::exception &e) {
+ MMKVError("%s", e.what());
+ return make_pair(false, KeyValueHolder());
+ }
+
+ auto offset = static_cast(m_actualSize);
+ auto ptr = (uint8_t *) m_file->getMemory() + Fixed32Size + m_actualSize;
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ m_crypter->encrypt(ptr, ptr, size);
+ }
+#endif
+ m_actualSize += size;
+ updateCRCDigest(ptr, size);
+
+ return make_pair(true, KeyValueHolder(originKeyLength, valueLength, offset));
+}
+
+KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, bool isDataHolder) {
+#ifdef MMKV_APPLE
+ auto oData = [key dataUsingEncoding:NSUTF8StringEncoding];
+ auto keyData = MMBuffer(oData, MMBufferNoCopy);
+#else
+ auto keyData = MMBuffer((void *) key.data(), key.size(), MMBufferNoCopy);
+#endif
+ return doAppendDataWithKey(data, keyData, isDataHolder, static_cast(keyData.length()));
+}
+
+KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ uint32_t keyLength = kvHolder.keySize;
+ // size needed to encode the key
+ size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);
+
+ // ensureMemorySize() might change kvHolder.offset, so have to do it early
+ {
+ auto valueLength = static_cast(data.length());
+ if (isDataHolder) {
+ valueLength += pbRawVarint32Size(valueLength);
+ }
+ auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);
+ bool hasEnoughSize = ensureMemorySize(size);
+ if (!hasEnoughSize) {
+ return make_pair(false, KeyValueHolder());
+ }
+ }
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ MMBuffer keyData(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);
+
+ return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);
+}
+
+bool MMKV::fullWriteback(AESCrypt *newCrypter) {
+ if (m_hasFullWriteback) {
+ return true;
+ }
+ if (m_needLoadFromFile) {
+ return true;
+ }
+ if (!isFileValid()) {
+ MMKVWarning("[%s] file not valid", m_mmapID.c_str());
+ return false;
+ }
+
+ if (m_crypter ? m_dicCrypt->empty() : m_dic->empty()) {
+ clearAll();
+ return true;
+ }
+
+ auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);
+ auto sizeOfDic = preparedData.second;
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ if (sizeOfDic > 0) {
+ auto fileSize = m_file->getFileSize();
+ if (sizeOfDic + Fixed32Size <= fileSize) {
+ return doFullWriteBack(move(preparedData), newCrypter);
+ } else {
+ assert(0);
+ assert(newCrypter == nullptr);
+ // ensureMemorySize will extend file & full rewrite, no need to write back again
+ return ensureMemorySize(sizeOfDic + Fixed32Size - fileSize);
+ }
+ }
+ return false;
+}
+
+// we don't need to really serialize the dictionary, just reuse what's already in the file
+static void
+memmoveDictionary(MMKVMap &dic, CodedOutputData *output, uint8_t *ptr, AESCrypt *encrypter, size_t totalSize) {
+ auto originOutputPtr = output->curWritePointer();
+ // make space to hold the fake size of dictionary's serialization result
+ auto writePtr = originOutputPtr + ItemSizeHolderSize;
+ // reuse what's already in the file
+ if (!dic.empty()) {
+ // sort by offset
+ vector vec;
+ vec.reserve(dic.size());
+ for (auto &itr : dic) {
+ vec.push_back(&itr.second);
+ }
+ sort(vec.begin(), vec.end(), [](const auto &left, const auto &right) { return left->offset < right->offset; });
+
+ // merge nearby items to make memmove quicker
+ vector> dataSections; // pair(offset, size)
+ dataSections.emplace_back(vec.front()->offset, vec.front()->computedKVSize + vec.front()->valueSize);
+ for (size_t index = 1, total = vec.size(); index < total; index++) {
+ auto kvHolder = vec[index];
+ auto &lastSection = dataSections.back();
+ if (kvHolder->offset == lastSection.first + lastSection.second) {
+ lastSection.second += kvHolder->computedKVSize + kvHolder->valueSize;
+ } else {
+ dataSections.emplace_back(kvHolder->offset, kvHolder->computedKVSize + kvHolder->valueSize);
+ }
+ }
+ // do the move
+ auto basePtr = ptr + Fixed32Size;
+ for (auto §ion : dataSections) {
+ // memmove() should handle this well: src == dst
+ memmove(writePtr, basePtr + section.first, section.second);
+ writePtr += section.second;
+ }
+ // update offset
+ if (!encrypter) {
+ auto offset = ItemSizeHolderSize;
+ for (auto kvHolder : vec) {
+ kvHolder->offset = offset;
+ offset += kvHolder->computedKVSize + kvHolder->valueSize;
+ }
+ }
+ }
+ // hold the fake size of dictionary's serialization result
+ output->writeRawVarint32(ItemSizeHolder);
+ auto writtenSize = static_cast(writePtr - originOutputPtr);
+#ifndef MMKV_DISABLE_CRYPT
+ if (encrypter) {
+ encrypter->encrypt(originOutputPtr, originOutputPtr, writtenSize);
+ }
+#endif
+ assert(writtenSize == totalSize);
+ output->seek(writtenSize - ItemSizeHolderSize);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+static void memmoveDictionary(MMKVMapCrypt &dic,
+ CodedOutputData *output,
+ uint8_t *ptr,
+ AESCrypt *decrypter,
+ AESCrypt *encrypter,
+ pair &preparedData) {
+ // reuse what's already in the file
+ vector vec;
+ if (!dic.empty()) {
+ // sort by offset
+ vec.reserve(dic.size());
+ for (auto &itr : dic) {
+ if (itr.second.type == KeyValueHolderType_Offset) {
+ vec.push_back(&itr.second);
+ }
+ }
+ sort(vec.begin(), vec.end(), [](auto left, auto right) { return left->offset < right->offset; });
+ }
+ auto sizeHolder = ItemSizeHolder, sizeHolderSize = ItemSizeHolderSize;
+ if (!vec.empty()) {
+ auto smallestOffset = vec.front()->offset;
+ if (smallestOffset != ItemSizeHolderSize && smallestOffset <= 5) {
+ sizeHolderSize = smallestOffset;
+ assert(sizeHolderSize != 0);
+ static const uint32_t ItemSizeHolders[] = {0, 0x0f, 0xff, 0xffff, 0xffffff, 0xffffffff};
+ sizeHolder = ItemSizeHolders[sizeHolderSize];
+ }
+ }
+ output->writeRawVarint32(static_cast(sizeHolder));
+ auto writePtr = output->curWritePointer();
+ if (encrypter) {
+ encrypter->encrypt(writePtr - sizeHolderSize, writePtr - sizeHolderSize, sizeHolderSize);
+ }
+ if (!vec.empty()) {
+ // merge nearby items to make memmove quicker
+ vector> dataSections; // pair(offset, size)
+ dataSections.push_back(vec.front()->toTuple());
+ for (size_t index = 1, total = vec.size(); index < total; index++) {
+ auto kvHolder = vec[index];
+ auto &lastSection = dataSections.back();
+ if (kvHolder->offset == get<0>(lastSection) + get<1>(lastSection)) {
+ get<1>(lastSection) += kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;
+ } else {
+ dataSections.push_back(kvHolder->toTuple());
+ }
+ }
+ // do the move
+ auto basePtr = ptr + Fixed32Size;
+ for (auto §ion : dataSections) {
+ auto crypter = decrypter->cloneWithStatus(*get<2>(section));
+ crypter.decrypt(basePtr + get<0>(section), writePtr, get<1>(section));
+ writePtr += get<1>(section);
+ }
+ // update offset & AESCryptStatus
+ if (encrypter) {
+ auto offset = sizeHolderSize;
+ for (auto kvHolder : vec) {
+ kvHolder->offset = offset;
+ auto size = kvHolder->pbKeyValueSize + kvHolder->keySize + kvHolder->valueSize;
+ encrypter->getCurStatus(kvHolder->cryptStatus);
+ encrypter->encrypt(basePtr + offset, basePtr + offset, size);
+ offset += size;
+ }
+ }
+ }
+ auto &data = preparedData.first;
+ if (data.length() > 0) {
+ auto dataSize = CodedInputData(data.getPtr(), data.length()).readUInt32();
+ if (dataSize > 0) {
+ auto dataPtr = (uint8_t *) data.getPtr() + pbRawVarint32Size(dataSize);
+ if (encrypter) {
+ encrypter->encrypt(dataPtr, writePtr, dataSize);
+ } else {
+ memcpy(writePtr, dataPtr, dataSize);
+ }
+ writePtr += dataSize;
+ }
+ }
+ auto writtenSize = static_cast(writePtr - output->curWritePointer());
+ assert(writtenSize + sizeHolderSize == preparedData.second);
+ output->seek(writtenSize);
+}
+
+# define InvalidCryptPtr ((AESCrypt *) (void *) (1))
+
+#endif // MMKV_DISABLE_CRYPT
+
+bool MMKV::doFullWriteBack(pair preparedData, AESCrypt *newCrypter) {
+ auto ptr = (uint8_t *) m_file->getMemory();
+ auto totalSize = preparedData.second;
+#ifdef MMKV_IOS
+ auto ret = guardForBackgroundWriting(ptr + Fixed32Size, totalSize);
+ if (!ret.first) {
+ return false;
+ }
+#endif
+
+#ifndef MMKV_DISABLE_CRYPT
+ uint8_t newIV[AES_KEY_LEN];
+ auto decrypter = m_crypter;
+ auto encrypter = (newCrypter == InvalidCryptPtr) ? nullptr : (newCrypter ? newCrypter : m_crypter);
+ if (encrypter) {
+ AESCrypt::fillRandomIV(newIV);
+ encrypter->resetIV(newIV, sizeof(newIV));
+ }
+#endif
+
+ delete m_output;
+ m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);
+#ifndef MMKV_DISABLE_CRYPT
+ if (m_crypter) {
+ memmoveDictionary(*m_dicCrypt, m_output, ptr, decrypter, encrypter, preparedData);
+ } else {
+#else
+ {
+ auto encrypter = m_crypter;
+#endif
+ memmoveDictionary(*m_dic, m_output, ptr, encrypter, totalSize);
+ }
+
+ m_actualSize = totalSize;
+#ifndef MMKV_DISABLE_CRYPT
+ if (encrypter) {
+ recaculateCRCDigestWithIV(newIV);
+ } else
+#endif
+ {
+ recaculateCRCDigestWithIV(nullptr);
+ }
+ m_hasFullWriteback = true;
+ // make sure lastConfirmedMetaInfo is saved
+ sync(MMKV_SYNC);
+ return true;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+bool MMKV::reKey(const string &cryptKey) {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ bool ret = false;
+ if (m_crypter) {
+ if (cryptKey.length() > 0) {
+ string oldKey = this->cryptKey();
+ if (cryptKey == oldKey) {
+ return true;
+ } else {
+ // change encryption key
+ MMKVInfo("reKey with new aes key");
+ auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length());
+ ret = fullWriteback(newCrypt);
+ if (ret) {
+ delete m_crypter;
+ m_crypter = newCrypt;
+ }
+ }
+ } else {
+ // decryption to plain text
+ MMKVInfo("reKey to no aes key");
+ ret = fullWriteback(InvalidCryptPtr);
+ if (ret) {
+ delete m_crypter;
+ m_crypter = nullptr;
+ if (!m_dic) {
+ m_dic = new MMKVMap();
+ }
+ }
+ }
+ } else {
+ if (cryptKey.length() > 0) {
+ // transform plain text to encrypted text
+ MMKVInfo("reKey to a aes key");
+ auto newCrypt = new AESCrypt(cryptKey.data(), cryptKey.length());
+ ret = fullWriteback(newCrypt);
+ if (ret) {
+ m_crypter = newCrypt;
+ if (!m_dicCrypt) {
+ m_dicCrypt = new MMKVMapCrypt();
+ }
+ }
+ } else {
+ return true;
+ }
+ }
+ // m_dic or m_dicCrypt is not valid after reKey
+ if (ret) {
+ clearMemoryCache();
+ }
+ return ret;
+}
+#endif
+
+void MMKV::trim() {
+ SCOPED_LOCK(m_lock);
+ MMKVInfo("prepare to trim %s", m_mmapID.c_str());
+
+ checkLoadData();
+
+ if (m_actualSize == 0) {
+ clearAll();
+ return;
+ } else if (m_file->getFileSize() <= DEFAULT_MMAP_SIZE) {
+ return;
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ fullWriteback();
+ auto oldSize = m_file->getFileSize();
+ auto fileSize = oldSize;
+ while (fileSize > (m_actualSize + Fixed32Size) * 2) {
+ fileSize /= 2;
+ }
+ fileSize = std::max(fileSize, DEFAULT_MMAP_SIZE);
+ if (oldSize == fileSize) {
+ MMKVInfo("there's no need to trim %s with size %zu, actualSize %zu", m_mmapID.c_str(), fileSize, m_actualSize);
+ return;
+ }
+
+ MMKVInfo("trimming %s from %zu to %zu, actualSize %zu", m_mmapID.c_str(), oldSize, fileSize, m_actualSize);
+
+ if (!m_file->truncate(fileSize)) {
+ return;
+ }
+ fileSize = m_file->getFileSize();
+ auto ptr = (uint8_t *) m_file->getMemory();
+ delete m_output;
+ m_output = new CodedOutputData(ptr + pbFixed32Size(), fileSize - Fixed32Size);
+ m_output->seek(m_actualSize);
+
+ MMKVInfo("finish trim %s from %zu to %zu", m_mmapID.c_str(), oldSize, fileSize);
+}
+
+void MMKV::clearAll() {
+ MMKVInfo("cleaning all key-values from [%s]", m_mmapID.c_str());
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ if (m_needLoadFromFile) {
+ m_file->reloadFromFile();
+ }
+
+ if (m_file->getFileSize() == DEFAULT_MMAP_SIZE && m_actualSize == 0) {
+ MMKVInfo("nothing to clear for [%s]", m_mmapID.c_str());
+ return;
+ }
+ m_file->truncate(DEFAULT_MMAP_SIZE);
+
+#ifndef MMKV_DISABLE_CRYPT
+ uint8_t newIV[AES_KEY_LEN];
+ AESCrypt::fillRandomIV(newIV);
+ if (m_crypter) {
+ m_crypter->resetIV(newIV, sizeof(newIV));
+ }
+ writeActualSize(0, 0, newIV, IncreaseSequence);
+#else
+ writeActualSize(0, 0, nullptr, IncreaseSequence);
+#endif
+ m_metaFile->msync(MMKV_SYNC);
+
+ clearMemoryCache();
+ loadFromFile();
+}
+
+bool MMKV::isFileValid(const string &mmapID, MMKVPath_t *relatePath) {
+ MMKVPath_t kvPath = mappedKVPathWithID(mmapID, MMKV_SINGLE_PROCESS, relatePath);
+ if (!isFileExist(kvPath)) {
+ return true;
+ }
+
+ MMKVPath_t crcPath = crcPathWithID(mmapID, MMKV_SINGLE_PROCESS, relatePath);
+ if (!isFileExist(crcPath)) {
+ return false;
+ }
+
+ uint32_t crcFile = 0;
+ MMBuffer *data = readWholeFile(crcPath);
+ if (data) {
+ if (data->getPtr()) {
+ MMKVMetaInfo metaInfo;
+ metaInfo.read(data->getPtr());
+ crcFile = metaInfo.m_crcDigest;
+ }
+ delete data;
+ } else {
+ return false;
+ }
+
+ uint32_t crcDigest = 0;
+ MMBuffer *fileData = readWholeFile(kvPath);
+ if (fileData) {
+ if (fileData->getPtr() && (fileData->length() >= Fixed32Size)) {
+ uint32_t actualSize = 0;
+ memcpy(&actualSize, fileData->getPtr(), Fixed32Size);
+ if (actualSize > (fileData->length() - Fixed32Size)) {
+ delete fileData;
+ return false;
+ }
+
+ crcDigest = (uint32_t) CRC32(0, (const uint8_t *) fileData->getPtr() + Fixed32Size, (uint32_t) actualSize);
+ }
+ delete fileData;
+ return crcFile == crcDigest;
+ } else {
+ return false;
+ }
+}
+
+MMKV_NAMESPACE_END
diff --git a/ios/Pods/MMKVCore/Core/MMKV_IO.h b/ios/Pods/MMKVCore/Core/MMKV_IO.h
new file mode 100644
index 000000000..4b2d723d2
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_IO.h
@@ -0,0 +1,57 @@
+/*
+* Tencent is pleased to support the open source community by making
+* MMKV available.
+*
+* Copyright (C) 2020 THL A29 Limited, a Tencent company.
+* All rights reserved.
+*
+* Licensed under the BSD 3-Clause License (the "License"); you may not use
+* this file except in compliance with the License. You may obtain a copy of
+* the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* 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.
+*/
+
+#ifndef MMKV_IO_h
+#define MMKV_IO_h
+#ifdef __cplusplus
+
+#include "MMKV.h"
+
+MMKV_NAMESPACE_BEGIN
+
+std::string mmapedKVKey(const std::string &mmapID, MMKVPath_t *rootPath = nullptr);
+MMKVPath_t mappedKVPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
+MMKVPath_t crcPathWithID(const std::string &mmapID, MMKVMode mode, MMKVPath_t *rootPath);
+
+MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID);
+MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID);
+
+template
+void clearDictionary(T *dic) {
+ if (!dic) {
+ return;
+ }
+#ifdef MMKV_APPLE
+ for (auto &pair : *dic) {
+ [pair.first release];
+ }
+#endif
+ dic->clear();
+}
+
+enum : bool {
+ KeepSequence = false,
+ IncreaseSequence = true,
+};
+
+MMKV_NAMESPACE_END
+
+#endif
+#endif /* MMKV_IO_h */
diff --git a/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp b/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp
new file mode 100644
index 000000000..a1db0e3ce
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_OSX.cpp
@@ -0,0 +1,333 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MMKVPredef.h"
+
+#ifdef MMKV_APPLE
+
+# include "CodedInputData.h"
+# include "CodedOutputData.h"
+# include "InterProcessLock.h"
+# include "MMKV.h"
+# include "MemoryFile.h"
+# include "MiniPBCoder.h"
+# include "PBUtility.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+# include "aes/AESCrypt.h"
+# include
+
+# ifdef MMKV_IOS
+# include "MMKV_OSX.h"
+# include
+# endif
+
+# ifdef __aarch64__
+# include "Checksum.h"
+# endif
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+using namespace mmkv;
+
+extern ThreadLock *g_instanceLock;
+extern MMKVPath_t g_rootDir;
+
+enum { UnKnown = 0, PowerMac = 1, Mac, iPhone, iPod, iPad, AppleTV, AppleWatch };
+static void GetAppleMachineInfo(int &device, int &version);
+
+MMKV_NAMESPACE_BEGIN
+
+# ifdef MMKV_IOS
+MLockPtr::MLockPtr(void *ptr, size_t size) : m_lockDownSize(0), m_lockedPtr(nullptr) {
+ if (!ptr || size == 0) {
+ return;
+ }
+ // calc ptr to mlock()
+ auto writePtr = (size_t) ptr;
+ auto lockPtr = (writePtr / DEFAULT_MMAP_SIZE) * DEFAULT_MMAP_SIZE;
+ auto lockDownSize = writePtr - lockPtr + size;
+ if (mlock((void *) lockPtr, lockDownSize) == 0) {
+ m_lockedPtr = (uint8_t *) lockPtr;
+ m_lockDownSize = lockDownSize;
+ } else {
+ MMKVError("fail to mlock [%p], %s", m_lockedPtr, strerror(errno));
+ // just fail on this condition, otherwise app will crash anyway
+ }
+}
+
+MLockPtr::MLockPtr(MLockPtr &&other) : m_lockDownSize(other.m_lockDownSize), m_lockedPtr(other.m_lockedPtr) {
+ other.m_lockedPtr = nullptr;
+}
+
+MLockPtr::~MLockPtr() {
+ if (m_lockedPtr) {
+ munlock(m_lockedPtr, m_lockDownSize);
+ }
+}
+
+# endif
+
+extern ThreadOnceToken_t once_control;
+extern void initialize();
+
+void MMKV::minimalInit(MMKVPath_t defaultRootDir) {
+ ThreadLock::ThreadOnce(&once_control, initialize);
+
+ // crc32 instruction requires A10 chip, aka iPhone 7 or iPad 6th generation
+ int device = 0, version = 0;
+ GetAppleMachineInfo(device, version);
+# ifdef MMKV_USE_ARMV8_CRC32
+ if ((device == iPhone && version >= 9) || (device == iPad && version >= 7)) {
+ CRC32 = mmkv::armv8_crc32;
+ }
+# endif
+ MMKVInfo("Apple Device:%d, version:%d", device, version);
+
+ g_rootDir = defaultRootDir;
+ mkPath(g_rootDir);
+
+ MMKVInfo("default root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());
+}
+
+# ifdef MMKV_IOS
+
+static bool g_isInBackground = false;
+
+void MMKV::setIsInBackground(bool isInBackground) {
+ SCOPED_LOCK(g_instanceLock);
+
+ g_isInBackground = isInBackground;
+ MMKVInfo("g_isInBackground:%d", g_isInBackground);
+}
+
+pair guardForBackgroundWriting(void *ptr, size_t size) {
+ if (g_isInBackground) {
+ MLockPtr mlockPtr(ptr, size);
+ return make_pair(mlockPtr.isLocked(), move(mlockPtr));
+ } else {
+ return make_pair(true, MLockPtr(nullptr, 0));
+ }
+}
+
+# endif // MMKV_IOS
+
+bool MMKV::set(NSObject *__unsafe_unretained obj, MMKVKey_t key) {
+ if (isKeyEmpty(key)) {
+ return false;
+ }
+ if (!obj) {
+ removeValueForKey(key);
+ return true;
+ }
+
+ NSData *tmpData = nil;
+ if ([obj isKindOfClass:NSString.class]) {
+ auto str = (NSString *) obj;
+ tmpData = [str dataUsingEncoding:NSUTF8StringEncoding];
+ } else if ([obj isKindOfClass:NSData.class]) {
+ tmpData = (NSData *) obj;
+ }
+ if (tmpData) {
+ // delay write the size needed for encoding tmpData
+ // avoid memory copying
+ return setDataForKey(MMBuffer(tmpData, MMBufferNoCopy), key, true);
+ } else if ([obj isKindOfClass:NSDate.class]) {
+ NSDate *oDate = (NSDate *) obj;
+ double time = oDate.timeIntervalSince1970;
+ return set(time, key);
+ } else {
+ /*if ([object conformsToProtocol:@protocol(NSCoding)])*/ {
+ auto tmp = [NSKeyedArchiver archivedDataWithRootObject:obj];
+ if (tmp.length > 0) {
+ return setDataForKey(MMBuffer(tmp, MMBufferNoCopy), key);
+ }
+ }
+ }
+ return false;
+}
+
+NSObject *MMKV::getObject(MMKVKey_t key, Class cls) {
+ if (isKeyEmpty(key) || !cls) {
+ return nil;
+ }
+ SCOPED_LOCK(m_lock);
+ auto data = getDataForKey(key);
+ if (data.length() > 0) {
+ if (MiniPBCoder::isCompatibleClass(cls)) {
+ try {
+ auto result = MiniPBCoder::decodeObject(data, cls);
+ return result;
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ if ([cls conformsToProtocol:@protocol(NSCoding)]) {
+ auto tmp = [NSData dataWithBytesNoCopy:data.getPtr() length:data.length() freeWhenDone:NO];
+ return [NSKeyedUnarchiver unarchiveObjectWithData:tmp];
+ }
+ }
+ }
+ return nil;
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+
+constexpr uint32_t Fixed32Size = pbFixed32Size();
+
+pair
+MMKV::appendDataWithKey(const MMBuffer &data, MMKVKey_t key, const KeyValueHolderCrypt &kvHolder, bool isDataHolder) {
+ if (kvHolder.type != KeyValueHolderType_Offset) {
+ return appendDataWithKey(data, key, isDataHolder);
+ }
+ SCOPED_LOCK(m_exclusiveProcessLock);
+
+ uint32_t keyLength = kvHolder.keySize;
+ // size needed to encode the key
+ size_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);
+
+ auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;
+ MMBuffer keyData(rawKeySize);
+ AESCrypt decrypter = m_crypter->cloneWithStatus(kvHolder.cryptStatus);
+ decrypter.decrypt(basePtr + kvHolder.offset, keyData.getPtr(), rawKeySize);
+
+ return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);
+}
+# endif
+
+NSArray *MMKV::allKeys() {
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ NSMutableArray *keys = [NSMutableArray array];
+ if (m_crypter) {
+ for (const auto &pair : *m_dicCrypt) {
+ [keys addObject:pair.first];
+ }
+ } else {
+ for (const auto &pair : *m_dic) {
+ [keys addObject:pair.first];
+ }
+ }
+ return keys;
+}
+
+void MMKV::removeValuesForKeys(NSArray *arrKeys) {
+ if (arrKeys.count == 0) {
+ return;
+ }
+ if (arrKeys.count == 1) {
+ return removeValueForKey(arrKeys[0]);
+ }
+
+ SCOPED_LOCK(m_lock);
+ SCOPED_LOCK(m_exclusiveProcessLock);
+ checkLoadData();
+
+ size_t deleteCount = 0;
+ if (m_crypter) {
+ for (NSString *key in arrKeys) {
+ auto itr = m_dicCrypt->find(key);
+ if (itr != m_dicCrypt->end()) {
+ [itr->first release];
+ m_dicCrypt->erase(itr);
+ deleteCount++;
+ }
+ }
+ } else {
+ for (NSString *key in arrKeys) {
+ auto itr = m_dic->find(key);
+ if (itr != m_dic->end()) {
+ [itr->first release];
+ m_dic->erase(itr);
+ deleteCount++;
+ }
+ }
+ }
+ if (deleteCount > 0) {
+ m_hasFullWriteback = false;
+
+ fullWriteback();
+ }
+}
+
+void MMKV::enumerateKeys(EnumerateBlock block) {
+ if (block == nil) {
+ return;
+ }
+ SCOPED_LOCK(m_lock);
+ checkLoadData();
+
+ MMKVInfo("enumerate [%s] begin", m_mmapID.c_str());
+ if (m_crypter) {
+ for (const auto &pair : *m_dicCrypt) {
+ BOOL stop = NO;
+ block(pair.first, &stop);
+ if (stop) {
+ break;
+ }
+ }
+ } else {
+ for (const auto &pair : *m_dic) {
+ BOOL stop = NO;
+ block(pair.first, &stop);
+ if (stop) {
+ break;
+ }
+ }
+ }
+ MMKVInfo("enumerate [%s] finish", m_mmapID.c_str());
+}
+
+MMKV_NAMESPACE_END
+
+static void GetAppleMachineInfo(int &device, int &version) {
+ device = UnKnown;
+ version = 0;
+
+ struct utsname systemInfo = {};
+ uname(&systemInfo);
+
+ std::string machine(systemInfo.machine);
+ if (machine.find("PowerMac") != std::string::npos || machine.find("Power Macintosh") != std::string::npos) {
+ device = PowerMac;
+ } else if (machine.find("Mac") != std::string::npos || machine.find("Macintosh") != std::string::npos) {
+ device = Mac;
+ } else if (machine.find("iPhone") != std::string::npos) {
+ device = iPhone;
+ } else if (machine.find("iPod") != std::string::npos) {
+ device = iPod;
+ } else if (machine.find("iPad") != std::string::npos) {
+ device = iPad;
+ } else if (machine.find("AppleTV") != std::string::npos) {
+ device = AppleTV;
+ } else if (machine.find("AppleWatch") != std::string::npos) {
+ device = AppleWatch;
+ }
+ auto pos = machine.find_first_of("0123456789");
+ if (pos != std::string::npos) {
+ version = std::atoi(machine.substr(pos).c_str());
+ }
+}
+
+#endif // MMKV_APPLE
diff --git a/ios/Pods/MMKVCore/Core/MMKV_OSX.h b/ios/Pods/MMKVCore/Core/MMKV_OSX.h
new file mode 100644
index 000000000..ae1915f6e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MMKV_OSX.h
@@ -0,0 +1,51 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#pragma once
+#include "MMKVPredef.h"
+
+#if defined(MMKV_IOS) && defined(__cplusplus)
+
+MMKV_NAMESPACE_BEGIN
+
+class MLockPtr {
+ size_t m_lockDownSize;
+ uint8_t *m_lockedPtr;
+
+public:
+ MLockPtr(void *ptr, size_t size);
+ MLockPtr(MLockPtr &&other);
+
+ ~MLockPtr();
+
+ bool isLocked() const {
+ return (m_lockedPtr != nullptr);
+ }
+
+ // just forbid it for possibly misuse
+ explicit MLockPtr(const MLockPtr &other) = delete;
+ MLockPtr &operator=(const MLockPtr &other) = delete;
+};
+
+std::pair guardForBackgroundWriting(void *ptr, size_t size);
+
+MMKV_NAMESPACE_END
+
+#endif
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile.cpp b/ios/Pods/MMKVCore/Core/MemoryFile.cpp
new file mode 100644
index 000000000..8247b166a
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile.cpp
@@ -0,0 +1,304 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MemoryFile.h"
+
+#ifndef MMKV_WIN32
+
+# include "InterProcessLock.h"
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include "ScopedLock.hpp"
+# include
+# include
+# include
+# include
+# include
+
+using namespace std;
+
+namespace mmkv {
+
+static bool getFileSize(int fd, size_t &size);
+
+# ifdef MMKV_ANDROID
+extern size_t ASharedMemory_getSize(int fd);
+# else
+MemoryFile::MemoryFile(const MMKVPath_t &path) : m_name(path), m_fd(-1), m_ptr(nullptr), m_size(0) {
+ reloadFromFile();
+}
+# endif // MMKV_ANDROID
+
+# ifdef MMKV_IOS
+void tryResetFileProtection(const string &path);
+# endif
+
+bool MemoryFile::truncate(size_t size) {
+ if (m_fd < 0) {
+ return false;
+ }
+ if (size == m_size) {
+ return true;
+ }
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ if (size > m_size) {
+ MMKVError("ashmem %s reach size limit:%zu, consider configure with larger size", m_name.c_str(), m_size);
+ } else {
+ MMKVInfo("no way to trim ashmem %s from %zu to smaller size %zu", m_name.c_str(), m_size, size);
+ }
+ return false;
+ }
+# endif // MMKV_ANDROID
+
+ auto oldSize = m_size;
+ m_size = size;
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+
+ if (::ftruncate(m_fd, static_cast(m_size)) != 0) {
+ MMKVError("fail to truncate [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
+ m_size = oldSize;
+ return false;
+ }
+ if (m_size > oldSize) {
+ if (!zeroFillFile(m_fd, oldSize, m_size - oldSize)) {
+ MMKVError("fail to zeroFile [%s] to size %zu, %s", m_name.c_str(), m_size, strerror(errno));
+ m_size = oldSize;
+ return false;
+ }
+ }
+
+ if (m_ptr) {
+ if (munmap(m_ptr, oldSize) != 0) {
+ MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ return ret;
+}
+
+bool MemoryFile::msync(SyncFlag syncFlag) {
+ if (m_ptr) {
+ auto ret = ::msync(m_ptr, m_size, syncFlag ? MMKV_SYNC : MMKV_ASYNC);
+ if (ret == 0) {
+ return true;
+ }
+ MMKVError("fail to msync [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ return false;
+}
+
+bool MemoryFile::mmap() {
+ m_ptr = (char *) ::mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
+ if (m_ptr == MAP_FAILED) {
+ MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));
+ m_ptr = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+void MemoryFile::reloadFromFile() {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ return;
+ }
+# endif
+ if (isFileValid()) {
+ MMKVWarning("calling reloadFromFile while the cache [%s] is still valid", m_name.c_str());
+ MMKV_ASSERT(0);
+ clearMemoryCache();
+ }
+
+ m_fd = open(m_name.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU);
+ if (m_fd < 0) {
+ MMKVError("fail to open:%s, %s", m_name.c_str(), strerror(errno));
+ } else {
+ FileLock fileLock(m_fd);
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
+ SCOPED_LOCK(&lock);
+
+ mmkv::getFileSize(m_fd, m_size);
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ truncate(roundSize);
+ } else {
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+# ifdef MMKV_IOS
+ tryResetFileProtection(m_name);
+# endif
+ }
+}
+
+void MemoryFile::doCleanMemoryCache(bool forceClean) {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM && !forceClean) {
+ return;
+ }
+# endif
+ if (m_ptr && m_ptr != MAP_FAILED) {
+ if (munmap(m_ptr, m_size) != 0) {
+ MMKVError("fail to munmap [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ m_ptr = nullptr;
+
+ if (m_fd >= 0) {
+ if (::close(m_fd) != 0) {
+ MMKVError("fail to close [%s], %s", m_name.c_str(), strerror(errno));
+ }
+ }
+ m_fd = -1;
+ m_size = 0;
+}
+
+size_t MemoryFile::getActualFileSize() {
+# ifdef MMKV_ANDROID
+ if (m_fileType == MMFILE_TYPE_ASHMEM) {
+ return ASharedMemory_getSize(m_fd);
+ }
+# endif
+ size_t size = 0;
+ mmkv::getFileSize(m_fd, size);
+ return size;
+}
+
+bool isFileExist(const string &nsFilePath) {
+ if (nsFilePath.empty()) {
+ return false;
+ }
+
+ struct stat temp = {};
+ return lstat(nsFilePath.c_str(), &temp) == 0;
+}
+
+extern bool mkPath(const MMKVPath_t &str) {
+ char *path = strdup(str.c_str());
+
+ struct stat sb = {};
+ bool done = false;
+ char *slash = path;
+
+ while (!done) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ done = (*slash == '\0');
+ *slash = '\0';
+
+ if (stat(path, &sb) != 0) {
+ if (errno != ENOENT || mkdir(path, 0777) != 0) {
+ MMKVWarning("%s : %s", path, strerror(errno));
+ free(path);
+ return false;
+ }
+ } else if (!S_ISDIR(sb.st_mode)) {
+ MMKVWarning("%s: %s", path, strerror(ENOTDIR));
+ free(path);
+ return false;
+ }
+
+ *slash = '/';
+ }
+ free(path);
+
+ return true;
+}
+
+MMBuffer *readWholeFile(const MMKVPath_t &path) {
+ MMBuffer *buffer = nullptr;
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ auto fileLength = lseek(fd, 0, SEEK_END);
+ if (fileLength > 0) {
+ buffer = new MMBuffer(static_cast(fileLength));
+ lseek(fd, 0, SEEK_SET);
+ auto readSize = read(fd, buffer->getPtr(), static_cast(fileLength));
+ if (readSize != -1) {
+ //fileSize = readSize;
+ } else {
+ MMKVWarning("fail to read %s: %s", path.c_str(), strerror(errno));
+
+ delete buffer;
+ buffer = nullptr;
+ }
+ }
+ close(fd);
+ } else {
+ MMKVWarning("fail to open %s: %s", path.c_str(), strerror(errno));
+ }
+ return buffer;
+}
+
+bool zeroFillFile(int fd, size_t startPos, size_t size) {
+ if (fd < 0) {
+ return false;
+ }
+
+ if (lseek(fd, static_cast(startPos), SEEK_SET) < 0) {
+ MMKVError("fail to lseek fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+
+ static const char zeros[4096] = {};
+ while (size >= sizeof(zeros)) {
+ if (write(fd, zeros, sizeof(zeros)) < 0) {
+ MMKVError("fail to write fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+ size -= sizeof(zeros);
+ }
+ if (size > 0) {
+ if (write(fd, zeros, size) < 0) {
+ MMKVError("fail to write fd[%d], error:%s", fd, strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool getFileSize(int fd, size_t &size) {
+ struct stat st = {};
+ if (fstat(fd, &st) != -1) {
+ size = (size_t) st.st_size;
+ return true;
+ }
+ return false;
+}
+
+size_t getPageSize() {
+ return static_cast(getpagesize());
+}
+
+} // namespace mmkv
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile.h b/ios/Pods/MMKVCore/Core/MemoryFile.h
new file mode 100644
index 000000000..a308deabb
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile.h
@@ -0,0 +1,106 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MAMERYFILE_H
+#define MMKV_MAMERYFILE_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#ifdef MMKV_ANDROID
+MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID);
+
+namespace mmkv {
+extern int g_android_api;
+
+enum FileType : bool { MMFILE_TYPE_FILE = false, MMFILE_TYPE_ASHMEM = true };
+} // namespace mmkv
+#endif // MMKV_ANDROID
+
+namespace mmkv {
+
+class MemoryFile {
+ MMKVPath_t m_name;
+ MMKVFileHandle_t m_fd;
+#ifdef MMKV_WIN32
+ HANDLE m_fileMapping;
+#endif
+ void *m_ptr;
+ size_t m_size;
+
+ bool mmap();
+
+ void doCleanMemoryCache(bool forceClean);
+
+public:
+#ifndef MMKV_ANDROID
+ explicit MemoryFile(const MMKVPath_t &path);
+#else
+ MemoryFile(const MMKVPath_t &path, size_t size, FileType fileType);
+ explicit MemoryFile(MMKVFileHandle_t ashmemFD);
+
+ const FileType m_fileType;
+#endif // MMKV_ANDROID
+
+ ~MemoryFile() { doCleanMemoryCache(true); }
+
+ size_t getFileSize() const { return m_size; }
+
+ // get the actual file size on disk
+ size_t getActualFileSize();
+
+ void *getMemory() { return m_ptr; }
+
+ const MMKVPath_t &getName() { return m_name; }
+
+ MMKVFileHandle_t getFd() { return m_fd; }
+
+ // the newly expanded file content will be zeroed
+ bool truncate(size_t size);
+
+ bool msync(SyncFlag syncFlag);
+
+ // call this if clearMemoryCache() has been called
+ void reloadFromFile();
+
+ void clearMemoryCache() { doCleanMemoryCache(false); }
+
+#ifndef MMKV_WIN32
+ bool isFileValid() { return m_fd >= 0 && m_size > 0 && m_ptr; }
+#else
+ bool isFileValid() { return m_fd != INVALID_HANDLE_VALUE && m_size > 0 && m_fileMapping && m_ptr; }
+#endif
+
+ // just forbid it for possibly misuse
+ explicit MemoryFile(const MemoryFile &other) = delete;
+ MemoryFile &operator=(const MemoryFile &other) = delete;
+};
+
+class MMBuffer;
+
+extern bool mkPath(const MMKVPath_t &path);
+extern bool isFileExist(const MMKVPath_t &nsFilePath);
+extern MMBuffer *readWholeFile(const MMKVPath_t &path);
+extern bool zeroFillFile(MMKVFileHandle_t fd, size_t startPos, size_t size);
+extern size_t getPageSize();
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MAMERYFILE_H
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp
new file mode 100644
index 000000000..c005151d1
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_Android.cpp
@@ -0,0 +1,192 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_ANDROID
+
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include
+# include
+# include
+# include
+# include
+
+using namespace std;
+
+constexpr char ASHMEM_NAME_DEF[] = "/dev/ashmem";
+
+namespace mmkv {
+
+// for Android Q limiting ashmem access
+extern int ASharedMemory_create(const char *name, size_t size);
+extern size_t ASharedMemory_getSize(int fd);
+extern string ASharedMemory_getName(int fd);
+
+MemoryFile::MemoryFile(const string &path, size_t size, FileType fileType)
+ : m_name(path), m_fd(-1), m_ptr(nullptr), m_size(0), m_fileType(fileType) {
+ if (m_fileType == MMFILE_TYPE_FILE) {
+ reloadFromFile();
+ } else {
+ // round up to (n * pagesize)
+ if (size < DEFAULT_MMAP_SIZE || (size % DEFAULT_MMAP_SIZE != 0)) {
+ size = ((size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+ auto filename = m_name.c_str();
+ auto ptr = strstr(filename, ASHMEM_NAME_DEF);
+ if (ptr && ptr[sizeof(ASHMEM_NAME_DEF) - 1] == '/') {
+ filename = ptr + sizeof(ASHMEM_NAME_DEF);
+ }
+ m_fd = ASharedMemory_create(filename, size);
+ if (m_fd >= 0) {
+ m_size = size;
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+ }
+}
+
+MemoryFile::MemoryFile(int ashmemFD)
+ : m_name(""), m_fd(ashmemFD), m_ptr(nullptr), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM) {
+ if (m_fd < 0) {
+ MMKVError("fd %d invalid", m_fd);
+ } else {
+ m_name = ASharedMemory_getName(m_fd);
+ m_size = ASharedMemory_getSize(m_fd);
+ MMKVInfo("ashmem name:%s, size:%zu", m_name.c_str(), m_size);
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+}
+
+} // namespace mmkv
+
+# pragma mark - ashmem
+# include
+# include
+
+namespace mmkv {
+
+constexpr auto ASHMEM_NAME_LEN = 256;
+constexpr auto __ASHMEMIOC = 0x77;
+# define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
+# define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
+# define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
+# define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
+
+int g_android_api = __ANDROID_API_L__;
+
+void *loadLibrary() {
+ auto name = "libandroid.so";
+ static auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
+ if (handle == RTLD_DEFAULT) {
+ MMKVError("unable to load library %s", name);
+ }
+ return handle;
+}
+
+typedef int (*AShmem_create_t)(const char *name, size_t size);
+
+int ASharedMemory_create(const char *name, size_t size) {
+ int fd = -1;
+ if (g_android_api >= __ANDROID_API_O__) {
+ static auto handle = loadLibrary();
+ static AShmem_create_t funcPtr =
+ (handle != nullptr) ? reinterpret_cast(dlsym(handle, "ASharedMemory_create")) : nullptr;
+ if (funcPtr) {
+ fd = funcPtr(name, size);
+ if (fd < 0) {
+ MMKVError("fail to ASharedMemory_create %s with size %zu, errno:%s", name, size, strerror(errno));
+ }
+ } else {
+ MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
+ }
+ }
+ if (fd < 0) {
+ fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ MMKVError("fail to open ashmem:%s, %s", name, strerror(errno));
+ } else {
+ if (ioctl(fd, ASHMEM_SET_NAME, name) != 0) {
+ MMKVError("fail to set ashmem name:%s, %s", name, strerror(errno));
+ } else if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
+ MMKVError("fail to set ashmem:%s, size %zu, %s", name, size, strerror(errno));
+ }
+ }
+ }
+ return fd;
+}
+
+typedef size_t (*AShmem_getSize_t)(int fd);
+
+size_t ASharedMemory_getSize(int fd) {
+ size_t size = 0;
+ if (g_android_api >= __ANDROID_API_O__) {
+ static auto handle = loadLibrary();
+ static AShmem_getSize_t funcPtr =
+ (handle != nullptr) ? reinterpret_cast(dlsym(handle, "ASharedMemory_getSize")) : nullptr;
+ if (funcPtr) {
+ size = funcPtr(fd);
+ if (size == 0) {
+ MMKVError("fail to ASharedMemory_getSize:%d, %s", fd, strerror(errno));
+ }
+ } else {
+ MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
+ }
+ }
+ if (size == 0) {
+ int tmp = ioctl(fd, ASHMEM_GET_SIZE, nullptr);
+ if (tmp < 0) {
+ MMKVError("fail to get ashmem size:%d, %s", fd, strerror(errno));
+ } else {
+ size = static_cast(tmp);
+ }
+ }
+ return size;
+}
+
+string ASharedMemory_getName(int fd) {
+ // Android Q doesn't have ASharedMemory_getName()
+ // I've make a request to Google, https://issuetracker.google.com/issues/130741665
+ // There's nothing we can do before it's supported officially by Google
+ if (g_android_api >= 29) {
+ return "";
+ }
+
+ char name[ASHMEM_NAME_LEN] = {0};
+ if (ioctl(fd, ASHMEM_GET_NAME, name) != 0) {
+ MMKVError("fail to get ashmem name:%d, %s", fd, strerror(errno));
+ return "";
+ }
+ return string(name);
+}
+
+} // namespace mmkv
+
+MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID) {
+ return MMKVPath_t(ASHMEM_NAME_DEF) + MMKV_PATH_SLASH + mmapID;
+}
+
+#endif // MMKV_ANDROID
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp
new file mode 100644
index 000000000..3235dc7f7
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_OSX.cpp
@@ -0,0 +1,52 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_IOS
+
+# include "MMKVLog.h"
+
+using namespace std;
+
+namespace mmkv {
+
+void tryResetFileProtection(const string &path) {
+ @autoreleasepool {
+ NSString *nsPath = [NSString stringWithUTF8String:path.c_str()];
+ NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:nsPath error:nullptr];
+ NSString *protection = [attr valueForKey:NSFileProtectionKey];
+ MMKVInfo("protection on [%@] is %@", nsPath, protection);
+ if ([protection isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication] == NO) {
+ NSMutableDictionary *newAttr = [NSMutableDictionary dictionaryWithDictionary:attr];
+ [newAttr setObject:NSFileProtectionCompleteUntilFirstUserAuthentication forKey:NSFileProtectionKey];
+ NSError *err = nil;
+ [[NSFileManager defaultManager] setAttributes:newAttr ofItemAtPath:nsPath error:&err];
+ if (err != nil) {
+ MMKVError("fail to set attribute %@ on [%@]: %@", NSFileProtectionCompleteUntilFirstUserAuthentication,
+ nsPath, err);
+ }
+ }
+ }
+}
+
+} // namespace mmkv
+
+#endif // MMKV_IOS
diff --git a/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp b/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp
new file mode 100644
index 000000000..29f0597b3
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MemoryFile_Win32.cpp
@@ -0,0 +1,315 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MemoryFile.h"
+
+#ifdef MMKV_WIN32
+
+# include "InterProcessLock.h"
+# include "MMBuffer.h"
+# include "MMKVLog.h"
+# include "ScopedLock.hpp"
+# include "ThreadLock.h"
+
+using namespace std;
+
+namespace mmkv {
+
+static bool getFileSize(MMKVFileHandle_t fd, size_t &size);
+static bool ftruncate(MMKVFileHandle_t file, size_t size);
+
+MemoryFile::MemoryFile(const MMKVPath_t &path)
+ : m_name(path), m_fd(INVALID_HANDLE_VALUE), m_fileMapping(nullptr), m_ptr(nullptr), m_size(0) {
+ reloadFromFile();
+}
+
+bool MemoryFile::truncate(size_t size) {
+ if (m_fd < 0) {
+ return false;
+ }
+ if (size == m_size) {
+ return true;
+ }
+
+ auto oldSize = m_size;
+ m_size = size;
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ }
+
+ if (!ftruncate(m_fd, m_size)) {
+ MMKVError("fail to truncate [%ws] to size %zu", m_name.c_str(), m_size);
+ m_size = oldSize;
+ return false;
+ }
+ if (m_size > oldSize) {
+ if (!zeroFillFile(m_fd, oldSize, m_size - oldSize)) {
+ MMKVError("fail to zeroFile [%ws] to size %zu", m_name.c_str(), m_size);
+ m_size = oldSize;
+ return false;
+ }
+ }
+
+ if (m_ptr) {
+ if (!UnmapViewOfFile(m_ptr)) {
+ MMKVError("fail to munmap [%ws], %d", m_name.c_str(), GetLastError());
+ }
+ m_ptr = nullptr;
+ }
+ if (m_fileMapping) {
+ CloseHandle(m_fileMapping);
+ m_fileMapping = nullptr;
+ }
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ return ret;
+}
+
+bool MemoryFile::msync(SyncFlag syncFlag) {
+ if (m_ptr) {
+ if (FlushViewOfFile(m_ptr, m_size)) {
+ if (syncFlag == MMKV_SYNC) {
+ if (!FlushFileBuffers(m_fd)) {
+ MMKVError("fail to FlushFileBuffers [%ws]:%d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ }
+ return true;
+ }
+ MMKVError("fail to FlushViewOfFile [%ws]:%d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ return false;
+}
+
+bool MemoryFile::mmap() {
+ m_fileMapping = CreateFileMapping(m_fd, nullptr, PAGE_READWRITE, 0, 0, nullptr);
+ if (!m_fileMapping) {
+ MMKVError("fail to CreateFileMapping [%ws], %d", m_name.c_str(), GetLastError());
+ return false;
+ } else {
+ m_ptr = (char *) MapViewOfFile(m_fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if (!m_ptr) {
+ MMKVError("fail to mmap [%ws], %d", m_name.c_str(), GetLastError());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void MemoryFile::reloadFromFile() {
+ if (isFileValid()) {
+ MMKVWarning("calling reloadFromFile while the cache [%ws] is still valid", m_name.c_str());
+ assert(0);
+ clearMemoryCache();
+ }
+
+ m_fd =
+ CreateFile(m_name.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (m_fd == INVALID_HANDLE_VALUE) {
+ MMKVError("fail to open:%ws, %d", m_name.c_str(), GetLastError());
+ } else {
+ FileLock fileLock(m_fd);
+ InterProcessLock lock(&fileLock, ExclusiveLockType);
+ SCOPED_LOCK(&lock);
+
+ mmkv::getFileSize(m_fd, m_size);
+ // round up to (n * pagesize)
+ if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) {
+ size_t roundSize = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
+ truncate(roundSize);
+ } else {
+ auto ret = mmap();
+ if (!ret) {
+ doCleanMemoryCache(true);
+ }
+ }
+ }
+}
+
+void MemoryFile::doCleanMemoryCache(bool forceClean) {
+ if (m_ptr) {
+ UnmapViewOfFile(m_ptr);
+ m_ptr = nullptr;
+ }
+ if (m_fileMapping) {
+ CloseHandle(m_fileMapping);
+ m_fileMapping = nullptr;
+ }
+ if (m_fd != INVALID_HANDLE_VALUE) {
+ CloseHandle(m_fd);
+ m_fd = INVALID_HANDLE_VALUE;
+ }
+}
+
+size_t MemoryFile::getActualFileSize() {
+ size_t size = 0;
+ mmkv::getFileSize(m_fd, size);
+ return size;
+}
+
+size_t getPageSize() {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ return system_info.dwPageSize;
+}
+
+bool isFileExist(const MMKVPath_t &nsFilePath) {
+ if (nsFilePath.empty()) {
+ return false;
+ }
+ auto attribute = GetFileAttributes(nsFilePath.c_str());
+ return (attribute != INVALID_FILE_ATTRIBUTES);
+}
+
+bool mkPath(const MMKVPath_t &str) {
+ wchar_t *path = _wcsdup(str.c_str());
+
+ bool done = false;
+ wchar_t *slash = path;
+
+ while (!done) {
+ slash += wcsspn(slash, L"\\");
+ slash += wcscspn(slash, L"\\");
+
+ done = (*slash == L'\0');
+ *slash = L'\0';
+
+ auto attribute = GetFileAttributes(path);
+ if (attribute == INVALID_FILE_ATTRIBUTES) {
+ if (!CreateDirectory(path, nullptr)) {
+ MMKVError("fail to create dir:%ws, %d", str.c_str(), GetLastError());
+ free(path);
+ return false;
+ }
+ } else if (!(attribute & FILE_ATTRIBUTE_DIRECTORY)) {
+ MMKVError("%ws attribute:%d not a directry", str.c_str(), attribute);
+ free(path);
+ return false;
+ }
+
+ *slash = L'\\';
+ }
+ free(path);
+ return true;
+}
+
+MMBuffer *readWholeFile(const MMKVPath_t &nsFilePath) {
+ MMBuffer *buffer = nullptr;
+ auto fd = CreateFile(nsFilePath.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (fd != INVALID_HANDLE_VALUE) {
+ size_t fileLength = 0;
+ getFileSize(fd, fileLength);
+ if (fileLength > 0) {
+ buffer = new MMBuffer(static_cast(fileLength));
+ SetFilePointer(fd, 0, 0, FILE_BEGIN);
+ DWORD readSize = 0;
+ if (ReadFile(fd, buffer->getPtr(), fileLength, &readSize, nullptr)) {
+ //fileSize = readSize;
+ } else {
+ MMKVWarning("fail to read %ws: %d", nsFilePath.c_str(), GetLastError());
+ delete buffer;
+ buffer = nullptr;
+ }
+ }
+ CloseHandle(fd);
+ } else {
+ MMKVWarning("fail to open %ws: %d", nsFilePath.c_str(), GetLastError());
+ }
+ return buffer;
+}
+
+bool zeroFillFile(MMKVFileHandle_t file, size_t startPos, size_t size) {
+ if (file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+ if (size == 0) {
+ return true;
+ }
+
+ LARGE_INTEGER position;
+ position.QuadPart = startPos;
+ if (!SetFilePointerEx(file, position, nullptr, FILE_BEGIN)) {
+ MMKVError("fail to lseek fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+
+ static const char zeros[4096] = {0};
+ while (size >= sizeof(zeros)) {
+ DWORD bytesWritten = 0;
+ if (!WriteFile(file, zeros, sizeof(zeros), &bytesWritten, nullptr)) {
+ MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+ size -= bytesWritten;
+ }
+ if (size > 0) {
+ DWORD bytesWritten = 0;
+ if (!WriteFile(file, zeros, size, &bytesWritten, nullptr)) {
+ MMKVError("fail to write fd[%p], error:%d", file, GetLastError());
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool ftruncate(MMKVFileHandle_t file, size_t size) {
+ LARGE_INTEGER large;
+ large.QuadPart = size;
+ if (SetFilePointerEx(file, large, 0, FILE_BEGIN)) {
+ if (SetEndOfFile(file)) {
+ return true;
+ }
+ MMKVError("fail to SetEndOfFile:%d", GetLastError());
+ return false;
+ } else {
+ MMKVError("fail to SetFilePointer:%d", GetLastError());
+ return false;
+ }
+}
+
+static bool getFileSize(MMKVFileHandle_t fd, size_t &size) {
+ LARGE_INTEGER filesize = {0};
+ if (GetFileSizeEx(fd, &filesize)) {
+ size = static_cast(filesize.QuadPart);
+ return true;
+ }
+ return false;
+}
+
+} // namespace mmkv
+
+std::wstring string2MMKVPath_t(const std::string &str) {
+ auto length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
+ auto buffer = new wchar_t[length];
+ MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, length);
+ wstring result(buffer);
+ delete[] buffer;
+ return result;
+}
+
+#endif // MMKV_WIN32
diff --git a/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp b/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp
new file mode 100644
index 000000000..8c88523e4
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MiniPBCoder.cpp
@@ -0,0 +1,366 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MiniPBCoder.h"
+#include "CodedInputData.h"
+#include "CodedInputDataCrypt.h"
+#include "CodedOutputData.h"
+#include "PBEncodeItem.hpp"
+
+#ifdef MMKV_APPLE
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+#endif // MMKV_APPLE
+
+using namespace std;
+
+namespace mmkv {
+
+MiniPBCoder::MiniPBCoder() : m_encodeItems(new std::vector()) {
+}
+
+MiniPBCoder::MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter) : MiniPBCoder() {
+ m_inputBuffer = inputBuffer;
+#ifndef MMKV_DISABLE_CRYPT
+ if (crypter) {
+ m_inputDataDecrpt = new CodedInputDataCrypt(m_inputBuffer->getPtr(), m_inputBuffer->length(), *crypter);
+ } else {
+ m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());
+ }
+#else
+ m_inputData = new CodedInputData(m_inputBuffer->getPtr(), m_inputBuffer->length());
+#endif // MMKV_DISABLE_CRYPT
+}
+
+MiniPBCoder::~MiniPBCoder() {
+ delete m_inputData;
+#ifndef MMKV_DISABLE_CRYPT
+ delete m_inputDataDecrpt;
+#endif
+ delete m_outputBuffer;
+ delete m_outputData;
+ delete m_encodeItems;
+}
+
+// encode
+
+// write object using prepared m_encodeItems[]
+void MiniPBCoder::writeRootObject() {
+ for (size_t index = 0, total = m_encodeItems->size(); index < total; index++) {
+ PBEncodeItem *encodeItem = &(*m_encodeItems)[index];
+ switch (encodeItem->type) {
+ case PBEncodeItemType_Data: {
+ m_outputData->writeData(*(encodeItem->value.bufferValue));
+ break;
+ }
+ case PBEncodeItemType_Container: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ break;
+ }
+#ifndef MMKV_APPLE
+ case PBEncodeItemType_String: {
+ m_outputData->writeString(*(encodeItem->value.strValue));
+ break;
+ }
+#else
+ case PBEncodeItemType_NSString: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ if (encodeItem->valueSize > 0 && encodeItem->value.tmpObjectValue != nullptr) {
+ auto obj = (__bridge NSData *) encodeItem->value.tmpObjectValue;
+ MMBuffer buffer(obj, MMBufferNoCopy);
+ m_outputData->writeRawData(buffer);
+ }
+ break;
+ }
+ case PBEncodeItemType_NSData: {
+ m_outputData->writeUInt32(encodeItem->valueSize);
+ if (encodeItem->valueSize > 0 && encodeItem->value.objectValue != nullptr) {
+ auto obj = (__bridge NSData *) encodeItem->value.objectValue;
+ MMBuffer buffer(obj, MMBufferNoCopy);
+ m_outputData->writeRawData(buffer);
+ }
+ break;
+ }
+ case PBEncodeItemType_NSDate: {
+ NSDate *oDate = (__bridge NSDate *) encodeItem->value.objectValue;
+ m_outputData->writeDouble(oDate.timeIntervalSince1970);
+ break;
+ }
+#endif // MMKV_APPLE
+ case PBEncodeItemType_None: {
+ MMKVError("%d", encodeItem->type);
+ break;
+ }
+ }
+ }
+}
+
+size_t MiniPBCoder::prepareObjectForEncode(const MMBuffer &buffer) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Data;
+ encodeItem->value.bufferValue = &buffer;
+ encodeItem->valueSize = static_cast(buffer.length());
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+size_t MiniPBCoder::prepareObjectForEncode(const MMKVVector &vec) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Container;
+ encodeItem->value.bufferValue = nullptr;
+
+ for (const auto &itr : vec) {
+ const auto &key = itr.first;
+ const auto &value = itr.second;
+# ifdef MMKV_APPLE
+ if (key.length <= 0) {
+# else
+ if (key.length() <= 0) {
+# endif
+ continue;
+ }
+
+ size_t keyIndex = prepareObjectForEncode(key);
+ if (keyIndex < m_encodeItems->size()) {
+ size_t valueIndex = prepareObjectForEncode(value);
+ if (valueIndex < m_encodeItems->size()) {
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[keyIndex].compiledSize;
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[valueIndex].compiledSize;
+ } else {
+ m_encodeItems->pop_back(); // pop key
+ }
+ }
+ }
+
+ encodeItem = &(*m_encodeItems)[index];
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+#endif // MMKV_DISABLE_CRYPT
+
+MMBuffer MiniPBCoder::writePreparedItems(size_t index) {
+ PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr;
+ if (oItem && oItem->compiledSize > 0) {
+ m_outputBuffer = new MMBuffer(oItem->compiledSize);
+ m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length());
+
+ writeRootObject();
+ }
+
+ return std::move(*m_outputBuffer);
+}
+
+MMBuffer MiniPBCoder::encodeDataWithObject(const MMBuffer &obj) {
+ try {
+ auto valueSize = static_cast(obj.length());
+ auto compiledSize = pbRawVarint32Size(valueSize) + valueSize;
+ MMBuffer result(compiledSize);
+ CodedOutputData output(result.getPtr(), result.length());
+ output.writeData(obj);
+ return result;
+ } catch (const std::exception &exception) {
+ MMKVError("%s", exception.what());
+ return MMBuffer();
+ }
+}
+
+#ifndef MMKV_APPLE
+
+size_t MiniPBCoder::prepareObjectForEncode(const string &str) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_String;
+ encodeItem->value.strValue = &str;
+ encodeItem->valueSize = static_cast(str.size());
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+size_t MiniPBCoder::prepareObjectForEncode(const vector &v) {
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+ {
+ encodeItem->type = PBEncodeItemType_Container;
+ encodeItem->value.bufferValue = nullptr;
+
+ for (const auto &str : v) {
+ size_t itemIndex = prepareObjectForEncode(str);
+ if (itemIndex < m_encodeItems->size()) {
+ (*m_encodeItems)[index].valueSize += (*m_encodeItems)[itemIndex].compiledSize;
+ }
+ }
+
+ encodeItem = &(*m_encodeItems)[index];
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+vector MiniPBCoder::decodeOneVector() {
+ vector v;
+
+ m_inputData->readInt32();
+
+ while (!m_inputData->isAtEnd()) {
+ auto value = m_inputData->readString();
+ v.push_back(move(value));
+ }
+
+ return v;
+}
+
+void MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMap &dictionary) {
+ if (position) {
+ m_inputData->seek(position);
+ } else {
+ m_inputData->readInt32();
+ }
+ while (!m_inputData->isAtEnd()) {
+ KeyValueHolder kvHolder;
+ const auto &key = m_inputData->readString(kvHolder);
+ if (key.length() > 0) {
+ m_inputData->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMap tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+
+void MiniPBCoder::decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMapCrypt &dictionary) {
+ if (position) {
+ m_inputDataDecrpt->seek(position);
+ } else {
+ m_inputDataDecrpt->readInt32();
+ }
+ while (!m_inputDataDecrpt->isAtEnd()) {
+ KeyValueHolderCrypt kvHolder;
+ const auto &key = m_inputDataDecrpt->readString(kvHolder);
+ if (key.length() > 0) {
+ m_inputDataDecrpt->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMapCrypt tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# endif // MMKV_DISABLE_CRYPT
+
+vector MiniPBCoder::decodeVector(const MMBuffer &oData) {
+ MiniPBCoder oCoder(&oData);
+ return oCoder.decodeOneVector();
+}
+
+#endif // MMKV_APPLE
+
+void MiniPBCoder::decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {
+ MiniPBCoder oCoder(&oData);
+ oCoder.decodeOneMap(dic, position, false);
+}
+
+void MiniPBCoder::greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position) {
+ MiniPBCoder oCoder(&oData);
+ oCoder.decodeOneMap(dic, position, true);
+}
+
+#ifndef MMKV_DISABLE_CRYPT
+
+void MiniPBCoder::decodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {
+ MiniPBCoder oCoder(&oData, crypter);
+ oCoder.decodeOneMap(dic, position, false);
+}
+
+void MiniPBCoder::greedyDecodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position) {
+ MiniPBCoder oCoder(&oData, crypter);
+ oCoder.decodeOneMap(dic, position, true);
+}
+
+#endif
+
+} // namespace mmkv
diff --git a/ios/Pods/MMKVCore/Core/MiniPBCoder.h b/ios/Pods/MMKVCore/Core/MiniPBCoder.h
new file mode 100644
index 000000000..15895687e
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MiniPBCoder.h
@@ -0,0 +1,129 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_MINIPBCODER_H
+#define MMKV_MINIPBCODER_H
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "KeyValueHolder.h"
+#include "MMBuffer.h"
+#include "MMBuffer.h"
+#include "MMKVLog.h"
+#include "PBUtility.h"
+#include
+
+namespace mmkv {
+
+class CodedInputData;
+class CodedOutputData;
+class AESCrypt;
+class CodedInputDataCrypt;
+struct PBEncodeItem;
+
+class MiniPBCoder {
+ const MMBuffer *m_inputBuffer = nullptr;
+ CodedInputData *m_inputData = nullptr;
+ CodedInputDataCrypt *m_inputDataDecrpt = nullptr;
+
+ MMBuffer *m_outputBuffer = nullptr;
+ CodedOutputData *m_outputData = nullptr;
+ std::vector *m_encodeItems = nullptr;
+
+ MiniPBCoder();
+ explicit MiniPBCoder(const MMBuffer *inputBuffer, AESCrypt *crypter = nullptr);
+ ~MiniPBCoder();
+
+ void writeRootObject();
+
+ size_t prepareObjectForEncode(const MMKVVector &vec);
+ size_t prepareObjectForEncode(const MMBuffer &buffer);
+
+ template
+ MMBuffer getEncodeData(const T &obj) {
+ size_t index = prepareObjectForEncode(obj);
+ return writePreparedItems(index);
+ }
+
+ MMBuffer writePreparedItems(size_t index);
+
+ void decodeOneMap(MMKVMap &dic, size_t position, bool greedy);
+#ifndef MMKV_DISABLE_CRYPT
+ void decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy);
+#endif
+
+#ifndef MMKV_APPLE
+ size_t prepareObjectForEncode(const std::string &str);
+ size_t prepareObjectForEncode(const std::vector &vector);
+
+ std::vector decodeOneVector();
+#else
+ // NSString, NSData, NSDate
+ size_t prepareObjectForEncode(__unsafe_unretained NSObject *obj);
+#endif
+
+public:
+ template
+ static MMBuffer encodeDataWithObject(const T &obj) {
+ try {
+ MiniPBCoder pbcoder;
+ return pbcoder.getEncodeData(obj);
+ } catch (const std::exception &exception) {
+ MMKVError("%s", exception.what());
+ return MMBuffer();
+ }
+ }
+
+ // opt encoding a single MMBuffer
+ static MMBuffer encodeDataWithObject(const MMBuffer &obj);
+
+ // return empty result if there's any error
+ static void decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position = 0);
+
+ // decode as much data as possible before any error happens
+ static void greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t position = 0);
+
+#ifndef MMKV_DISABLE_CRYPT
+ // return empty result if there's any error
+ static void decodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position = 0);
+
+ // decode as much data as possible before any error happens
+ static void greedyDecodeMap(MMKVMapCrypt &dic, const MMBuffer &oData, AESCrypt *crypter, size_t position = 0);
+#endif // MMKV_DISABLE_CRYPT
+
+#ifndef MMKV_APPLE
+ static std::vector decodeVector(const MMBuffer &oData);
+#else
+ // NSString, NSData, NSDate
+ static NSObject *decodeObject(const MMBuffer &oData, Class cls);
+
+ static bool isCompatibleClass(Class cls);
+#endif
+
+ // just forbid it for possibly misuse
+ explicit MiniPBCoder(const MiniPBCoder &other) = delete;
+ MiniPBCoder &operator=(const MiniPBCoder &other) = delete;
+};
+
+} // namespace mmkv
+
+#endif
+#endif //MMKV_MINIPBCODER_H
diff --git a/ios/Pods/MMKVCore/Core/MiniPBCoder_OSX.cpp b/ios/Pods/MMKVCore/Core/MiniPBCoder_OSX.cpp
new file mode 100644
index 000000000..20d55b007
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/MiniPBCoder_OSX.cpp
@@ -0,0 +1,218 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#include "MiniPBCoder.h"
+
+#ifdef MMKV_APPLE
+
+# include "CodedInputData.h"
+# include "CodedInputDataCrypt.h"
+# include "CodedOutputData.h"
+# include "MMBuffer.h"
+# include "PBEncodeItem.hpp"
+# include "PBUtility.h"
+# include
+# include
+
+# if __has_feature(objc_arc)
+# error This file must be compiled with MRC. Use -fno-objc-arc flag.
+# endif
+
+using namespace std;
+
+namespace mmkv {
+
+size_t MiniPBCoder::prepareObjectForEncode(__unsafe_unretained NSObject *obj) {
+ if (!obj) {
+ return m_encodeItems->size();
+ }
+ m_encodeItems->push_back(PBEncodeItem());
+ PBEncodeItem *encodeItem = &(m_encodeItems->back());
+ size_t index = m_encodeItems->size() - 1;
+
+ if ([obj isKindOfClass:[NSString class]]) {
+ NSString *str = (NSString *) obj;
+ encodeItem->type = PBEncodeItemType_NSString;
+ NSData *buffer = [[str dataUsingEncoding:NSUTF8StringEncoding] retain];
+ encodeItem->value.tmpObjectValue = (__bridge void *) buffer;
+ encodeItem->valueSize = static_cast(buffer.length);
+ } else if ([obj isKindOfClass:[NSDate class]]) {
+ NSDate *oDate = (NSDate *) obj;
+ encodeItem->type = PBEncodeItemType_NSDate;
+ encodeItem->value.objectValue = (__bridge void *) oDate;
+ encodeItem->valueSize = pbDoubleSize();
+ encodeItem->compiledSize = encodeItem->valueSize;
+ return index; // double has fixed compilesize
+ } else if ([obj isKindOfClass:[NSData class]]) {
+ NSData *oData = (NSData *) obj;
+ encodeItem->type = PBEncodeItemType_NSData;
+ encodeItem->value.objectValue = (__bridge void *) oData;
+ encodeItem->valueSize = static_cast(oData.length);
+ } else {
+ m_encodeItems->pop_back();
+ MMKVError("%@ not recognized", NSStringFromClass(obj.class));
+ return m_encodeItems->size();
+ }
+ encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize;
+
+ return index;
+}
+
+void MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMap &dictionary) {
+ if (position) {
+ m_inputData->seek(position);
+ } else {
+ m_inputData->readInt32();
+ }
+ while (!m_inputData->isAtEnd()) {
+ KeyValueHolder kvHolder;
+ const auto &key = m_inputData->readString(kvHolder);
+ if (key.length > 0) {
+ m_inputData->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ [key retain];
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ [itr->first release];
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMap tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ for (auto &pair : tmpDic) {
+ [pair.first release];
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# ifndef MMKV_DISABLE_CRYPT
+
+void MiniPBCoder::decodeOneMap(MMKVMapCrypt &dic, size_t position, bool greedy) {
+ auto block = [position, this](MMKVMapCrypt &dictionary) {
+ if (position) {
+ m_inputDataDecrpt->seek(position);
+ } else {
+ m_inputDataDecrpt->readInt32();
+ }
+ while (!m_inputDataDecrpt->isAtEnd()) {
+ KeyValueHolderCrypt kvHolder;
+ const auto &key = m_inputDataDecrpt->readString(kvHolder);
+ if (key.length > 0) {
+ m_inputDataDecrpt->readData(kvHolder);
+ if (kvHolder.valueSize > 0) {
+ dictionary[key] = move(kvHolder);
+ [key retain];
+ } else {
+ auto itr = dictionary.find(key);
+ if (itr != dictionary.end()) {
+ dictionary.erase(itr);
+ [itr->first release];
+ }
+ }
+ }
+ }
+ };
+
+ if (greedy) {
+ try {
+ block(dic);
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ } else {
+ try {
+ MMKVMapCrypt tmpDic;
+ block(tmpDic);
+ dic.swap(tmpDic);
+ for (auto &pair : tmpDic) {
+ [pair.first release];
+ }
+ } catch (std::exception &exception) {
+ MMKVError("%s", exception.what());
+ }
+ }
+}
+
+# endif // MMKV_DISABLE_CRYPT
+
+NSObject *MiniPBCoder::decodeObject(const MMBuffer &oData, Class cls) {
+ if (!cls || oData.length() == 0) {
+ return nil;
+ }
+ CodedInputData input(oData.getPtr(), oData.length());
+ if (cls == [NSString class]) {
+ return input.readString();
+ } else if (cls == [NSMutableString class]) {
+ return [NSMutableString stringWithString:input.readString()];
+ } else if (cls == [NSData class]) {
+ return input.readNSData();
+ } else if (cls == [NSMutableData class]) {
+ return [NSMutableData dataWithData:input.readNSData()];
+ } else if (cls == [NSDate class]) {
+ return [NSDate dateWithTimeIntervalSince1970:input.readDouble()];
+ } else {
+ MMKVError("%@ not recognized", NSStringFromClass(cls));
+ }
+
+ return nil;
+}
+
+bool MiniPBCoder::isCompatibleClass(Class cls) {
+ if (cls == [NSString class]) {
+ return true;
+ }
+ if (cls == [NSMutableString class]) {
+ return true;
+ }
+ if (cls == [NSData class]) {
+ return true;
+ }
+ if (cls == [NSMutableData class]) {
+ return true;
+ }
+ if (cls == [NSDate class]) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace mmkv
+
+#endif // MMKV_APPLE
diff --git a/ios/Pods/MMKVCore/Core/PBEncodeItem.hpp b/ios/Pods/MMKVCore/Core/PBEncodeItem.hpp
new file mode 100644
index 000000000..f5a1a3451
--- /dev/null
+++ b/ios/Pods/MMKVCore/Core/PBEncodeItem.hpp
@@ -0,0 +1,86 @@
+/*
+ * Tencent is pleased to support the open source community by making
+ * MMKV available.
+ *
+ * Copyright (C) 2018 THL A29 Limited, a Tencent company.
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef MMKV_PBENCODEITEM_HPP
+#define MMKV_PBENCODEITEM_HPP
+#ifdef __cplusplus
+
+#include "MMKVPredef.h"
+
+#include "MMBuffer.h"
+#include