From 36e55c95a2fca7b0ac7c60494f9f71748a897e61 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 13 May 2020 16:54:07 -0400 Subject: [PATCH] MM-23570 Migrate from Segment to Rudder (#4293) * Replace Segment with Rudder (ios not working) * Update @assets reference and fix tests --- Jenkinsfile | 14 -- NOTICE.txt | 224 ++++++++++++++++-- android/build.gradle | 3 + app/actions/views/root.js | 5 +- app/components/error_list/error_list.js | 6 +- .../file_attachment_icon.js | 28 +-- .../file_attachment_image.js | 6 +- .../markdown/markdown_image/markdown_image.js | 2 +- .../markdown/markdown_link/markdown_link.js | 20 +- .../post_body_additional_content.js | 2 +- .../quick_actions/input_quick_action/index.js | 2 +- app/components/post_header/post_pre_header.js | 11 +- .../post_profile_picture.js | 12 +- .../profile_picture/profile_picture.js | 8 +- .../progressive_image/progressive_image.js | 7 +- .../reaction_picker/reaction_picker.js | 9 +- app/components/reactions/reactions.js | 11 +- .../main/channels_list/filtered_list/index.js | 4 +- app/components/vector_icon.js | 2 +- app/components/video_controls.js | 8 +- app/i18n/index.js | 32 +-- app/init/analytics.ts | 84 +++++++ app/init/emm_provider.js | 3 +- app/init/fetch.js | 5 +- app/init/global_event_handler.js | 29 ++- app/init/global_event_handler.test.js | 9 +- app/mattermost.js | 1 + app/mm-redux/client/client4.ts | 36 +-- app/mm-redux/utils/i18n_utils.test.js | 2 +- app/reducers/views/select_server.js | 2 +- app/screens/about/about.js | 14 +- app/screens/channel/channel.android.js | 3 +- app/screens/channel/channel.ios.js | 3 +- app/screens/channel/channel_base.js | 19 +- app/screens/channel_info/channel_info.js | 16 +- app/screens/channel_info/channel_info.test.js | 7 +- app/screens/client_upgrade/client_upgrade.js | 14 +- .../forgot_password/forgot_password.js | 12 +- app/screens/login/login.js | 26 +- app/screens/login_options/login_options.js | 20 +- app/screens/mfa/mfa.js | 19 +- app/screens/notification/notification.js | 16 +- app/screens/pinned_posts/pinned_posts.js | 25 +- app/screens/post_options/post_option.js | 23 +- app/screens/select_server/select_server.js | 31 ++- .../advanced_settings/advanced_settings.js | 18 +- app/screens/settings/general/settings.js | 17 +- app/screens/settings/theme/theme.js | 18 +- app/screens/settings/theme/theme_tile.js | 4 +- app/screens/user_profile/user_profile.js | 31 ++- app/selectors/client_upgrade.js | 3 +- app/store/initial_state.js | 2 +- app/telemetry/telemetry.android.js | 3 +- app/telemetry/telemetry_utils.js | 2 +- app/utils/client_upgrade.js | 5 +- app/utils/segment.js | 71 ------ app/utils/sentry/index.js | 2 +- assets/base/config.json | 2 +- babel.config.js | 2 +- fastlane/Fastfile | 4 +- index.js | 6 +- ios/Podfile.lock | 22 +- package-lock.json | 33 +-- package.json | 2 +- packager/moduleNames.js | 2 +- packager/modulePaths.js | 2 +- test/test_helper.js | 4 +- tsconfig.json | 3 + 68 files changed, 625 insertions(+), 468 deletions(-) delete mode 100644 Jenkinsfile create mode 100644 app/init/analytics.ts delete mode 100644 app/utils/segment.js diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 457f39a49..000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,14 +0,0 @@ -pipeline { - agent any - - stages { - stage('Test') { - steps { - echo 'assets/base/config.json' - sh 'cat assets/base/config.json' - sh 'touch .podinstall' - sh 'make test || exit 1' - } - } - } -} \ No newline at end of file diff --git a/NOTICE.txt b/NOTICE.txt index 71e2f1ff3..7137b79f1 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -183,38 +183,218 @@ SOFTWARE. --- -## analytics-react-native +## rudder-sdk-react-native -This product contains 'analytics-react-native' by Javier Alvarez. +This product contains 'rudder-sdk-react-native' by Rudder Labs -A React Native client for [Segment](https://segment.com) — The hassle-free way to integrate analytics into any application. +Rudder is an open-source Segment alternative written in Go, built for the enterprise. * HOMEPAGE: - * https://github.com/neiker/analytics-react-native + * https://github.com/rudderlabs/rudder-sdk-react-native -* LICENSE: MIT +* LICENSE: Apache-2.0 -MIT License + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Copyright (c) 2016 Javier Alvarez + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + 1. Definitions. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -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. + "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 2019 Rudderlabs + +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. --- diff --git a/android/build.gradle b/android/build.gradle index 9b1f3ad02..2076b9e7f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -63,5 +63,8 @@ allprojects { maven { url "https://www.jitpack.io" } + maven { + url ("https://dl.bintray.com/rudderstack/rudderstack") + } } } diff --git a/app/actions/views/root.js b/app/actions/views/root.js index 8825fdb33..d63d72f91 100644 --- a/app/actions/views/root.js +++ b/app/actions/views/root.js @@ -3,6 +3,8 @@ import {batchActions} from 'redux-batched-actions'; +import {NavigationTypes, ViewTypes} from '@constants'; +import {recordTime} from '@init/analytics.ts'; import {ChannelTypes, GeneralTypes, TeamTypes} from '@mm-redux/action_types'; import {fetchMyChannelsAndMembers} from '@mm-redux/actions/channels'; import {getDataRetentionPolicy} from '@mm-redux/actions/general'; @@ -11,12 +13,9 @@ import {getMyTeams, getMyTeamMembers} from '@mm-redux/actions/teams'; import {Client4} from '@mm-redux/client'; import {General} from '@mm-redux/constants'; import EventEmitter from '@mm-redux/utils/event_emitter'; - -import {NavigationTypes, ViewTypes} from '@constants'; import EphemeralStore from '@store/ephemeral_store'; import initialState from '@store/initial_state'; import {getStateForReset} from '@store/utils'; -import {recordTime} from '@utils/segment'; import {markChannelViewedAndRead} from './channel'; diff --git a/app/components/error_list/error_list.js b/app/components/error_list/error_list.js index ec3edc524..4e1377d1b 100644 --- a/app/components/error_list/error_list.js +++ b/app/components/error_list/error_list.js @@ -10,9 +10,11 @@ import { TouchableOpacity, } from 'react-native'; import Icon from 'react-native-vector-icons/FontAwesome'; -import FormattedText from 'app/components/formatted_text'; + +import Config from '@assets/config.json'; +import FormattedText from '@components/formatted_text'; + import GeneralError from './general_error'; -import Config from 'assets/config.json'; const {width: deviceWidth} = Dimensions.get('window'); diff --git a/app/components/file_attachment_list/file_attachment_icon.js b/app/components/file_attachment_list/file_attachment_icon.js index 8b9c2db4d..e54d393e7 100644 --- a/app/components/file_attachment_list/file_attachment_icon.js +++ b/app/components/file_attachment_list/file_attachment_icon.js @@ -9,22 +9,20 @@ import { StyleSheet, } from 'react-native'; +import audioIcon from '@assets/images/icons/audio.png'; +import codeIcon from '@assets/images/icons/code.png'; +import excelIcon from '@assets/images/icons/excel.png'; +import genericIcon from '@assets/images/icons/generic.png'; +import imageIcon from '@assets/images/icons/image.png'; +import patchIcon from '@assets/images/icons/patch.png'; +import pdfIcon from '@assets/images/icons/pdf.png'; +import pptIcon from '@assets/images/icons/ppt.png'; +import textIcon from '@assets/images/icons/text.png'; +import videoIcon from '@assets/images/icons/video.png'; +import wordIcon from '@assets/images/icons/word.png'; +import {ATTACHMENT_ICON_HEIGHT, ATTACHMENT_ICON_WIDTH} from '@constants/attachment'; import * as Utils from '@mm-redux/utils/file_utils'; - -import audioIcon from 'assets/images/icons/audio.png'; -import codeIcon from 'assets/images/icons/code.png'; -import excelIcon from 'assets/images/icons/excel.png'; -import genericIcon from 'assets/images/icons/generic.png'; -import imageIcon from 'assets/images/icons/image.png'; -import patchIcon from 'assets/images/icons/patch.png'; -import pdfIcon from 'assets/images/icons/pdf.png'; -import pptIcon from 'assets/images/icons/ppt.png'; -import textIcon from 'assets/images/icons/text.png'; -import videoIcon from 'assets/images/icons/video.png'; -import wordIcon from 'assets/images/icons/word.png'; - -import {ATTACHMENT_ICON_HEIGHT, ATTACHMENT_ICON_WIDTH} from 'app/constants/attachment'; -import {changeOpacity} from 'app/utils/theme'; +import {changeOpacity} from '@utils/theme'; const ICON_PATH_FROM_FILE_TYPE = { audio: audioIcon, diff --git a/app/components/file_attachment_list/file_attachment_image.js b/app/components/file_attachment_list/file_attachment_image.js index 9deccbbed..511a6588e 100644 --- a/app/components/file_attachment_list/file_attachment_image.js +++ b/app/components/file_attachment_list/file_attachment_image.js @@ -9,14 +9,12 @@ import { } from 'react-native'; import FastImage from 'react-native-fast-image'; -import {Client4} from '@mm-redux/client'; - +import brokenImageIcon from '@assets/images/icons/brokenimage.png'; import ProgressiveImage from '@components/progressive_image'; +import {Client4} from '@mm-redux/client'; import {isGif} from '@utils/file'; import {changeOpacity} from '@utils/theme'; -import brokenImageIcon from 'assets/images/icons/brokenimage.png'; - const SMALL_IMAGE_MAX_HEIGHT = 48; const SMALL_IMAGE_MAX_WIDTH = 48; diff --git a/app/components/markdown/markdown_image/markdown_image.js b/app/components/markdown/markdown_image/markdown_image.js index 71859a21f..d646a30c0 100644 --- a/app/components/markdown/markdown_image/markdown_image.js +++ b/app/components/markdown/markdown_image/markdown_image.js @@ -15,6 +15,7 @@ import { } from 'react-native'; import FastImage from 'react-native-fast-image'; +import brokenImageIcon from '@assets/images/icons/brokenimage.png'; import ImageViewPort from '@components/image_viewport'; import ProgressiveImage from '@components/progressive_image'; import FormattedText from '@components/formatted_text'; @@ -26,7 +27,6 @@ import {calculateDimensions, getViewPortWidth, isGifTooLarge, previewImageAtInde import {normalizeProtocol} from '@utils/url'; import mattermostManaged from 'app/mattermost_managed'; -import brokenImageIcon from 'assets/images/icons/brokenimage.png'; const ANDROID_MAX_HEIGHT = 4096; const ANDROID_MAX_WIDTH = 4096; diff --git a/app/components/markdown/markdown_link/markdown_link.js b/app/components/markdown/markdown_link/markdown_link.js index 122c18e3f..6e1640d4b 100644 --- a/app/components/markdown/markdown_link/markdown_link.js +++ b/app/components/markdown/markdown_link/markdown_link.js @@ -7,17 +7,17 @@ import {Alert, Clipboard, Linking, Text} from 'react-native'; import urlParse from 'url-parse'; import {intlShape} from 'react-intl'; -import CustomPropTypes from 'app/constants/custom_prop_types'; -import {DeepLinkTypes} from 'app/constants'; -import {getCurrentServerUrl} from 'app/init/credentials'; -import mattermostManaged from 'app/mattermost_managed'; -import BottomSheet from 'app/utils/bottom_sheet'; -import {preventDoubleTap} from 'app/utils/tap'; -import {matchDeepLink, normalizeProtocol} from 'app/utils/url'; -import {alertErrorWithFallback} from 'app/utils/general'; -import {t} from 'app/utils/i18n'; +import Config from '@assets/config'; +import {DeepLinkTypes} from '@constants'; +import CustomPropTypes from '@constants/custom_prop_types'; +import {getCurrentServerUrl} from '@init/credentials'; +import BottomSheet from '@utils/bottom_sheet'; +import {alertErrorWithFallback} from '@utils/general'; +import {t} from '@utils/i18n'; +import {preventDoubleTap} from '@utils/tap'; +import {matchDeepLink, normalizeProtocol} from '@utils/url'; -import Config from 'assets/config'; +import mattermostManaged from 'app/mattermost_managed'; export default class MarkdownLink extends PureComponent { static propTypes = { diff --git a/app/components/post_body_additional_content/post_body_additional_content.js b/app/components/post_body_additional_content/post_body_additional_content.js index fd25bc920..699aa9c09 100644 --- a/app/components/post_body_additional_content/post_body_additional_content.js +++ b/app/components/post_body_additional_content/post_body_additional_content.js @@ -411,7 +411,7 @@ export default class PostBodyAdditionalContent extends ImageViewPort { type={'opacity'} > diff --git a/app/components/post_draft/quick_actions/input_quick_action/index.js b/app/components/post_draft/quick_actions/input_quick_action/index.js index d75a48bcc..184d0170a 100644 --- a/app/components/post_draft/quick_actions/input_quick_action/index.js +++ b/app/components/post_draft/quick_actions/input_quick_action/index.js @@ -59,7 +59,7 @@ export default class InputQuickAction extends PureComponent { return ( ); diff --git a/app/components/post_header/post_pre_header.js b/app/components/post_header/post_pre_header.js index 1b9edcf5a..cde747105 100644 --- a/app/components/post_header/post_pre_header.js +++ b/app/components/post_header/post_pre_header.js @@ -5,12 +5,11 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Image, View} from 'react-native'; -import FormattedText from 'app/components/formatted_text'; -import {t} from 'app/utils/i18n'; -import {makeStyleSheetFromTheme} from 'app/utils/theme'; - -import flagIcon from 'assets/images/post_header/flag.png'; -import pinIcon from 'assets/images/post_header/pin.png'; +import flagIcon from '@assets/images/post_header/flag.png'; +import pinIcon from '@assets/images/post_header/pin.png'; +import FormattedText from '@components/formatted_text'; +import {t} from '@utils/i18n'; +import {makeStyleSheetFromTheme} from '@utils/theme'; export default class PostPreHeader extends PureComponent { static propTypes = { diff --git a/app/components/post_profile_picture/post_profile_picture.js b/app/components/post_profile_picture/post_profile_picture.js index 7f7e95b08..6745cbcf1 100644 --- a/app/components/post_profile_picture/post_profile_picture.js +++ b/app/components/post_profile_picture/post_profile_picture.js @@ -6,12 +6,12 @@ import PropTypes from 'prop-types'; import {Platform, StyleSheet, View} from 'react-native'; import FastImage from 'react-native-fast-image'; -import AppIcon from 'app/components/app_icon'; -import ProfilePicture from 'app/components/profile_picture'; -import TouchableWithFeedback from 'app/components/touchable_with_feedback'; -import {emptyFunction} from 'app/utils/general'; -import webhookIcon from 'assets/images/icons/webhook.jpg'; -import {ViewTypes} from 'app/constants'; +import webhookIcon from '@assets/images/icons/webhook.jpg'; +import AppIcon from '@components/app_icon'; +import ProfilePicture from '@components/profile_picture'; +import TouchableWithFeedback from '@components/touchable_with_feedback'; +import {ViewTypes} from '@constants'; +import {emptyFunction} from '@utils/general'; export default class PostProfilePicture extends PureComponent { static propTypes = { diff --git a/app/components/profile_picture/profile_picture.js b/app/components/profile_picture/profile_picture.js index 883e6ef01..7399e725f 100644 --- a/app/components/profile_picture/profile_picture.js +++ b/app/components/profile_picture/profile_picture.js @@ -7,12 +7,10 @@ import {Platform, View} from 'react-native'; import FastImage from 'react-native-fast-image'; import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome'; +import placeholder from '@assets/images/profile.jpg'; +import UserStatus from '@components/user_status'; import {Client4} from '@mm-redux/client'; - -import UserStatus from 'app/components/user_status'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; - -import placeholder from 'assets/images/profile.jpg'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; const STATUS_BUFFER = Platform.select({ ios: 3, diff --git a/app/components/progressive_image/progressive_image.js b/app/components/progressive_image/progressive_image.js index d74385c05..c4a05dc02 100644 --- a/app/components/progressive_image/progressive_image.js +++ b/app/components/progressive_image/progressive_image.js @@ -6,10 +6,9 @@ import PropTypes from 'prop-types'; import {Animated, ImageBackground, View, StyleSheet} from 'react-native'; import FastImage from 'react-native-fast-image'; -import CustomPropTypes from 'app/constants/custom_prop_types'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; - -import thumb from 'assets/images/thumb.png'; +import thumb from '@assets/images/thumb.png'; +import CustomPropTypes from '@constants/custom_prop_types'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; const AnimatedImageBackground = Animated.createAnimatedComponent(ImageBackground); const AnimatedFastImage = Animated.createAnimatedComponent(FastImage); diff --git a/app/components/reaction_picker/reaction_picker.js b/app/components/reaction_picker/reaction_picker.js index e61003f66..56319a4c4 100644 --- a/app/components/reaction_picker/reaction_picker.js +++ b/app/components/reaction_picker/reaction_picker.js @@ -9,9 +9,7 @@ import { TouchableWithoutFeedback, } from 'react-native'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import addReactionIcon from 'assets/images/icons/reaction.png'; -import ReactionButton from './reaction_button'; +import addReactionIcon from '@assets/images/icons/reaction.png'; import { REACTION_PICKER_HEIGHT, DEFAULT_EMOJIS, @@ -20,7 +18,10 @@ import { LARGE_CONTAINER_SIZE, SMALL_ICON_SIZE, LARGE_ICON_SIZE, -} from 'app/constants/reaction_picker'; +} from '@constants/reaction_picker'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; + +import ReactionButton from './reaction_button'; export default class ReactionPicker extends PureComponent { static propTypes = { diff --git a/app/components/reactions/reactions.js b/app/components/reactions/reactions.js index 5406cac1a..99aad4a6f 100644 --- a/app/components/reactions/reactions.js +++ b/app/components/reactions/reactions.js @@ -10,12 +10,11 @@ import { import {intlShape} from 'react-intl'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; -import TouchableWithFeedback from 'app/components/touchable_with_feedback'; -import {preventDoubleTap} from 'app/utils/tap'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {showModal, showModalOverCurrentContext} from 'app/actions/navigation'; - -import addReactionIcon from 'assets/images/icons/reaction.png'; +import {showModal, showModalOverCurrentContext} from '@actions/navigation'; +import addReactionIcon from '@assets/images/icons/reaction.png'; +import TouchableWithFeedback from '@components/touchable_with_feedback'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import Reaction from './reaction'; diff --git a/app/components/sidebars/main/channels_list/filtered_list/index.js b/app/components/sidebars/main/channels_list/filtered_list/index.js index 938ec8f0e..02a6811dd 100644 --- a/app/components/sidebars/main/channels_list/filtered_list/index.js +++ b/app/components/sidebars/main/channels_list/filtered_list/index.js @@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import {createSelector} from 'reselect'; +import Config from '@assets/config'; import {searchChannels} from '@mm-redux/actions/channels'; import {getProfilesInTeam, searchProfiles} from '@mm-redux/actions/users'; import {makeGroupMessageVisibleIfNecessary} from '@mm-redux/actions/preferences'; @@ -20,8 +21,7 @@ import {getConfig} from '@mm-redux/selectors/entities/general'; import {getCurrentTeam} from '@mm-redux/selectors/entities/teams'; import {getCurrentUserId, getProfilesInCurrentTeam, getUsers, getUserIdsInChannels, getUserStatuses} from '@mm-redux/selectors/entities/users'; import {getDirectShowPreferences, getTeammateNameDisplaySetting, getTheme} from '@mm-redux/selectors/entities/preferences'; -import {isLandscape} from 'app/selectors/device'; -import Config from 'assets/config'; +import {isLandscape} from '@selectors/device'; import FilteredList from './filtered_list'; diff --git a/app/components/vector_icon.js b/app/components/vector_icon.js index 935820b55..23d57414c 100644 --- a/app/components/vector_icon.js +++ b/app/components/vector_icon.js @@ -11,7 +11,7 @@ import FoundationIcon from 'react-native-vector-icons/Foundation'; import IonIcon from 'react-native-vector-icons/Ionicons'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; -import icoMoonConfig from 'assets/mattermost-fonts.json'; +import icoMoonConfig from '@assets/mattermost-fonts.json'; const Mattermost = createIconSetFromIcoMoon( icoMoonConfig, diff --git a/app/components/video_controls.js b/app/components/video_controls.js index 61ed42017..5474d560e 100644 --- a/app/components/video_controls.js +++ b/app/components/video_controls.js @@ -18,10 +18,10 @@ import { import FastImage from 'react-native-fast-image'; import Slider from 'react-native-slider'; -import fullscreenImage from 'assets/images/video_player/fullscreen.png'; -import pauseImage from 'assets/images/video_player/pause.png'; -import playImage from 'assets/images/video_player/play.png'; -import replayImage from 'assets/images/video_player/replay.png'; +import fullscreenImage from '@assets/images/video_player/fullscreen.png'; +import pauseImage from '@assets/images/video_player/pause.png'; +import playImage from '@assets/images/video_player/play.png'; +import replayImage from '@assets/images/video_player/replay.png'; export const PLAYER_STATE = { PLAYING: 0, diff --git a/app/i18n/index.js b/app/i18n/index.js index 2dadac003..2de7582b0 100644 --- a/app/i18n/index.js +++ b/app/i18n/index.js @@ -6,7 +6,7 @@ import {addLocaleData} from 'react-intl'; import enLocaleData from 'react-intl/locale-data/en'; import moment from 'moment'; -import en from 'assets/i18n/en.json'; +import en from '@assets/i18n/en.json'; export const DEFAULT_LOCALE = 'en'; @@ -19,77 +19,77 @@ function loadTranslation(locale) { let momentData; switch (locale) { case 'de': - translations = require('assets/i18n/de.json'); + translations = require('@assets/i18n/de.json'); localeData = require('react-intl/locale-data/de'); momentData = require('moment/locale/de'); break; case 'es': - translations = require('assets/i18n/es.json'); + translations = require('@assets/i18n/es.json'); localeData = require('react-intl/locale-data/es'); momentData = require('moment/locale/es'); break; case 'fr': - translations = require('assets/i18n/fr.json'); + translations = require('@assets/i18n/fr.json'); localeData = require('react-intl/locale-data/fr'); momentData = require('moment/locale/fr'); break; case 'it': - translations = require('assets/i18n/it.json'); + translations = require('@assets/i18n/it.json'); localeData = require('react-intl/locale-data/it'); momentData = require('moment/locale/it'); break; case 'ja': - translations = require('assets/i18n/ja.json'); + translations = require('@assets/i18n/ja.json'); localeData = require('react-intl/locale-data/ja'); momentData = require('moment/locale/ja'); break; case 'ko': - translations = require('assets/i18n/ko.json'); + translations = require('@assets/i18n/ko.json'); localeData = require('react-intl/locale-data/ko'); momentData = require('moment/locale/ko'); break; case 'nl': - translations = require('assets/i18n/nl.json'); + translations = require('@assets/i18n/nl.json'); localeData = require('react-intl/locale-data/nl'); momentData = require('moment/locale/nl'); break; case 'pl': - translations = require('assets/i18n/pl.json'); + translations = require('@assets/i18n/pl.json'); localeData = require('react-intl/locale-data/pl'); momentData = require('moment/locale/pl'); break; case 'pt-BR': - translations = require('assets/i18n/pt-BR.json'); + translations = require('@assets/i18n/pt-BR.json'); localeData = require('react-intl/locale-data/pt'); momentData = require('moment/locale/pt-br'); break; case 'ro': - translations = require('assets/i18n/ro.json'); + translations = require('@assets/i18n/ro.json'); localeData = require('react-intl/locale-data/ro'); momentData = require('moment/locale/ro'); break; case 'ru': - translations = require('assets/i18n/ru.json'); + translations = require('@assets/i18n/ru.json'); localeData = require('react-intl/locale-data/ru'); momentData = require('moment/locale/ru'); break; case 'tr': - translations = require('assets/i18n/tr.json'); + translations = require('@assets/i18n/tr.json'); localeData = require('react-intl/locale-data/tr'); momentData = require('moment/locale/tr'); break; case 'uk': - translations = require('assets/i18n/uk.json'); + translations = require('@assets/i18n/uk.json'); localeData = require('react-intl/locale-data/uk'); momentData = require('moment/locale/uk'); break; case 'zh-CN': - translations = require('assets/i18n/zh-CN.json'); + translations = require('@assets/i18n/zh-CN.json'); localeData = require('react-intl/locale-data/zh'); momentData = require('moment/locale/zh-cn'); break; case 'zh-TW': - translations = require('assets/i18n/zh-TW.json'); + translations = require('@assets/i18n/zh-TW.json'); localeData = require('react-intl/locale-data/zh'); momentData = require('moment/locale/zh-tw'); break; diff --git a/app/init/analytics.ts b/app/init/analytics.ts new file mode 100644 index 000000000..666d21a80 --- /dev/null +++ b/app/init/analytics.ts @@ -0,0 +1,84 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import DeviceInfo from 'react-native-device-info'; +import {Dimensions} from 'react-native'; + +import LocalConfig from '@assets/config.json'; +import {Config} from '@mm-redux/types/config'; +import tracker from '@utils/time_tracker'; + +type RudderClient = { + setup(key: string, options: any): Promise; + track(event: string, properties: Record | undefined, options?: Record): void; + identify(userId: string, traits: Record, options?: Record): Promise; + screen(name: string, properties: Record | undefined, options?: Record): void; + reset(): Promise; +} + +let diagnosticId: string | undefined; +export let analytics: RudderClient | null = null; +export let context: any; + +export async function init(config: Config) { + if (!analytics) { + analytics = require('@rudderstack/rudder-sdk-react-native').default; + } + + if (analytics) { + const {height, width} = Dimensions.get('window'); + diagnosticId = config.DiagnosticId; + + if (diagnosticId) { + await analytics.setup(LocalConfig.RudderApiKey, { + dataPlaneUrl: 'https://pdat.matterlytics.com', + recordScreenViews: true, + flushQueueSize: 20, + }); + + context = { + app: { + version: DeviceInfo.getVersion(), + build: DeviceInfo.getBuildNumber(), + }, + device: { + dimensions: { + height, + width, + }, + isTablet: DeviceInfo.isTablet(), + os: DeviceInfo.getSystemVersion(), + }, + ip: '0.0.0.0', + server: config.Version, + }; + + analytics.identify( + diagnosticId, + context, + ); + } else { + analytics.reset(); + } + } + + return analytics; +} + +export function recordTime(screenName: string, category: string, userId: string) { + if (analytics) { + const track: Record = tracker; + const startTime: number = track[category]; + track[category] = 0; + analytics.screen( + screenName, { + userId: diagnosticId, + context, + properties: { + actual_user_id: userId, + time: Date.now() - startTime, + }, + }, + ); + } +} diff --git a/app/init/emm_provider.js b/app/init/emm_provider.js index 89df193bc..e6f1bad5c 100644 --- a/app/init/emm_provider.js +++ b/app/init/emm_provider.js @@ -4,6 +4,7 @@ import {Alert, Platform} from 'react-native'; import {setServerUrl} from '@actions/views/select_server'; +import LocalConfig from '@assets/config'; import {getTranslations} from '@i18n'; import {getCurrentLocale} from '@selectors/i18n'; import Store from '@store/store'; @@ -14,8 +15,6 @@ import mattermostManaged from 'app/mattermost_managed'; import {getAppCredentials} from './credentials'; -import LocalConfig from 'assets/config'; - class EMMProvider { constructor() { this.enabled = false; diff --git a/app/init/fetch.js b/app/init/fetch.js index 0ea1180e5..cf7b53633 100644 --- a/app/init/fetch.js +++ b/app/init/fetch.js @@ -6,16 +6,15 @@ import DeviceInfo from 'react-native-device-info'; import RNFetchBlob from 'rn-fetch-blob'; import urlParse from 'url-parse'; +import LocalConfig from '@assets/config'; import {Client4} from '@mm-redux/client'; import {ClientError, HEADER_X_VERSION_ID} from '@mm-redux/client/client4'; import EventEmitter from '@mm-redux/utils/event_emitter'; import {General} from '@mm-redux/constants'; +import {t} from '@utils/i18n'; import mattermostBucket from 'app/mattermost_bucket'; import mattermostManaged from 'app/mattermost_managed'; -import LocalConfig from 'assets/config'; - -import {t} from 'app/utils/i18n'; /* eslint-disable no-throw-literal */ diff --git a/app/init/global_event_handler.js b/app/init/global_event_handler.js index f02ec4109..9d9a07925 100644 --- a/app/init/global_event_handler.js +++ b/app/init/global_event_handler.js @@ -13,6 +13,7 @@ import {autoUpdateTimezone} from '@mm-redux/actions/timezone'; import {close as closeWebSocket} from '@actions/websocket'; import {Client4} from '@mm-redux/client'; import {General} from '@mm-redux/constants'; +import {getConfig} from '@mm-redux/selectors/entities/general'; import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels'; import {getCurrentUserId, getUser} from '@mm-redux/selectors/entities/users'; import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone'; @@ -23,6 +24,7 @@ import {selectDefaultChannel} from '@actions/views/channel'; import {showOverlay} from '@actions/navigation'; import {loadConfigAndLicense, setDeepLinkURL, startDataCleanup} from '@actions/views/root'; import {loadMe, logout} from '@actions/views/user'; +import LocalConfig from '@assets/config'; import {NavigationTypes, ViewTypes} from '@constants'; import {getTranslations, resetMomentLocale} from '@i18n'; import PushNotifications from 'app/push_notifications'; @@ -35,7 +37,6 @@ import {getDeviceTimezoneAsync} from '@utils/timezone'; import mattermostBucket from 'app/mattermost_bucket'; import mattermostManaged from 'app/mattermost_managed'; -import LocalConfig from 'assets/config'; import {getAppCredentials, removeAppCredentials} from './credentials'; import emmProvider from './emm_provider'; @@ -43,6 +44,8 @@ import emmProvider from './emm_provider'; const {StatusBarManager} = NativeModules; const PROMPT_IN_APP_PIN_CODE_AFTER = 5 * 1000; +let analytics; + class GlobalEventHandler { constructor() { this.pushNotificationListener = false; @@ -112,14 +115,16 @@ class GlobalEventHandler { mattermostManaged.addEventListener('managedConfigDidChange', this.onManagedConfigurationChange); }; - configureAnalytics = (config) => { - const initAnalytics = require('app/utils/segment').init; + configureAnalytics = async () => { + const state = Store.redux.getState(); + const config = getConfig(state); + const initAnalytics = require('./analytics').init; - if (!__DEV__ && config && config.DiagnosticsEnabled === 'true' && config.DiagnosticId && LocalConfig.SegmentApiKey) { - initAnalytics(config); - } else { - global.analytics = null; + if (config && config.DiagnosticsEnabled === 'true' && config.DiagnosticId && LocalConfig.RudderApiKey) { + analytics = await initAnalytics(config); } + + return analytics; }; onAppStateChange = (appState) => { @@ -158,6 +163,11 @@ class GlobalEventHandler { Store.redux.dispatch(closeWebSocket(false)); Store.redux.dispatch(setServerVersion('')); await this.resetState(); + + if (analytics) { + await analytics.reset(); + } + removeAppCredentials(); deleteFileCache(); resetMomentLocale(); @@ -235,7 +245,7 @@ class GlobalEventHandler { } }; - onServerVersionChanged = async (serverVersion) => { + onServerVersionChanged = (serverVersion) => { const {dispatch, getState} = Store.redux; const state = getState(); const match = serverVersion && serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g); @@ -256,8 +266,7 @@ class GlobalEventHandler { ); } else if (state.entities.users && state.entities.users.currentUserId) { dispatch(setServerVersion(serverVersion)); - const data = await dispatch(loadConfigAndLicense()); - this.configureAnalytics(data.config); + dispatch(loadConfigAndLicense()); } } }; diff --git a/app/init/global_event_handler.test.js b/app/init/global_event_handler.test.js index 898661778..07e0bbcb1 100644 --- a/app/init/global_event_handler.test.js +++ b/app/init/global_event_handler.test.js @@ -6,14 +6,14 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import semver from 'semver/preload'; -import PushNotification from 'app/push_notifications'; -import mattermostBucket from 'app/mattermost_bucket'; +import {MinServerVersion} from '@assets/config'; import * as I18n from '@i18n'; import EventEmitter from '@mm-redux/utils/event_emitter'; import Store from '@store/store'; import intitialState from '@store/initial_state'; -import {MinServerVersion} from 'assets/config'; +import PushNotification from 'app/push_notifications'; +import mattermostBucket from 'app/mattermost_bucket'; import GlobalEventHandler from './global_event_handler'; @@ -183,7 +183,6 @@ describe('GlobalEventHandler', () => { expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledWith('setServerVersion'); expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense'); - expect(configureAnalytics).toHaveBeenCalledTimes(1); }); it('should dispatch on gte min server version with currentUserId', async () => { @@ -193,13 +192,11 @@ describe('GlobalEventHandler', () => { expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledWith('setServerVersion'); expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense'); - expect(configureAnalytics).toHaveBeenCalledTimes(1); version = semver.coerce(minVersion.major + 1).version; await GlobalEventHandler.onServerVersionChanged(version); expect(alert).not.toHaveBeenCalled(); expect(dispatch).toHaveBeenCalledTimes(4); - expect(configureAnalytics).toHaveBeenCalledTimes(2); }); it('should alert on lt min server version', async () => { diff --git a/app/mattermost.js b/app/mattermost.js index c44d42f06..7600309d9 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -62,6 +62,7 @@ const launchApp = (credentials) => { const valid = validatePreviousVersion(previousVersion); if (valid) { store.dispatch(loadMe()); + await globalEventHandler.configureAnalytics(); resetToChannel({skipMetrics: true}); } else { const error = new Error(`Previous app version "${previousVersion}" is invalid.`); diff --git a/app/mm-redux/client/client4.ts b/app/mm-redux/client/client4.ts index e10e81ed3..41cc64b73 100644 --- a/app/mm-redux/client/client4.ts +++ b/app/mm-redux/client/client4.ts @@ -1,13 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {analytics, context} from '@init/analytics.ts'; import {buildQueryString, isMinimumServerVersion} from '@mm-redux/utils/helpers'; import {cleanUrlForLogging} from '@mm-redux/utils/sentry'; -import {General} from '../constants'; - import {isSystemAdmin} from '@mm-redux/utils/user_utils'; - -import fetch from './fetch_etag'; import {UserProfile, UserStatus} from '@mm-redux/types/users'; import {Team} from '@mm-redux/types/teams'; import {Channel, ChannelModerationPatch} from '@mm-redux/types/channels'; @@ -23,6 +20,9 @@ import {Config} from '@mm-redux/types/config'; import {Bot, BotPatch} from '@mm-redux/types/bots'; import {SyncablePatch} from '@mm-redux/types/groups'; +import fetch from './fetch_etag'; +import {General} from '../constants'; + const FormData = require('form-data'); const HEADER_AUTH = 'Authorization'; const HEADER_BEARER = 'BEARER'; @@ -3078,6 +3078,10 @@ export default class Client4 { }; trackEvent(category: string, event: string, props?: any) { + if (!analytics) { + return; + } + // Temporary change to allow only certain events to reduce data rate - see MM-13062 // All events in 'admin' category are allowed, since they are low-volume if (category !== 'admin' && ![ @@ -3116,31 +3120,11 @@ export default class Client4 { user_actual_id: this.userId, }, props); const options = { - context: { - ip: '0.0.0.0', - }, - page: { - path: '', - referrer: '', - search: '', - title: '', - url: '', - }, + context, anonymousId: '00000000000000000000000000', }; - const globalAny: any = global; - if (globalAny && globalAny.window && globalAny.window.analytics && globalAny.window.analytics.initialized) { - globalAny.window.analytics.track('event', properties, options); - } else if (globalAny && globalAny.analytics) { - if (globalAny.analytics_context) { - options.context = globalAny.analytics_context; - } - globalAny.analytics.track(Object.assign({ - event: 'event', - userId: this.diagnosticId, - }, {properties}, options)); - } + analytics.track(event, properties, options); } } diff --git a/app/mm-redux/utils/i18n_utils.test.js b/app/mm-redux/utils/i18n_utils.test.js index cb133a863..3fbae6b2b 100644 --- a/app/mm-redux/utils/i18n_utils.test.js +++ b/app/mm-redux/utils/i18n_utils.test.js @@ -4,7 +4,7 @@ import assert from 'assert'; import {setLocalizeFunction, localizeMessage} from '@mm-redux/utils/i18n_utils'; -const en = require('assets/i18n/en.json'); +const en = require('@assets/i18n/en.json'); const [testKey, testValue] = Object.entries(en)[0]; describe('i18n utils', () => { diff --git a/app/reducers/views/select_server.js b/app/reducers/views/select_server.js index ea9fb04f4..04233d981 100644 --- a/app/reducers/views/select_server.js +++ b/app/reducers/views/select_server.js @@ -3,7 +3,7 @@ import {combineReducers} from 'redux'; -import Config from 'assets/config.json'; +import Config from '@assets/config.json'; import {ViewTypes} from 'app/constants'; diff --git a/app/screens/about/about.js b/app/screens/about/about.js index 67482c698..dbfbdd79a 100644 --- a/app/screens/about/about.js +++ b/app/screens/about/about.js @@ -12,13 +12,13 @@ import { } from 'react-native'; import DeviceInfo from 'react-native-device-info'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; -import AppIcon from 'app/components/app_icon'; -import Config from 'assets/config'; -import AboutLinks from 'app/constants/about_links'; +import Config from '@assets/config'; +import AppIcon from '@components/app_icon'; +import FormattedText from '@components/formatted_text'; +import {paddingHorizontal as padding} from '@components/safe_area_view/iphone_x_spacing'; +import StatusBar from '@components/status_bar'; +import AboutLinks from '@constants/about_links'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; const MATTERMOST_BUNDLE_IDS = ['com.mattermost.rnbeta', 'com.mattermost.rn']; diff --git a/app/screens/channel/channel.android.js b/app/screens/channel/channel.android.js index 6b492eb1c..1ae28fd5d 100644 --- a/app/screens/channel/channel.android.js +++ b/app/screens/channel/channel.android.js @@ -5,6 +5,7 @@ import React from 'react'; import {StyleSheet, View} from 'react-native'; import {openMainSideMenu, openSettingsSideMenu} from '@actions/navigation'; +import LocalConfig from '@assets/config'; import KeyboardLayout from '@components/layout/keyboard_layout'; import InteractiveDialogController from '@components/interactive_dialog_controller'; import NetworkIndicator from '@components/network_indicator'; @@ -12,8 +13,6 @@ import PostDraft from '@components/post_draft'; import {NavigationTypes} from '@constants'; import EventEmitter from '@mm-redux/utils/event_emitter'; -import LocalConfig from 'assets/config'; - import ChannelNavBar from './channel_nav_bar'; import ChannelPostList from './channel_post_list'; diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js index f0e84747b..c871096eb 100644 --- a/app/screens/channel/channel.ios.js +++ b/app/screens/channel/channel.ios.js @@ -5,6 +5,7 @@ import React from 'react'; import {View} from 'react-native'; import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view'; +import LocalConfig from '@assets/config'; import Autocomplete, {AUTOCOMPLETE_MAX_HEIGHT} from '@components/autocomplete'; import InteractiveDialogController from '@components/interactive_dialog_controller'; import NetworkIndicator from '@components/network_indicator'; @@ -16,8 +17,6 @@ import StatusBar from '@components/status_bar'; import {ACCESSORIES_CONTAINER_NATIVE_ID, CHANNEL_POST_TEXTBOX_CURSOR_CHANGE, CHANNEL_POST_TEXTBOX_VALUE_CHANGE} from '@constants/post_draft'; import {makeStyleSheetFromTheme} from '@utils/theme'; -import LocalConfig from 'assets/config'; - import ChannelBase, {ClientUpgradeListener} from './channel_base'; import ChannelNavBar from './channel_nav_bar'; import ChannelPostList from './channel_post_list'; diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js index 6fef6a586..50d15f860 100644 --- a/app/screens/channel/channel_base.js +++ b/app/screens/channel/channel_base.js @@ -11,20 +11,19 @@ import { } from 'react-native'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; +import {showModal, showModalOverCurrentContext} from '@actions/navigation'; +import LocalConfig from '@assets/config'; +import SafeAreaView from '@components/safe_area_view'; +import EmptyToolbar from '@components/start/empty_toolbar'; +import {NavigationTypes} from '@constants'; import EventEmitter from '@mm-redux/utils/event_emitter'; +import EphemeralStore from '@store/ephemeral_store'; +import {preventDoubleTap} from '@utils/tap'; +import {setNavigatorStyles} from '@utils/theme'; +import tracker from '@utils/time_tracker'; -import {showModal, showModalOverCurrentContext} from 'app/actions/navigation'; -import SafeAreaView from 'app/components/safe_area_view'; -import EmptyToolbar from 'app/components/start/empty_toolbar'; -import {NavigationTypes} from 'app/constants'; import PushNotifications from 'app/push_notifications'; -import EphemeralStore from 'app/store/ephemeral_store'; import telemetry from 'app/telemetry'; -import {preventDoubleTap} from 'app/utils/tap'; -import {setNavigatorStyles} from 'app/utils/theme'; -import tracker from 'app/utils/time_tracker'; - -import LocalConfig from 'assets/config'; export let ClientUpgradeListener; diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js index 91b6f8528..9829799e9 100644 --- a/app/screens/channel_info/channel_info.js +++ b/app/screens/channel_info/channel_info.js @@ -11,16 +11,14 @@ import { } from 'react-native'; import {Navigation} from 'react-native-navigation'; +import {dismissModal, goToScreen, showModalOverCurrentContext} from '@actions/navigation'; +import pinIcon from '@assets/images/channel_info/pin.png'; +import StatusBar from '@components/status_bar'; import {General, Users} from '@mm-redux/constants'; - -import StatusBar from 'app/components/status_bar'; -import {preventDoubleTap} from 'app/utils/tap'; -import {alertErrorWithFallback} from 'app/utils/general'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {t} from 'app/utils/i18n'; -import {dismissModal, goToScreen, showModalOverCurrentContext} from 'app/actions/navigation'; - -import pinIcon from 'assets/images/channel_info/pin.png'; +import {alertErrorWithFallback} from '@utils/general'; +import {t} from '@utils/i18n'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import ChannelInfoHeader from './channel_info_header'; import ChannelInfoRow from './channel_info_row'; diff --git a/app/screens/channel_info/channel_info.test.js b/app/screens/channel_info/channel_info.test.js index be326b699..846a5cfc0 100644 --- a/app/screens/channel_info/channel_info.test.js +++ b/app/screens/channel_info/channel_info.test.js @@ -3,19 +3,18 @@ import React from 'react'; import {shallow} from 'enzyme'; +import * as NavigationActions from '@actions/navigation'; import Preferences from '@mm-redux/constants/preferences'; import {General} from '@mm-redux/constants'; -import * as NavigationActions from 'app/actions/navigation'; - import ChannelInfo from './channel_info'; // ChannelInfoRow expects to receive the pinIcon as a number -jest.mock('assets/images/channel_info/pin.png', () => { +jest.mock('@assets/images/channel_info/pin.png', () => { return 1; }); -jest.mock('app/utils/theme', () => { +jest.mock('@utils/theme', () => { const original = require.requireActual('app/utils/theme'); return { ...original, diff --git a/app/screens/client_upgrade/client_upgrade.js b/app/screens/client_upgrade/client_upgrade.js index 581caea68..f9ebe2e98 100644 --- a/app/screens/client_upgrade/client_upgrade.js +++ b/app/screens/client_upgrade/client_upgrade.js @@ -14,13 +14,13 @@ import { } from 'react-native'; import {intlShape} from 'react-intl'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; -import {UpgradeTypes} from 'app/constants'; -import logo from 'assets/images/logo.png'; -import {checkUpgradeType, isUpgradeAvailable} from 'app/utils/client_upgrade'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {popTopScreen, dismissModal} from 'app/actions/navigation'; +import {popTopScreen, dismissModal} from '@actions/navigation'; +import logo from '@assets/images/logo.png'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; +import {UpgradeTypes} from '@constants'; +import {checkUpgradeType, isUpgradeAvailable} from '@utils/client_upgrade'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; export default class ClientUpgrade extends PureComponent { static propTypes = { diff --git a/app/screens/forgot_password/forgot_password.js b/app/screens/forgot_password/forgot_password.js index bcc372bab..b9ef2f697 100644 --- a/app/screens/forgot_password/forgot_password.js +++ b/app/screens/forgot_password/forgot_password.js @@ -15,16 +15,14 @@ import { View, } from 'react-native'; +import ErrorText from '@components/error_text'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; import {isEmail} from '@mm-redux/utils/helpers'; +import {changeOpacity} from '@utils/theme'; import {GlobalStyles} from 'app/styles'; -import ErrorText from 'app/components/error_text'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; - -import {changeOpacity} from 'app/utils/theme'; - export default class ForgotPassword extends PureComponent { static propTypes = { actions: PropTypes.shape({ @@ -156,7 +154,7 @@ export default class ForgotPassword extends PureComponent { > {displayError} {passwordFormView} diff --git a/app/screens/login/login.js b/app/screens/login/login.js index 2e3dbd800..dfba4fb77 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -19,19 +19,19 @@ import { import Button from 'react-native-button'; import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; -import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; -import ErrorText from 'app/components/error_text'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; -import {resetToChannel, goToScreen} from 'app/actions/navigation'; -import mattermostManaged from 'app/mattermost_managed'; -import {preventDoubleTap} from 'app/utils/tap'; -import tracker from 'app/utils/time_tracker'; -import {t} from 'app/utils/i18n'; -import {setMfaPreflightDone, getMfaPreflightDone} from 'app/utils/security'; -import {changeOpacity} from 'app/utils/theme'; -import {GlobalStyles} from 'app/styles'; +import {resetToChannel, goToScreen} from '@actions/navigation'; +import ErrorText from '@components/error_text'; +import FormattedText from '@components/formatted_text'; +import {paddingHorizontal as padding} from '@components/safe_area_view/iphone_x_spacing'; +import StatusBar from '@components/status_bar'; +import {t} from '@utils/i18n'; +import {setMfaPreflightDone, getMfaPreflightDone} from '@utils/security'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity} from '@utils/theme'; +import tracker from '@utils/time_tracker'; +import mattermostManaged from 'app/mattermost_managed'; +import {GlobalStyles} from 'app/styles'; import telemetry from 'app/telemetry'; export const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error']; @@ -388,7 +388,7 @@ export default class Login extends PureComponent { enableOnAndroid={true} > diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js index 2c3ee77a7..b95b120e8 100644 --- a/app/screens/login_options/login_options.js +++ b/app/screens/login_options/login_options.js @@ -13,17 +13,17 @@ import { } from 'react-native'; import Button from 'react-native-button'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; -import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; -import {GlobalStyles} from 'app/styles'; -import {preventDoubleTap} from 'app/utils/tap'; -import {ViewTypes} from 'app/constants'; -import {goToScreen} from 'app/actions/navigation'; +import {goToScreen} from '@actions/navigation'; +import LocalConfig from '@assets/config'; +import gitlab from '@assets/images/gitlab.png'; +import logo from '@assets/images/logo.png'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; +import {paddingHorizontal as padding} from '@components/safe_area_view/iphone_x_spacing'; +import {ViewTypes} from '@constants'; +import {preventDoubleTap} from '@utils/tap'; -import LocalConfig from 'assets/config'; -import gitlab from 'assets/images/gitlab.png'; -import logo from 'assets/images/logo.png'; +import {GlobalStyles} from 'app/styles'; export default class LoginOptions extends PureComponent { static propTypes = { diff --git a/app/screens/mfa/mfa.js b/app/screens/mfa/mfa.js index c10359416..0f14c1379 100644 --- a/app/screens/mfa/mfa.js +++ b/app/screens/mfa/mfa.js @@ -15,15 +15,16 @@ import { } from 'react-native'; import Button from 'react-native-button'; -import ErrorText from 'app/components/error_text'; -import FormattedText from 'app/components/formatted_text'; -import StatusBar from 'app/components/status_bar'; -import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder'; +import {popTopScreen} from '@actions/navigation'; +import ErrorText from '@components/error_text'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; +import TextInputWithLocalizedPlaceholder from '@components/text_input_with_localized_placeholder'; +import {t} from '@utils/i18n'; +import {preventDoubleTap} from '@utils/tap'; +import {setMfaPreflightDone} from '@utils/security'; + import {GlobalStyles} from 'app/styles'; -import {preventDoubleTap} from 'app/utils/tap'; -import {t} from 'app/utils/i18n'; -import {setMfaPreflightDone} from 'app/utils/security'; -import {popTopScreen} from 'app/actions/navigation'; export default class Mfa extends PureComponent { static propTypes = { @@ -140,7 +141,7 @@ export default class Mfa extends PureComponent { { + handleLoginOptions = async (props = this.props) => { const {formatMessage} = this.context.intl; const {config, license} = props; const samlEnabled = config.EnableSaml === 'true' && license.IsLicensed === 'true' && license.SAML === 'true'; @@ -252,6 +250,7 @@ export default class SelectServer extends PureComponent { } this.props.actions.resetPing(); + await globalEventHandler.configureAnalytics(); if (Platform.OS === 'ios') { if (config.ExperimentalClientSideCertEnable === 'true' && config.ExperimentalClientSideCertCheck === 'primary') { @@ -477,7 +476,7 @@ export default class SelectServer extends PureComponent { > diff --git a/app/screens/settings/advanced_settings/advanced_settings.js b/app/screens/settings/advanced_settings/advanced_settings.js index c6ded2e5f..df21d52d9 100644 --- a/app/screens/settings/advanced_settings/advanced_settings.js +++ b/app/screens/settings/advanced_settings/advanced_settings.js @@ -14,17 +14,15 @@ import { } from 'react-native'; import * as Sentry from '@sentry/react-native'; +import {dismissAllModals} from '@actions/navigation'; +import Config from '@assets/config'; +import StatusBar from '@components/status_bar'; import {getFormattedFileSize} from '@mm-redux/utils/file_utils'; - -import SettingsItem from 'app/screens/settings/settings_item'; -import StatusBar from 'app/components/status_bar'; -import {t} from 'app/utils/i18n'; -import {deleteFileCache, getFileCacheSize} from 'app/utils/file'; -import {preventDoubleTap} from 'app/utils/tap'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {dismissAllModals} from 'app/actions/navigation'; - -import Config from 'assets/config'; +import SettingsItem from '@screens/settings/settings_item'; +import {t} from '@utils/i18n'; +import {deleteFileCache, getFileCacheSize} from '@utils/file'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; class AdvancedSettings extends Component { static propTypes = { diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js index a94022765..a9cccf1a5 100644 --- a/app/screens/settings/general/settings.js +++ b/app/screens/settings/general/settings.js @@ -13,15 +13,14 @@ import { import DeviceInfo from 'react-native-device-info'; import {Navigation} from 'react-native-navigation'; -import SettingsItem from 'app/screens/settings/settings_item'; -import StatusBar from 'app/components/status_bar'; -import {preventDoubleTap} from 'app/utils/tap'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {isValidUrl} from 'app/utils/url'; -import {t} from 'app/utils/i18n'; -import {goToScreen, dismissModal} from 'app/actions/navigation'; - -import LocalConfig from 'assets/config'; +import {goToScreen, dismissModal} from '@actions/navigation'; +import LocalConfig from '@assets/config'; +import StatusBar from '@components/status_bar'; +import SettingsItem from '@screens/settings/settings_item'; +import {t} from '@utils/i18n'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {isValidUrl} from '@utils/url'; class Settings extends PureComponent { static propTypes = { diff --git a/app/screens/settings/theme/theme.js b/app/screens/settings/theme/theme.js index 946af24b9..092fcd943 100644 --- a/app/screens/settings/theme/theme.js +++ b/app/screens/settings/theme/theme.js @@ -8,19 +8,19 @@ import {intlShape} from 'react-intl'; import Preferences from '@mm-redux/constants/preferences'; -import StatusBar from 'app/components/status_bar'; -import Section from 'app/screens/settings/section'; -import SectionItem from 'app/screens/settings/section_item'; -import FormattedText from 'app/components/formatted_text'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; +import FormattedText from '@components/formatted_text'; +import StatusBar from '@components/status_bar'; +import Section from '@screens/settings/section'; +import SectionItem from '@screens/settings/section_item'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import ThemeTile from './theme_tile'; const thumbnailImages = { - default: require('assets/images/themes/mattermost.png'), - organization: require('assets/images/themes/organization.png'), - mattermostDark: require('assets/images/themes/mattermost_dark.png'), - windows10: require('assets/images/themes/windows_dark.png'), + default: require('@assets/images/themes/mattermost.png'), + organization: require('@assets/images/themes/organization.png'), + mattermostDark: require('@assets/images/themes/mattermost_dark.png'), + windows10: require('@assets/images/themes/windows_dark.png'), }; export default class Theme extends React.PureComponent { diff --git a/app/screens/settings/theme/theme_tile.js b/app/screens/settings/theme/theme_tile.js index 01b146e9a..2d50449ae 100644 --- a/app/screens/settings/theme/theme_tile.js +++ b/app/screens/settings/theme/theme_tile.js @@ -10,9 +10,9 @@ import { View, } from 'react-native'; -import {makeStyleSheetFromTheme} from 'app/utils/theme'; +import {makeStyleSheetFromTheme} from '@utils/theme'; -const checkmark = require('assets/images/themes/check.png'); +const checkmark = require('@assets/images/themes/check.png'); const tilePadding = 8; diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index e74062ec1..02ca131d4 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -12,30 +12,27 @@ import { import {intlShape} from 'react-intl'; import {Navigation} from 'react-native-navigation'; -import {displayUsername} from '@mm-redux/utils/user_utils'; -import {getUserCurrentTimezone} from '@mm-redux/utils/timezone_utils'; - -import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing'; -import ProfilePicture from 'app/components/profile_picture'; -import FormattedText from 'app/components/formatted_text'; -import FormattedTime from 'app/components/formatted_time'; -import StatusBar from 'app/components/status_bar'; -import {BotTag, GuestTag} from 'app/components/tag'; - -import {alertErrorWithFallback} from 'app/utils/general'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {t} from 'app/utils/i18n'; -import {isGuest} from 'app/utils/users'; - import { goToScreen, popToRoot, dismissModal, setButtons, -} from 'app/actions/navigation'; +} from '@actions/navigation'; +import Config from '@assets/config'; +import FormattedTime from '@components/formatted_time'; +import ProfilePicture from '@components/profile_picture'; +import FormattedText from '@components/formatted_text'; +import {paddingHorizontal as padding} from '@components/safe_area_view/iphone_x_spacing'; +import StatusBar from '@components/status_bar'; +import {BotTag, GuestTag} from '@components/tag'; +import {displayUsername} from '@mm-redux/utils/user_utils'; +import {getUserCurrentTimezone} from '@mm-redux/utils/timezone_utils'; +import {alertErrorWithFallback} from '@utils/general'; +import {t} from '@utils/i18n'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {isGuest} from '@utils/users'; import UserProfileRow from './user_profile_row'; -import Config from 'assets/config'; export default class UserProfile extends PureComponent { static propTypes = { diff --git a/app/selectors/client_upgrade.js b/app/selectors/client_upgrade.js index bbd39620d..bfb0f2295 100644 --- a/app/selectors/client_upgrade.js +++ b/app/selectors/client_upgrade.js @@ -4,10 +4,9 @@ import {createSelector} from 'reselect'; import {Platform} from 'react-native'; import DeviceInfo from 'react-native-device-info'; +import LocalConfig from '@assets/config'; import {getConfig} from '@mm-redux/selectors/entities/general'; -import LocalConfig from 'assets/config'; - const getClientUpgrade = createSelector( getConfig, (config) => { diff --git a/app/store/initial_state.js b/app/store/initial_state.js index 41d16827a..00a716ccb 100644 --- a/app/store/initial_state.js +++ b/app/store/initial_state.js @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import Config from 'assets/config.json'; +import Config from '@assets/config.json'; const state = { app: { diff --git a/app/telemetry/telemetry.android.js b/app/telemetry/telemetry.android.js index 81ae31134..9f5808237 100644 --- a/app/telemetry/telemetry.android.js +++ b/app/telemetry/telemetry.android.js @@ -1,8 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import LocalConfig from 'assets/config'; // eslint-disable-line - +import LocalConfig from '@assets/config'; import Store from '@store/store'; import { diff --git a/app/telemetry/telemetry_utils.js b/app/telemetry/telemetry_utils.js index 553c934b0..88b3b2d38 100644 --- a/app/telemetry/telemetry_utils.js +++ b/app/telemetry/telemetry_utils.js @@ -4,7 +4,7 @@ import {Dimensions} from 'react-native'; import DeviceInfo from 'react-native-device-info'; -import LocalConfig from 'assets/config'; +import LocalConfig from '@assets/config'; export function saveToTelemetryServer(data) { const { diff --git a/app/utils/client_upgrade.js b/app/utils/client_upgrade.js index 52017933a..65c4dd297 100644 --- a/app/utils/client_upgrade.js +++ b/app/utils/client_upgrade.js @@ -2,9 +2,8 @@ // See LICENSE.txt for license information. import semver from 'semver'; -import {UpgradeTypes} from 'app/constants'; - -import LocalConfig from 'assets/config'; +import LocalConfig from '@assets/config'; +import {UpgradeTypes} from '@constants'; export function checkUpgradeType(currentVersion, minVersion, latestVersion, logError) { let upgradeType = UpgradeTypes.NO_UPGRADE; diff --git a/app/utils/segment.js b/app/utils/segment.js deleted file mode 100644 index 2542faa6f..000000000 --- a/app/utils/segment.js +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import DeviceInfo from 'react-native-device-info'; -import {Dimensions} from 'react-native'; - -import Config from 'assets/config'; - -import tracker from './time_tracker'; - -let diagnosticId; -let Analytics; - -export function init(config) { - if (!global.analytics) { - if (!Analytics) { - Analytics = require('analytics-react-native').default; - } - diagnosticId = config.DiagnosticId; - const {height, width} = Dimensions.get('window'); - global.analytics = new Analytics(Config.SegmentApiKey, { - flushAt: 20, - flushInterval: 10000, - }); - global.analytics_context = { - app: { - version: DeviceInfo.getVersion(), - build: DeviceInfo.getBuildNumber(), - }, - device: { - dimensions: { - height, - width, - }, - isTablet: DeviceInfo.isTablet(), - os: DeviceInfo.getSystemVersion(), - }, - ip: '0.0.0.0', - server: config.Version, - }; - - global.analytics.identify({ - userId: diagnosticId, - context: global.analytics_context, - page: { - path: '', - referrer: '', - search: '', - title: '', - url: '', - }, - anonymousId: '00000000000000000000000000', - }); - } -} - -export function recordTime(screenName, category, userId) { - if (global.analytics) { - const startTime = tracker[category]; - tracker[category] = 0; - global.analytics.screen({ - userId: diagnosticId, - name: screenName, - context: global.analytics_context, - properties: { - actual_user_id: userId, - time: Date.now() - startTime, - }, - }); - } -} diff --git a/app/utils/sentry/index.js b/app/utils/sentry/index.js index 0086d5b52..645812dbc 100644 --- a/app/utils/sentry/index.js +++ b/app/utils/sentry/index.js @@ -2,8 +2,8 @@ // See LICENSE.txt for license information. import {Platform} from 'react-native'; -import Config from 'assets/config'; +import Config from '@assets/config'; import {Client4} from '@mm-redux/client'; import {getConfig} from '@mm-redux/selectors/entities/general'; import {getCurrentUser} from '@mm-redux/selectors/entities/users'; diff --git a/assets/base/config.json b/assets/base/config.json index ff134a642..d465a5ac2 100644 --- a/assets/base/config.json +++ b/assets/base/config.json @@ -10,7 +10,7 @@ "AboutEnterpriseURL": "http://about.mattermost.com/", "PlatformNoticeURL": "https://about.mattermost.com/platform-notice-txt/", "MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/", - "SegmentApiKey": "", + "RudderApiKey": "", "HideEmailLoginExperimental": false, "HideGitLabLoginExperimental": false, diff --git a/babel.config.js b/babel.config.js index 3ae18fa24..1a0d77bb2 100644 --- a/babel.config.js +++ b/babel.config.js @@ -12,7 +12,7 @@ module.exports = { ['module-resolver', { root: ['.'], alias: { - assets: './dist/assets', + '@assets': './dist/assets', '@actions': './app/actions', '@components': './app/components', '@constants': './app/constants', diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 8ad72e049..05a864e54 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -154,8 +154,8 @@ lane :configure do json = load_config_json('../dist/assets/config.json') # Set the Segment API Key - unless ENV['SEGMENT_API_KEY'].nil? || ENV['SEGMENT_API_KEY'].empty? - json['SegmentApiKey'] = ENV['SEGMENT_API_KEY'] + unless ENV['RUDDER_API_KEY'].nil? || ENV['RUDDER_API_KEY'].empty? + json['RudderApiKey'] = ENV['RUDDER_API_KEY'] end # Configure Sentry if enabled diff --git a/index.js b/index.js index 89b2d352a..e255ea41d 100644 --- a/index.js +++ b/index.js @@ -5,11 +5,9 @@ import 'react-native/Libraries/Core/InitializeCore'; import {DeviceEventEmitter, Platform, Text} from 'react-native'; import 'react-native-gesture-handler'; -import LocalConfig from 'assets/config'; - -import telemetry from 'app/telemetry'; - +import LocalConfig from '@assets/config'; import 'app/mattermost'; +import telemetry from 'app/telemetry'; if (Platform.OS === 'android') { require('harmony-reflect'); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d6da1aa67..87494b7d2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -34,7 +34,7 @@ PODS: - libwebp/webp (1.1.0) - MMKV (1.1.0): - MMKVCore (~> 1.1.0) - - MMKVCore (1.1.0) + - MMKVCore (1.1.1) - Permission-Camera (2.0.10): - RNPermissions - Permission-PhotoLibrary (2.0.10): @@ -322,6 +322,9 @@ PODS: - React - RNReactNativeHapticFeedback (1.9.0): - React + - RNRudderSdk (1.0.0): + - React + - Rudder (= 1.0.3-beta-4) - RNSentry (1.3.6): - React - Sentry (~> 4.4.0) @@ -329,9 +332,10 @@ PODS: - React - RNVectorIcons (6.6.0): - React - - SDWebImage (5.6.1): - - SDWebImage/Core (= 5.6.1) - - SDWebImage/Core (5.6.1) + - Rudder (1.0.3-beta.4) + - SDWebImage (5.8.0): + - SDWebImage/Core (= 5.8.0) + - SDWebImage/Core (5.8.0) - SDWebImageWebPCoder (0.4.1): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.5) @@ -401,6 +405,7 @@ DEPENDENCIES: - RNPermissions (from `../node_modules/react-native-permissions`) - RNReactNativeDocViewer (from `../node_modules/react-native-doc-viewer`) - RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`) + - "RNRudderSdk (from `../node_modules/@rudderstack/rudder-sdk-react-native/ios`)" - "RNSentry (from `../node_modules/@sentry/react-native`)" - RNSVG (from `../node_modules/react-native-svg`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) @@ -414,6 +419,7 @@ SPEC REPOS: - libwebp - MMKV - MMKVCore + - Rudder - SDWebImage - SDWebImageWebPCoder - Sentry @@ -530,6 +536,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-doc-viewer" RNReactNativeHapticFeedback: :path: "../node_modules/react-native-haptic-feedback" + RNRudderSdk: + :path: "../node_modules/@rudderstack/rudder-sdk-react-native/ios" RNSentry: :path: "../node_modules/@sentry/react-native" RNSVG: @@ -550,7 +558,7 @@ SPEC CHECKSUMS: jail-monkey: d7c5048b2336f22ee9c9e0efa145f1f917338ea9 libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 MMKV: 7bb6c30f9ff2ea45bc2398c86e66dd1cb63cfe20 - MMKVCore: f455d4bf01bb5257511c1062fdbc3d7ebdec216b + MMKVCore: 2748bf702287a814a6c514d054033e41e07f5c0f Permission-Camera: 8f0e5decca5f28f70f28a8dc31f012c9bad40ad8 Permission-PhotoLibrary: b209bf23b784c9e1409a57d81c6d11ab1d3079c1 RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035 @@ -598,10 +606,12 @@ SPEC CHECKSUMS: RNPermissions: 8afd37dbc1be65748363c6a90caffd1806567651 RNReactNativeDocViewer: 571c6ac38483531b8fb521d02a6ba54652ed10a7 RNReactNativeHapticFeedback: 2566b468cc8d0e7bb2f84b23adc0f4614594d071 + RNRudderSdk: 3abdcc4b145b3db6571827b0fc7af4e75a96af13 RNSentry: d892cbdee165cc6159d71e2a7954efdb8a361918 RNSVG: 7e16ddfc6e00d5aa69c9eb83e699bcce5dcb85d4 RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 - SDWebImage: 7edb9c3ea661e77a66661f7f044de8c1b55d1120 + Rudder: 358a71278045a608ae4b081b37a465ffb65c129d + SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8 Sentry: 14bdd673870e8cf64932b149fad5bbbf39a9b390 Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b diff --git a/package-lock.json b/package-lock.json index 2fc51b9be..f18859e24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5039,6 +5039,25 @@ } } }, + "@rudderstack/rudder-sdk-react-native": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@rudderstack/rudder-sdk-react-native/-/rudder-sdk-react-native-0.1.2.tgz", + "integrity": "sha512-k7KMnRpNvk0DDu83sIJLKCuOWTmPn0c9qAbhQ0d91NU6QADG2466oYf8yobmLi29au0r5HAnK0M9kIEYKY+Y+g==", + "requires": { + "@babel/runtime": "^7.7.7", + "@types/react-native": "^0.62.2" + }, + "dependencies": { + "@types/react-native": { + "version": "0.62.7", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.7.tgz", + "integrity": "sha512-FGFEt9GcFVl//XxWmxkeBxAx0YnzyEhJpR8hOJrjfaFKZm0KjHzzyCmCksBAP2qHSTrcJCiBkIvYCX/kGiOgww==", + "requires": { + "@types/react": "*" + } + } + } + }, "@sentry/browser": { "version": "5.15.4", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.15.4.tgz", @@ -5756,15 +5775,6 @@ "uri-js": "^4.2.2" } }, - "analytics-react-native": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/analytics-react-native/-/analytics-react-native-1.2.0.tgz", - "integrity": "sha512-ppgJZjnsXMS5YMnYNEXm4LMxpoq08ghkz8l0wX+ECTKICkmP8t+KqKSy0sxCzbrltUyDEJLHOfOkeBr8Ieo7CA==", - "requires": { - "base-64": "0.1.0", - "type-of": "2.0.1" - } - }, "anser": { "version": "1.4.9", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.9.tgz", @@ -22258,11 +22268,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, - "type-of": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-of/-/type-of-2.0.1.tgz", - "integrity": "sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=" - }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/package.json b/package.json index e0f76f50e..b6019e890 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "@react-native-community/async-storage": "1.8.1", "@react-native-community/cameraroll": "1.5.2", "@react-native-community/netinfo": "4.4.0", + "@rudderstack/rudder-sdk-react-native": "0.1.2", "@sentry/react-native": "1.3.6", - "analytics-react-native": "1.2.0", "commonmark": "github:mattermost/commonmark.js#f6ab98dede6ce4b4e7adea140ac77249bfb2d6ce", "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#3a2ac19cab725ad28b170fdc1d397dddedcf87eb", "core-js": "3.6.4", diff --git a/packager/moduleNames.js b/packager/moduleNames.js index bb903d67f..1f67d784b 100644 --- a/packager/moduleNames.js +++ b/packager/moduleNames.js @@ -23,6 +23,7 @@ module.exports = [ 'app/constants/view.js', 'app/constants/websocket.ts', 'app/i18n/index.js', + 'app/init/analytics.ts', 'app/init/credentials.js', 'app/init/device.js', 'app/init/emm_provider.js', @@ -183,7 +184,6 @@ module.exports = [ 'app/utils/preferences.js', 'app/utils/push_notifications.js', 'app/utils/security.js', - 'app/utils/segment.js', 'app/utils/sentry/index.js', 'app/utils/time_tracker.js', 'app/utils/timezone.js', diff --git a/packager/modulePaths.js b/packager/modulePaths.js index bc5e3ec92..2ce82a1ed 100644 --- a/packager/modulePaths.js +++ b/packager/modulePaths.js @@ -23,6 +23,7 @@ module.exports = [ './node_modules/app/constants/view.js', './node_modules/app/constants/websocket.ts', './node_modules/app/i18n/index.js', + './node_modules/app/init/analytics.ts', './node_modules/app/init/credentials.js', './node_modules/app/init/device.js', './node_modules/app/init/emm_provider.js', @@ -183,7 +184,6 @@ module.exports = [ './node_modules/app/utils/preferences.js', './node_modules/app/utils/push_notifications.js', './node_modules/app/utils/security.js', - './node_modules/app/utils/segment.js', './node_modules/app/utils/sentry/index.js', './node_modules/app/utils/time_tracker.js', './node_modules/app/utils/timezone.js', diff --git a/test/test_helper.js b/test/test_helper.js index 334e709e5..32ec9ab4b 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -4,10 +4,8 @@ import assert from 'assert'; import nock from 'nock'; -import Config from 'assets/config.json'; - +import Config from '@assets/config.json'; import Client from '@mm-redux/client/client4'; - import {DEFAULT_LOCALE} from '@mm-redux/constants/general'; import {generateId} from '@mm-redux/utils/helpers'; diff --git a/tsconfig.json b/tsconfig.json index efb2824c2..3a4cd9a29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,12 +29,15 @@ "noUnusedParameters": false, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, "baseUrl": ".", "paths": { "@actions/*": ["app/actions/*"], + "@assets/*": ["dist/assets/*"], "@constants/*": ["app/constants/*"], "@constants": ["app/constants/index"], "@i18n": ["app/i18n/index"], + "@init/*": ["app/init/*"], "@mm-redux/*": ["app/mm-redux/*"], "@selectors/*": ["app/selectors/*"], "@store/*": ["app/store"],