Browse Source

Updated react native to 62.2

Arnaud Vergnet 1 year ago
parent
commit
d18b812e53

+ 23
- 3
android/app/build.gradle View File

@@ -77,7 +77,7 @@ import com.android.build.OutputFile
77 77
 
78 78
 project.ext.react = [
79 79
     entryFile: "index.js",
80
-    enableHermes: false,
80
+    enableHermes: true,
81 81
 ]
82 82
 
83 83
 apply from: "../../node_modules/react-native/react.gradle"
@@ -136,8 +136,8 @@ android {
136 136
         applicationId 'fr.amicaleinsat.application'
137 137
         minSdkVersion rootProject.ext.minSdkVersion
138 138
         targetSdkVersion rootProject.ext.targetSdkVersion
139
-        versionCode 19
140
-        versionName "3.0.2"
139
+        versionCode 20
140
+        versionName "3.0.3"
141 141
         missingDimensionStrategy 'react-native-camera', 'general'
142 142
     }
143 143
     splits {
@@ -176,6 +176,14 @@ android {
176 176
             proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
177 177
         }
178 178
     }
179
+
180
+    packagingOptions {
181
+        pickFirst "lib/armeabi-v7a/libc++_shared.so"
182
+        pickFirst "lib/arm64-v8a/libc++_shared.so"
183
+        pickFirst "lib/x86/libc++_shared.so"
184
+        pickFirst "lib/x86_64/libc++_shared.so"
185
+    }
186
+
179 187
     // applicationVariants are e.g. debug, release
180 188
     applicationVariants.all { variant ->
181 189
         variant.outputs.each { output ->
@@ -194,8 +202,20 @@ android {
194 202
 
195 203
 dependencies {
196 204
     implementation fileTree(dir: "libs", include: ["*.jar"])
205
+    //noinspection GradleDynamicVersion
197 206
     implementation "com.facebook.react:react-native:+"  // From node_modules
198 207
 
208
+    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
209
+    debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
210
+      exclude group:'com.facebook.fbjni'
211
+    }
212
+    debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
213
+        exclude group:'com.facebook.flipper'
214
+    }
215
+    debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
216
+        exclude group:'com.facebook.flipper'
217
+    }
218
+
199 219
     if (enableHermes) {
200 220
         def hermesPath = "../../node_modules/hermes-engine/android/";
201 221
         debugImplementation files(hermesPath + "hermes-debug.aar")

+ 67
- 0
android/app/src/debug/java/fr/amicaleinsat/application/ReactNativeFlipper.java View File

@@ -0,0 +1,67 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * <p>This source code is licensed under the MIT license found in the LICENSE file in the root
5
+ * directory of this source tree.
6
+ */
7
+package com.rndiffapp;
8
+import android.content.Context;
9
+import com.facebook.flipper.android.AndroidFlipperClient;
10
+import com.facebook.flipper.android.utils.FlipperUtils;
11
+import com.facebook.flipper.core.FlipperClient;
12
+import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
13
+import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
14
+import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
15
+import com.facebook.flipper.plugins.inspector.DescriptorMapping;
16
+import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
17
+import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
18
+import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
19
+import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
20
+import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21
+import com.facebook.react.ReactInstanceManager;
22
+import com.facebook.react.bridge.ReactContext;
23
+import com.facebook.react.modules.network.NetworkingModule;
24
+import okhttp3.OkHttpClient;
25
+public class ReactNativeFlipper {
26
+  public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
27
+    if (FlipperUtils.shouldEnableFlipper(context)) {
28
+      final FlipperClient client = AndroidFlipperClient.getInstance(context);
29
+      client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
30
+      client.addPlugin(new ReactFlipperPlugin());
31
+      client.addPlugin(new DatabasesFlipperPlugin(context));
32
+      client.addPlugin(new SharedPreferencesFlipperPlugin(context));
33
+      client.addPlugin(CrashReporterPlugin.getInstance());
34
+      NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
35
+      NetworkingModule.setCustomClientBuilder(
36
+          new NetworkingModule.CustomClientBuilder() {
37
+            @Override
38
+            public void apply(OkHttpClient.Builder builder) {
39
+              builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
40
+            }
41
+          });
42
+      client.addPlugin(networkFlipperPlugin);
43
+      client.start();
44
+      // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
45
+      // Hence we run if after all native modules have been initialized
46
+      ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
47
+      if (reactContext == null) {
48
+        reactInstanceManager.addReactInstanceEventListener(
49
+            new ReactInstanceManager.ReactInstanceEventListener() {
50
+              @Override
51
+              public void onReactContextInitialized(ReactContext reactContext) {
52
+                reactInstanceManager.removeReactInstanceEventListener(this);
53
+                reactContext.runOnNativeModulesQueueThread(
54
+                    new Runnable() {
55
+                      @Override
56
+                      public void run() {
57
+                        client.addPlugin(new FrescoFlipperPlugin());
58
+                      }
59
+                    });
60
+              }
61
+            });
62
+      } else {
63
+        client.addPlugin(new FrescoFlipperPlugin());
64
+      }
65
+    }
66
+  }
67
+}

+ 12
- 6
android/app/src/main/java/fr/amicaleinsat/application/MainApplication.java View File

@@ -5,6 +5,7 @@ import android.content.Context;
5 5
 
6 6
 import com.facebook.react.PackageList;
7 7
 import com.facebook.react.ReactApplication;
8
+import com.facebook.react.ReactInstanceManager;
8 9
 import com.facebook.react.ReactNativeHost;
9 10
 import com.facebook.react.ReactPackage;
10 11
 import com.facebook.react.shell.MainReactPackage;
@@ -44,23 +45,28 @@ public class MainApplication extends Application implements ReactApplication {
44 45
   public void onCreate() {
45 46
     super.onCreate();
46 47
     SoLoader.init(this, /* native exopackage */ false);
47
-    initializeFlipper(this); // Remove this line if you don't want Flipper enabled
48
+    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
48 49
   }
49 50
 
50
-  /**
51
-   * Loads Flipper in React Native templates.
51
+ /**
52
+   * Loads Flipper in React Native templates. Call this in the onCreate method with something like
53
+   * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
52 54
    *
53 55
    * @param context
56
+   * @param reactInstanceManager
54 57
    */
55
-  private static void initializeFlipper(Context context) {
58
+  private static void initializeFlipper(
59
+      Context context, ReactInstanceManager reactInstanceManager) {
56 60
     if (BuildConfig.DEBUG) {
57 61
       try {
58 62
         /*
59 63
          We use reflection here to pick up the class that initializes Flipper,
60 64
         since Flipper library is not available in release mode
61 65
         */
62
-        Class<?> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
63
-        aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
66
+        Class<?> aClass = Class.forName("com.rndiffapp.ReactNativeFlipper");
67
+        aClass
68
+            .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
69
+            .invoke(null, context, reactInstanceManager);
64 70
       } catch (ClassNotFoundException e) {
65 71
         e.printStackTrace();
66 72
       } catch (NoSuchMethodException e) {

+ 2
- 2
android/build.gradle View File

@@ -12,7 +12,7 @@ buildscript {
12 12
         jcenter()
13 13
     }
14 14
     dependencies {
15
-        classpath("com.android.tools.build:gradle:3.5.3")
15
+        classpath("com.android.tools.build:gradle:3.5.2")
16 16
 
17 17
         // NOTE: Do not place your application dependencies here; they belong
18 18
         // in the individual module build.gradle files
@@ -36,6 +36,6 @@ allprojects {
36 36
         }
37 37
         google()
38 38
         jcenter()
39
-        maven { url 'https://jitpack.io' }
39
+        maven { url 'https://www.jitpack.io' }
40 40
     }
41 41
 }

+ 1
- 1
android/gradle/wrapper/gradle-wrapper.properties View File

@@ -1,5 +1,5 @@
1 1
 distributionBase=GRADLE_USER_HOME
2 2
 distributionPath=wrapper/dists
3
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-all.zip
3
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
4 4
 zipStoreBase=GRADLE_USER_HOME
5 5
 zipStorePath=wrapper/dists

+ 3
- 3
android/gradlew View File

@@ -7,7 +7,7 @@
7 7
 # you may not use this file except in compliance with the License.
8 8
 # You may obtain a copy of the License at
9 9
 #
10
-#      http://www.apache.org/licenses/LICENSE-2.0
10
+#      https://www.apache.org/licenses/LICENSE-2.0
11 11
 #
12 12
 # Unless required by applicable law or agreed to in writing, software
13 13
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -125,8 +125,8 @@ if $darwin; then
125 125
     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 126
 fi
127 127
 
128
-# For Cygwin, switch paths to Windows format before running java
129
-if $cygwin ; then
128
+# For Cygwin or MSYS, switch paths to Windows format before running java
129
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 130
     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 131
     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 132
     JAVACMD=`cygpath --unix "$JAVACMD"`

+ 2
- 38
ios/Campus.xcodeproj/xcshareddata/xcschemes/Campus.xcscheme View File

@@ -1,9 +1,9 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <Scheme
3
-   LastUpgradeVersion = "0940"
3
+   LastUpgradeVersion = "1130"
4 4
    version = "1.3">
5 5
    <BuildAction
6
-      parallelizeBuildables = "NO"
6
+      parallelizeBuildables = "YES"
7 7
       buildImplicitDependencies = "YES">
8 8
       <BuildActionEntries>
9 9
          <BuildActionEntry
@@ -14,40 +14,12 @@
14 14
             buildForAnalyzing = "YES">
15 15
             <BuildableReference
16 16
                BuildableIdentifier = "primary"
17
-               BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
18
-               BuildableName = "libReact.a"
19
-               BlueprintName = "React"
20
-               ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
21
-            </BuildableReference>
22
-         </BuildActionEntry>
23
-         <BuildActionEntry
24
-            buildForTesting = "YES"
25
-            buildForRunning = "YES"
26
-            buildForProfiling = "YES"
27
-            buildForArchiving = "YES"
28
-            buildForAnalyzing = "YES">
29
-            <BuildableReference
30
-               BuildableIdentifier = "primary"
31 17
                BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
32 18
                BuildableName = "application.app"
33 19
                BlueprintName = "Campus"
34 20
                ReferencedContainer = "container:Campus.xcodeproj">
35 21
             </BuildableReference>
36 22
          </BuildActionEntry>
37
-         <BuildActionEntry
38
-            buildForTesting = "YES"
39
-            buildForRunning = "YES"
40
-            buildForProfiling = "NO"
41
-            buildForArchiving = "NO"
42
-            buildForAnalyzing = "YES">
43
-            <BuildableReference
44
-               BuildableIdentifier = "primary"
45
-               BlueprintIdentifier = "00E356ED1AD99517003FC87E"
46
-               BuildableName = "CampusTests.xctest"
47
-               BlueprintName = "CampusTests"
48
-               ReferencedContainer = "container:Campus.xcodeproj">
49
-            </BuildableReference>
50
-         </BuildActionEntry>
51 23
       </BuildActionEntries>
52 24
    </BuildAction>
53 25
    <TestAction
@@ -55,14 +27,6 @@
55 27
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
56 28
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
57 29
       shouldUseLaunchSchemeArgsEnv = "YES">
58
-      <MacroExpansion>
59
-         <BuildableReference
60
-            BuildableIdentifier = "primary"
61
-            BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
62
-            BuildableName = "application.app"
63
-            BlueprintName = "Campus"
64
-            ReferencedContainer = "container:Campus.xcodeproj">
65
-         </BuildableReference>
66 30
       </MacroExpansion>
67 31
       <Testables>
68 32
          <TestableReference

+ 22
- 0
ios/Campus/AppDelegate.m View File

@@ -26,9 +26,31 @@
26 26
   return [RCTLinkingManager application:application openURL:url options:options];
27 27
 }
28 28
 
29
+#if DEBUG
30
+#import <FlipperKit/FlipperClient.h>
31
+#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
32
+#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
33
+#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
34
+#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
35
+#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
36
+static void InitializeFlipper(UIApplication *application) {
37
+  FlipperClient *client = [FlipperClient sharedClient];
38
+  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
39
+  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
40
+  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
41
+  [client addPlugin:[FlipperKitReactPlugin new]];
42
+  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
43
+  [client start];
44
+}
45
+#endif
46
+
29 47
 
30 48
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
31 49
 {
50
+  #if DEBUG
51
+    InitializeFlipper(application);
52
+  #endif
53
+
32 54
   RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
33 55
   RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
34 56
   rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

+ 55
- 5
ios/Podfile View File

@@ -2,10 +2,51 @@ platform :ios, '9.0'
2 2
 
3 3
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
4 4
 
5
+def add_flipper_pods!(versions = {})
6
+  versions['Flipper'] ||= '~> 0.33.1'
7
+  versions['DoubleConversion'] ||= '1.1.7'
8
+  versions['Flipper-Folly'] ||= '~> 2.1'
9
+  versions['Flipper-Glog'] ||= '0.3.6'
10
+  versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
11
+  versions['Flipper-RSocket'] ||= '~> 1.0'
12
+  pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
13
+  pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
14
+  pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
15
+  pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
16
+  pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
17
+  # List all transitive dependencies for FlipperKit pods
18
+  # to avoid them being linked in Release builds
19
+  pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
20
+  pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
21
+  pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
22
+  pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
23
+  pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
24
+  pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
25
+  pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
26
+  pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
27
+  pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
28
+  pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
29
+  pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
30
+  pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
31
+  pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
32
+  pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
33
+end
34
+# Post Install processing for Flipper
35
+def flipper_post_install(installer)
36
+  installer.pods_project.targets.each do |target|
37
+    if target.name == 'YogaKit'
38
+      target.build_configurations.each do |config|
39
+        config.build_settings['SWIFT_VERSION'] = '4.1'
40
+      end
41
+    end
42
+  end
43
+end
44
+
45
+
46
+
5 47
 target 'Campus' do
48
+  # Pods for Campus
6 49
   rnPrefix = "../node_modules/react-native"
7
-
8
-  # React Native and its dependencies
9 50
   pod 'FBLazyVector', :path => "#{rnPrefix}/Libraries/FBLazyVector"
10 51
   pod 'FBReactNativeSpec', :path => "#{rnPrefix}/Libraries/FBReactNativeSpec"
11 52
   pod 'RCTRequired', :path => "#{rnPrefix}/Libraries/RCTRequired"
@@ -28,14 +69,14 @@ target 'Campus' do
28 69
   pod 'React-jsi', :path => "#{rnPrefix}/ReactCommon/jsi"
29 70
   pod 'React-jsiexecutor', :path => "#{rnPrefix}/ReactCommon/jsiexecutor"
30 71
   pod 'React-jsinspector', :path => "#{rnPrefix}/ReactCommon/jsinspector"
31
-  pod 'ReactCommon/jscallinvoker', :path => "#{rnPrefix}/ReactCommon"
72
+  pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
32 73
   pod 'ReactCommon/turbomodule/core', :path => "#{rnPrefix}/ReactCommon"
33
-  pod 'Yoga', :path => "#{rnPrefix}/ReactCommon/yoga"
74
+  pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
75
+
34 76
   pod 'DoubleConversion', :podspec => "#{rnPrefix}/third-party-podspecs/DoubleConversion.podspec"
35 77
   pod 'glog', :podspec => "#{rnPrefix}/third-party-podspecs/glog.podspec"
36 78
   pod 'Folly', :podspec => "#{rnPrefix}/third-party-podspecs/Folly.podspec"
37 79
 
38
-  # Other native modules
39 80
 
40 81
   # react-native-cli autolinking
41 82
   use_native_modules!
@@ -46,4 +87,13 @@ target 'Campus' do
46 87
   pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec"
47 88
   pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
48 89
 
90
+  # Enables Flipper.
91
+  #
92
+  # Note that if you have use_frameworks! enabled, Flipper will not work and
93
+  # you should disable these next few lines.
94
+  add_flipper_pods!
95
+  post_install do |installer|
96
+    flipper_post_install(installer)
97
+  end
98
+
49 99
 end

+ 4
- 4
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "campus",
3
-  "version": "3.0.2",
3
+  "version": "3.0.3",
4 4
   "private": true,
5 5
   "scripts": {
6 6
     "start": "react-native start",
@@ -28,9 +28,9 @@
28 28
     "@react-navigation/native": "^5.2.2",
29 29
     "@react-navigation/stack": "^5.2.17",
30 30
     "i18n-js": "^3.3.0",
31
-    "react": "~16.9.0",
31
+    "react": "16.11.0",
32 32
     "react-dom": "16.9.0",
33
-    "react-native": "~0.61.5",
33
+    "react-native": "0.62.2",
34 34
     "react-native-animatable": "^1.3.3",
35 35
     "react-native-app-intro-slider": "^4.0.0",
36 36
     "react-native-appearance": "~0.3.3",
@@ -67,6 +67,6 @@
67 67
     "jest": "^25.5.3",
68 68
     "jest-extended": "^0.11.5",
69 69
     "metro-react-native-babel-preset": "^0.59.0",
70
-    "react-test-renderer": "16.9.0"
70
+    "react-test-renderer": "16.11.0"
71 71
   }
72 72
 }

+ 0
- 2
src/components/Overrides/CustomHeaderButton.js View File

@@ -4,7 +4,6 @@ import * as React from 'react';
4 4
 import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
5 5
 import {HeaderButton, HeaderButtons} from 'react-navigation-header-buttons';
6 6
 import {withTheme} from "react-native-paper";
7
-import * as Touchable from "react-native/Libraries/Components/Touchable/TouchableNativeFeedback.android";
8 7
 
9 8
 const MaterialHeaderButton = (props: Object) =>
10 9
     <HeaderButton
@@ -12,7 +11,6 @@ const MaterialHeaderButton = (props: Object) =>
12 11
         IconComponent={MaterialCommunityIcons}
13 12
         iconSize={26}
14 13
         color={props.color != null ? props.color : props.theme.colors.text}
15
-        background={Touchable.Ripple(props.theme.colors.ripple, true)}
16 14
     />;
17 15
 
18 16
 const MaterialHeaderButtons = (props: Object) => {

Loading…
Cancel
Save