Skip to content

Commit

Permalink
chore: [IA-842] Integrate React Native Vision Camera to scan QR Codes (
Browse files Browse the repository at this point in the history
…pagopa#3928)

* Upgrading bottomsheet library and hooks

* upgrade new bottomsheet component

* update bancomatInformationBottomSheet

* update OptOutBottomSheet

* update OtpNotWorking

* wip updating useChangeActivationConfirmationBottomSheet

* Upgrades useBottomSheetMethodsToDelete

* Upgrades UnsubscribeToBpd

* Upgrades useDownloadAttachmentConfirmationBottomSheet and tests

* Upgrades useDownloadAttachmentConfirmationBottomSheet usage

* Upgrades useDownloadAttachmentConfirmationBottomSheet usage on tests

* downgrades react-native-gesture-handler to avoid warning

* upgrades VoucherDetailsScreen bottomsheet

* fixes on tests and setup and removes old hooks

* final upgrade

* prettifies

* fixes BpdToggles bottomSheets

* prettify

* Fixes InnerBpdPaymentMethodCapability

* fixes test execution

* fixes

* fixes mocks and tests

* prettify

* Fixes podfile

* fixes and updates

* patches bottom sheet library for accessibility

* Updates failing snapshot

* Created the `BarcodeCamera` component

* Integrated the barcode scanner

* Fixes android configuration

* Now building on android

* Restore gradle settings

* Camera is now working on Android

* Restore the old kotlin version param

* Fix dependencies versions

* Upgrade `yarn.lock`

* Restore `Podfile`

* Improve `BarcodeCamera` component to handle a scan callback

* Handle permissions in `BarcodeCamera`

* Wire the old QRCode logic with the new `BarcodeCamera` component

* Restore QRCode screen footer

* Remove Data Matrix support in `useScanBarcodes`

* Restore android permissions alert

* Remove `BottomSheetModalProvider` from `AddCardScreen` test

* Restore the scanning marker

* Restore settings link on `denied` permission

* Render `props.marker` only when it does exist

* Add inline styles to the `styles` object

* Disable audio from camera

* Restore invalid QR Code timeout

* Remove `react-native-qrcode-scanner` from the dependencies

* Removed `react-native-qrcode-scanner` patch

* Re-introduce `react-native-permissions`

* Fix `Podfile` RNPermissions directory

* Removed `react-native-permissions` from the core

* Remove another RNPersmissionPackage reference from `MainApplication.java`

* Add scan haptic feedback

* Fix `useEffect` leak while disabled

* Fix multiple barcode scan on low-end devices

* Use IO typography components

* Move the haptic feedback to the external `ScanQrCodeScreen`

* Add `onInvalidQrCode` scanning state check to avoid flickering

Co-authored-by: CrisTofani <[email protected]>
Co-authored-by: fabriziofff <[email protected]>
Co-authored-by: Matteo Boschi <[email protected]>
  • Loading branch information
4 people authored May 11, 2022
1 parent dadb556 commit f5508c8
Show file tree
Hide file tree
Showing 19 changed files with 404 additions and 160 deletions.
1 change: 0 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ dependencies {
implementation project(':jail-monkey')
implementation project(':react-native-linear-gradient')
implementation project(':react-native-share')
implementation project(':react-native-permissions')
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
Expand Down
7 changes: 5 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<!-- Required by react-native-vision-camera -->
<uses-permission android:name="android.permission.CAMERA" />

<!-- Required by react-native-fingerprint-scanner -->
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
Expand All @@ -62,7 +65,7 @@

<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
Expand Down Expand Up @@ -125,7 +128,7 @@
android:theme="@style/ZendeskTheme" />
<!-- END Zendesk -->

<activity android:name=".MainActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:windowSoftInputMode="adjustResize">
<activity android:name=".MainActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:windowSoftInputMode="adjustResize" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import it.ipzs.cieidsdk.native_bridge.CiePackage;
import com.reactnativecommunity.rnpermissions.RNPermissionsPackage;
import com.reactnativecommunity.art.ARTPackage;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
Expand Down Expand Up @@ -42,7 +41,6 @@ protected JSIModulePackage getJSIModulePackage() {
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new RNPermissionsPackage());
packages.add(new CiePackage());
packages.add(new ARTPackage());
return packages;
Expand Down
10 changes: 5 additions & 5 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
buildscript {
ext {
firebaseVersion = "17.6.0"
kotlin_version = '1.4.10'
buildToolsVersion = "29.0.3"
kotlin_version = "1.5.21"
buildToolsVersion = "30.0.0"
minSdkVersion = 21
compileSdkVersion = 30
targetSdkVersion = 30
compileSdkVersion = 31
targetSdkVersion = 31
supportLibVersion = "28.0.0"
ndkVersion = "20.1.5948944"
}
Expand All @@ -16,7 +16,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:4.2.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

Expand Down
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 0 additions & 2 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ include ':react-native-community-netinfo'
project(':react-native-community-netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-screen-brightness'
project(':react-native-screen-brightness').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screen-brightness/android')
include ':react-native-permissions'
project(':react-native-permissions').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-permissions/android')
include ':app'
include ':@react-native-community_datetimepicker'
project(':@react-native-community_datetimepicker').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/datetimepicker/android')
Expand Down
10 changes: 9 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
module.exports = {
plugins: ["react-native-reanimated/plugin", "macros"],
plugins: [
"macros",
[
"react-native-reanimated/plugin",
{
globals: ["__scanCodes"]
}
]
],
presets: ["module:metro-react-native-babel-preset"]
};
6 changes: 3 additions & 3 deletions ios/ItaliaApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@
);
HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = ItaliaAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
Expand Down Expand Up @@ -875,7 +875,7 @@
PROVISIONING_PROFILE_SPECIFIER = "match Development it.pagopa.app.io";
SWIFT_OBJC_BRIDGING_HEADER = "ItaliaApp-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 5.2;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
Expand Down Expand Up @@ -915,7 +915,7 @@
PROVISIONING_PROFILE = "0848c226-68ec-4314-ad27-50803db95257";
PROVISIONING_PROFILE_SPECIFIER = "match AppStore it.pagopa.app.io";
SWIFT_OBJC_BRIDGING_HEADER = "ItaliaApp-Bridging-Header.h";
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 5.2;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<dict>
<key>BuildSystemType</key>
<string>Latest</string>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
14 changes: 1 addition & 13 deletions ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '10.0'
platform :ios, '11.0'

target 'ItaliaApp' do
config = use_native_modules!

permissions_path = '../node_modules/react-native-permissions/ios'

pod 'RNPermissions', :path => '../node_modules/react-native-permissions/'
pod 'Permission-Calendars', :path => "#{permissions_path}/Calendars.podspec"
pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
pod 'Permission-Contacts', :path => "#{permissions_path}/Contacts.podspec"
pod 'Permission-FaceID', :path => "#{permissions_path}/FaceID.podspec"
pod 'Permission-MediaLibrary', :path => "#{permissions_path}/MediaLibrary.podspec"
pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec"
pod 'Permission-PhotoLibrary', :path => "#{permissions_path}/PhotoLibrary.podspec"
pod 'Permission-Reminders', :path => "#{permissions_path}/Reminders.podspec"

pod 'RNCPushNotificationIOS', :path => '../node_modules/@react-native-community/push-notification-ios'
pod 'ReactNativeART', :podspec => '../node_modules/@react-native-community/art/ReactNativeART.podspec'
use_react_native!(
Expand Down
108 changes: 103 additions & 5 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,66 @@ PODS:
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- glog (0.3.5)
- GoogleDataTransport (9.1.2):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleMLKit/BarcodeScanning (2.6.0):
- GoogleMLKit/MLKitCore
- MLKitBarcodeScanning (~> 1.7.0)
- GoogleMLKit/MLKitCore (2.6.0):
- MLKitCommon (~> 5.0.0)
- GoogleToolboxForMac/DebugUtils (2.3.2):
- GoogleToolboxForMac/Defines (= 2.3.2)
- GoogleToolboxForMac/Defines (2.3.2)
- GoogleToolboxForMac/Logger (2.3.2):
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSData+zlib (2.3.2)":
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.2)":
- GoogleToolboxForMac/DebugUtils (= 2.3.2)
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)"
- "GoogleToolboxForMac/NSString+URLArguments (2.3.2)"
- GoogleUtilities/Environment (7.7.0):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.7.0):
- GoogleUtilities/Environment
- GoogleUtilities/UserDefaults (7.7.0):
- GoogleUtilities/Logger
- GoogleUtilitiesComponents (1.1.0):
- GoogleUtilities/Logger
- GTMSessionFetcher/Core (1.7.1)
- hermes-engine (0.7.2)
- jail-monkey (2.3.2):
- React
- libevent (2.1.12)
- Mixpanel (3.6.1)
- MLImage (1.0.0-beta2)
- MLKitBarcodeScanning (1.7.0):
- MLKitCommon (~> 5.0)
- MLKitVision (~> 3.0)
- MLKitCommon (5.0.0):
- GoogleDataTransport (~> 9.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
- GoogleUtilities/UserDefaults (~> 7.0)
- GoogleUtilitiesComponents (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
- Protobuf (~> 3.12)
- MLKitVision (3.0.0):
- GoogleToolboxForMac/Logger (~> 2.1)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- GTMSessionFetcher/Core (~> 1.1)
- MLImage (= 1.0.0-beta2)
- MLKitCommon (~> 5.0)
- Protobuf (~> 3.12)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- OpenSSL-Universal (1.1.180)
- Permission-Calendars (2.2.2):
- RNPermissions
Expand All @@ -81,6 +136,8 @@ PODS:
- RNPermissions
- Permission-Reminders (2.2.2):
- RNPermissions
- PromisesObjC (2.1.0)
- Protobuf (3.20.0)
- QrCode (1.1.0):
- React
- RCT-Folly (2020.01.13.00):
Expand Down Expand Up @@ -305,9 +362,9 @@ PODS:
- React-Core
- react-native-cameraroll (4.0.0):
- React
- react-native-config (1.4.3):
- react-native-config/App (= 1.4.3)
- react-native-config/App (1.4.3):
- react-native-config (1.4.5):
- react-native-config/App (= 1.4.5)
- react-native-config/App (1.4.5):
- React-Core
- react-native-cookies (4.0.1):
- React
Expand Down Expand Up @@ -469,6 +526,13 @@ PODS:
- ZendeskChatSDK (~> 2.11.1)
- ZendeskMessagingAPISDK (~> 3.8.2)
- ZendeskSupportSDK (~> 5.3.0)
- vision-camera-code-scanner (0.2.0):
- GoogleMLKit/BarcodeScanning
- React-Core
- VisionCamera (2.13.0):
- React
- React-callinvoker
- React-Core
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
Expand Down Expand Up @@ -596,6 +660,8 @@ DEPENDENCIES:
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNZendeskChat (from `../node_modules/io-react-native-zendesk`)
- vision-camera-code-scanner (from `../node_modules/vision-camera-code-scanner`)
- VisionCamera (from `../node_modules/react-native-vision-camera`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
Expand All @@ -609,10 +675,23 @@ SPEC REPOS:
- Flipper-PeerTalk
- Flipper-RSocket
- FlipperKit
- GoogleDataTransport
- GoogleMLKit
- GoogleToolboxForMac
- GoogleUtilities
- GoogleUtilitiesComponents
- GTMSessionFetcher
- hermes-engine
- libevent
- Mixpanel
- MLImage
- MLKitBarcodeScanning
- MLKitCommon
- MLKitVision
- nanopb
- OpenSSL-Universal
- PromisesObjC
- Protobuf
- YogaKit
- ZendeskAnswerBotProvidersSDK
- ZendeskAnswerBotSDK
Expand Down Expand Up @@ -779,6 +858,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-vector-icons"
RNZendeskChat:
:path: "../node_modules/io-react-native-zendesk"
vision-camera-code-scanner:
:path: "../node_modules/vision-camera-code-scanner"
VisionCamera:
:path: "../node_modules/react-native-vision-camera"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"

Expand All @@ -797,10 +880,21 @@ SPEC CHECKSUMS:
Flipper-RSocket: 127954abe8b162fcaf68d2134d34dc2bd7076154
FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
GoogleMLKit: 755661c46990a85e42278015f26400286d98ad95
GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe
GTMSessionFetcher: 4577a4cc914a5a07c40a8a0ad0acc22080418c2d
hermes-engine: 7d97ba46a1e29bacf3e3c61ecb2804a5ddd02d4f
jail-monkey: d7c5048b2336f22ee9c9e0efa145f1f917338ea9
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
Mixpanel: 61e6d8c0717c8e94ccc6d3a1ae8677b9a78f64c5
MLImage: a454f9f8ecfd537783a12f9488f5be1a68820829
MLKitBarcodeScanning: b8257854f6afc1c8443d61ec6b98c28b35625df6
MLKitCommon: 3bc17c6f7d25ce3660f030350b46ae7ec9ebca6e
MLKitVision: e87dc3f2e456a6ab32361ebd985e078dd2746143
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
Permission-Calendars: 17bb6a871acc7580754be7b56cfb25537e1b57de
Permission-Camera: 5d2aaf95592660b6dcb5e8d1d90ed5d0156cad02
Expand All @@ -810,6 +904,8 @@ SPEC CHECKSUMS:
Permission-Notifications: 9c6b5cc4f0e6599e9fc3395b77cebddc48f1be41
Permission-PhotoLibrary: 8227a6ed9f6a971537afe63742d54f5f23a38fe2
Permission-Reminders: f1bddb60953645830289f9b00977b11e2b62fbf8
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
Protobuf: 66e2f3b26a35e4cc379831f4ced538274ee4b923
QrCode: 1212ff8b8fae1a0e2c698d1c42560abcc0c9019b
RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
RCTRequired: 6d3e854f0e7260a648badd0d44fc364bc9da9728
Expand All @@ -826,7 +922,7 @@ SPEC CHECKSUMS:
react-native-blob-util: a5d3561045ed98cfb2fb80cbbff600fae0e8edee
react-native-camera: b8cc03e2feec0c04403d0998e37cf519d8fd4c6f
react-native-cameraroll: ae0a7c0cc8462508855707ff623b1e789b692865
react-native-config: 387b1ea507bc50a2059e69149dac1342b9532f58
react-native-config: 6502b1879f97ed5ac570a029961fc35ea606cd14
react-native-cookies: ff2e6865dff2e5feeca8f1ed082ae7898e4fa912
react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe
react-native-flipper: 169e8ba429b73ad637ce007337ce4b415e783799
Expand Down Expand Up @@ -872,6 +968,8 @@ SPEC CHECKSUMS:
RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8
RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976
RNZendeskChat: d22f866bc9cf8b426d5fa9d773a4a280889cc6c8
vision-camera-code-scanner: dda884a7f3ec8243a2a6d6489b91860648371bca
VisionCamera: 57487a11a98a0d5d3ed73946062ab3920110f08e
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
ZendeskAnswerBotProvidersSDK: 8ef75d4484f09c324cd93ce2725364d73dbabcf5
Expand All @@ -886,6 +984,6 @@ SPEC CHECKSUMS:
ZendeskSupportProvidersSDK: 2bdf8544f7cd0fd4c002546f5704b813845beb2a
ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba

PODFILE CHECKSUM: cfe9cf6887cc0c4a2fe426f861721f73b4fcf161
PODFILE CHECKSUM: 54d3b578042f3349aeeab78cd456ba1424f4b858

COCOAPODS: 1.11.3
3 changes: 1 addition & 2 deletions jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Set up of the testing environment
*/

import {NativeModules} from "react-native";
import { NativeModules } from "react-native";
import mockAsyncStorage from "@react-native-community/async-storage/jest/async-storage-mock";
import mockClipboard from "@react-native-clipboard/clipboard/jest/clipboard-mock.js";
import nodeFetch from "node-fetch";
Expand All @@ -22,7 +22,6 @@ NativeModules.RNGestureHandlerModule = {

jest.mock("@react-native-community/async-storage", () => mockAsyncStorage);
jest.mock("@react-native-community/push-notification-ios", jest.fn());
jest.mock("react-native-permissions", jest.fn());
jest.mock("@react-native-community/cookies", jest.fn());
jest.mock("react-native-share", () => jest.fn());
jest.mock("@react-native-clipboard/clipboard", () => mockClipboard);
Expand Down
Loading

0 comments on commit f5508c8

Please sign in to comment.