From e05aa1eb528dd62f937edec3603551cdc6e42259 Mon Sep 17 00:00:00 2001 From: Youssef Muhamad Date: Thu, 30 Jul 2020 10:26:17 -0300 Subject: [PATCH] [NEW] Log events from RoomsList, SideDrawer and Profile (#2190) * Create method to track user event to isolate the logic to improve future refactoring * Track Onboarding view * Track NewServer view * Refactor track method due to firebase already send the current screen * Track default login and all the oAuth options * Track default sign up in RegisterView * Change trackUserEvent signature and update all the files * Track the remaining login services * track add server, change server and search * Track SidebarView and refactor to use react-navigation * Track profile events and handle exceptions * Track create channel flux * Track send message to user via NewMessageView * Track create direct message flux * Handle failure of create channel and group in the saga * Track create discussion flux * Track navigate to directory and its actions * Track read, favorite and hide a channel, handling its errors * Track all channels sorting and grouping * Resolve requests to improve the importing logs and events * Remove unused events file * Leave a bugsnag breadcrumb when logging an event * Move all logEvent to the top of code block and log remaining fail events * Move all the non-logic-dependent logEvent to the top of code block * Improve the logging of sidebar events * Improve events from onboarding and newserver * Improve events from login and register view, and log enter with apple * Improve NewMessageView events * Improve CreateChannel events * Improve CreateDiscussion and SelectedUsers create group events * Improve RoomsList events and log trivial events * Improve ProfileView events * Remove single line function body for the sidebarNavigate * Navigate to Status and AdminPanel View using the defined sidebarNavigate method Co-authored-by: Diego Mello --- app/containers/LoginServices.js | 25 ++-- app/sagas/createChannel.js | 5 + app/sagas/createDiscussion.js | 5 +- app/sagas/login.js | 4 +- app/utils/log/events.js | 128 +++++++++++++++--- app/views/CreateChannelView.js | 13 +- app/views/CreateDiscussionView/index.js | 15 +- app/views/DirectoryView/index.js | 8 +- app/views/ForgotPasswordView.js | 3 + app/views/LoginView.js | 2 - app/views/NewMessageView.js | 6 +- app/views/NewServerView.js | 6 +- app/views/OnboardingView/index.js | 6 +- app/views/ProfileView/index.js | 15 +- app/views/RegisterView.js | 4 +- app/views/RoomsListView/Header/index.js | 2 + app/views/RoomsListView/ServerDropdown.js | 3 + app/views/RoomsListView/SortDropdown/index.js | 8 +- app/views/RoomsListView/index.js | 27 +++- app/views/SelectedUsersView.js | 4 +- app/views/SidebarView/index.js | 8 +- 21 files changed, 232 insertions(+), 65 deletions(-) diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js index 31c8bbaf2..417e6523a 100644 --- a/app/containers/LoginServices.js +++ b/app/containers/LoginServices.js @@ -78,7 +78,7 @@ class LoginServices extends React.PureComponent { } onPressFacebook = () => { - logEvent(events.LOGIN_WITH_FACEBOOK); + logEvent(events.ENTER_WITH_FACEBOOK); const { services, server } = this.props; const { clientId } = services.facebook; const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth'; @@ -90,7 +90,7 @@ class LoginServices extends React.PureComponent { } onPressGithub = () => { - logEvent(events.LOGIN_WITH_GITHUB); + logEvent(events.ENTER_WITH_GITHUB); const { services, server } = this.props; const { clientId } = services.github; const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`; @@ -102,7 +102,7 @@ class LoginServices extends React.PureComponent { } onPressGitlab = () => { - logEvent(events.LOGIN_WITH_GITLAB); + logEvent(events.ENTER_WITH_GITLAB); const { services, server, Gitlab_URL } = this.props; const { clientId } = services.gitlab; const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com'; @@ -115,7 +115,7 @@ class LoginServices extends React.PureComponent { } onPressGoogle = () => { - logEvent(events.LOGIN_WITH_GOOGLE); + logEvent(events.ENTER_WITH_GOOGLE); const { services, server } = this.props; const { clientId } = services.google; const endpoint = 'https://accounts.google.com/o/oauth2/auth'; @@ -127,7 +127,7 @@ class LoginServices extends React.PureComponent { } onPressLinkedin = () => { - logEvent(events.LOGIN_WITH_LINKEDIN); + logEvent(events.ENTER_WITH_LINKEDIN); const { services, server } = this.props; const { clientId } = services.linkedin; const endpoint = 'https://www.linkedin.com/oauth/v2/authorization'; @@ -139,7 +139,7 @@ class LoginServices extends React.PureComponent { } onPressMeteor = () => { - logEvent(events.LOGIN_WITH_METEOR); + logEvent(events.ENTER_WITH_METEOR); const { services, server } = this.props; const { clientId } = services['meteor-developer']; const endpoint = 'https://www.meteor.com/oauth2/authorize'; @@ -150,7 +150,7 @@ class LoginServices extends React.PureComponent { } onPressTwitter = () => { - logEvent(events.LOGIN_WITH_TWITTER); + logEvent(events.ENTER_WITH_TWITTER); const { server } = this.props; const state = this.getOAuthState(); const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`; @@ -158,7 +158,7 @@ class LoginServices extends React.PureComponent { } onPressWordpress = () => { - logEvent(events.LOGIN_WITH_WORDPRESS); + logEvent(events.ENTER_WITH_WORDPRESS); const { services, server } = this.props; const { clientId, serverURL } = services.wordpress; const endpoint = `${ serverURL }/oauth/authorize`; @@ -170,7 +170,7 @@ class LoginServices extends React.PureComponent { } onPressCustomOAuth = (loginService) => { - logEvent(events.LOGIN_WITH_CUSTOM_OAUTH); + logEvent(events.ENTER_WITH_CUSTOM_OAUTH); const { server } = this.props; const { serverURL, authorizePath, clientId, scope, service @@ -185,7 +185,7 @@ class LoginServices extends React.PureComponent { } onPressSaml = (loginService) => { - logEvent(events.LOGIN_WITH_SAML); + logEvent(events.ENTER_WITH_SAML); const { server } = this.props; const { clientConfig } = loginService; const { provider } = clientConfig; @@ -195,7 +195,7 @@ class LoginServices extends React.PureComponent { } onPressCas = () => { - logEvent(events.LOGIN_WITH_CAS); + logEvent(events.ENTER_WITH_CAS); const { server, CAS_login_url } = this.props; const ssoToken = random(17); const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`; @@ -203,6 +203,7 @@ class LoginServices extends React.PureComponent { } onPressAppleLogin = async() => { + logEvent(events.ENTER_WITH_APPLE); try { const { fullName, email, identityToken } = await AppleAuthentication.signInAsync({ requestedScopes: [ @@ -213,7 +214,7 @@ class LoginServices extends React.PureComponent { await RocketChat.loginOAuthOrSso({ fullName, email, identityToken }); } catch { - // Do nothing + logEvent(events.ENTER_WITH_APPLE_F); } } diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js index 062a0092b..2c642006c 100644 --- a/app/sagas/createChannel.js +++ b/app/sagas/createChannel.js @@ -10,6 +10,7 @@ import RocketChat from '../lib/rocketchat'; import Navigation from '../lib/Navigation'; import database from '../lib/database'; import I18n from '../i18n'; +import { logEvent, events } from '../utils/log'; import { goRoom } from '../utils/goRoom'; const createChannel = function createChannel(data) { @@ -29,11 +30,14 @@ const handleRequest = function* handleRequest({ data }) { let sub; if (data.group) { + logEvent(events.SELECTED_USERS_CREATE_GROUP); const result = yield call(createGroupChat); if (result.success) { ({ room: sub } = result); } } else { + const { type, readOnly, broadcast } = data; + logEvent(events.CREATE_CHANNEL_CREATE, { type: type ? 'private' : 'public', readOnly, broadcast }); sub = yield call(createChannel, data); } @@ -52,6 +56,7 @@ const handleRequest = function* handleRequest({ data }) { yield put(createChannelSuccess(sub)); } catch (err) { + logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CREATE_CHANNEL_CREATE_F']); yield put(createChannelFailure(err)); } }; diff --git a/app/sagas/createDiscussion.js b/app/sagas/createDiscussion.js index 1e53e57d4..a185e6671 100644 --- a/app/sagas/createDiscussion.js +++ b/app/sagas/createDiscussion.js @@ -7,12 +7,14 @@ import { CREATE_DISCUSSION, LOGIN } from '../actions/actionsTypes'; import { createDiscussionSuccess, createDiscussionFailure } from '../actions/createDiscussion'; import RocketChat from '../lib/rocketchat'; import database from '../lib/database'; +import { logEvent, events } from '../utils/log'; const create = function* create(data) { return yield RocketChat.createDiscussion(data); }; const handleRequest = function* handleRequest({ data }) { + logEvent(events.CREATE_DISCUSSION_CREATE); try { const auth = yield select(state => state.login.isAuthenticated); if (!auth) { @@ -35,12 +37,13 @@ const handleRequest = function* handleRequest({ data }) { } catch { // do nothing } - yield put(createDiscussionSuccess(sub)); } else { + logEvent(events.CREATE_DISCUSSION_CREATE_F); yield put(createDiscussionFailure(result)); } } catch (err) { + logEvent(events.CREATE_DISCUSSION_CREATE_F); yield put(createDiscussionFailure(err)); } }; diff --git a/app/sagas/login.js b/app/sagas/login.js index 199952190..749e1d18a 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -32,7 +32,7 @@ const loginCall = args => RocketChat.login(args); const logoutCall = args => RocketChat.logout(args); const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false }) { - logEvent(events.DEFAULT_LOGIN); + logEvent(events.LOGIN_DEFAULT_LOGIN); try { let result; if (credentials.resume) { @@ -53,7 +53,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE if (logoutOnError && (e.data && e.data.message && /you've been logged out by the server/i.test(e.data.message))) { yield put(logout(true)); } else { - logEvent(events.DEFAULT_LOGIN_FAIL); + logEvent(events.LOGIN_DEFAULT_LOGIN_F); yield put(loginFailure(e)); } } diff --git a/app/utils/log/events.js b/app/utils/log/events.js index 01297cd2a..6c5f16e67 100644 --- a/app/utils/log/events.js +++ b/app/utils/log/events.js @@ -1,24 +1,108 @@ export default { - JOIN_A_WORKSPACE: 'join_a_workspace', - CREATE_NEW_WORKSPACE: 'create_new_workspace', - CREATE_NEW_WORKSPACE_FAIL: 'create_new_workspace_fail', - CONNECT_TO_WORKSPACE: 'connect_to_workspace', - CONNECT_TO_WORKSPACE_FAIL: 'connect_to_workspace_fail', - JOIN_OPEN_WORKSPACE: 'join_open_workspace', - DEFAULT_LOGIN: 'default_login', - DEFAULT_LOGIN_FAIL: 'default_login_fail', - DEFAULT_SIGN_UP: 'default_sign_up', - DEFAULT_SIGN_UP_FAIL: 'default_sign_up_fail', - FORGOT_PASSWORD: 'forgot_password', - LOGIN_WITH_FACEBOOK: 'login_with_facebook', - LOGIN_WITH_GITHUB: 'login_with_github', - LOGIN_WITH_GITLAB: 'login_with_gitlab', - LOGIN_WITH_LINKEDIN: 'login_with_linkedin', - LOGIN_WITH_GOOGLE: 'login_with_google', - LOGIN_WITH_METEOR: 'login_with_meteor', - LOGIN_WITH_TWITTER: 'login_with_twitter', - LOGIN_WITH_WORDPRESS: 'login_with_wordpress', - LOGIN_WITH_CUSTOM_OAUTH: 'login_with_custom_oauth', - LOGIN_WITH_SAML: 'login_with_saml', - LOGIN_WITH_CAS: 'login_with_cas' + // ONBOARDING VIEW + ONBOARD_JOIN_A_WORKSPACE: 'onboard_join_a_workspace', + ONBOARD_CREATE_NEW_WORKSPACE: 'onboard_create_new_workspace', + ONBOARD_CREATE_NEW_WORKSPACE_F: 'onboard_create_new_workspace_f', + + // NEW SERVER VIEW + NEWSERVER_CONNECT_TO_WORKSPACE: 'newserver_connect_to_workspace', + NEWSERVER_CONNECT_TO_WORKSPACE_F: 'newserver_connect_to_workspace_f', + NEWSERVER_JOIN_OPEN_WORKSPACE: 'newserver_join_open_workspace', + + // LOGIN VIEW + LOGIN_DEFAULT_LOGIN: 'login_default_login', + LOGIN_DEFAULT_LOGIN_F: 'login_default_login_f', + + // FORGOT PASSWORD VIEW + FP_FORGOT_PASSWORD: 'fp_forgot_password', + FP_FORGOT_PASSWORD_F: 'fp_forgot_password_f', + + // REGISTER VIEW + REGISTER_DEFAULT_SIGN_UP: 'register_default_sign_up', + REGISTER_DEFAULT_SIGN_UP_F: 'register_default_sign_up_f', + + // LOGIN AND REGISTER VIEW + ENTER_WITH_FACEBOOK: 'enter_with_facebook', + ENTER_WITH_GITHUB: 'enter_with_github', + ENTER_WITH_GITLAB: 'enter_with_gitlab', + ENTER_WITH_LINKEDIN: 'enter_with_linkedin', + ENTER_WITH_GOOGLE: 'enter_with_google', + ENTER_WITH_METEOR: 'enter_with_meteor', + ENTER_WITH_TWITTER: 'enter_with_twitter', + ENTER_WITH_WORDPRESS: 'enter_with_wordpress', + ENTER_WITH_CUSTOM_OAUTH: 'enter_with_custom_oauth', + ENTER_WITH_SAML: 'enter_with_saml', + ENTER_WITH_CAS: 'enter_with_cas', + ENTER_WITH_APPLE: 'enter_with_apple', + ENTER_WITH_APPLE_F: 'enter_with_apple_f', + + // SIDEBAR VIEW + SIDEBAR_NAVIGATE_TO_STATUS: 'sidebar_navigate_to_status', + SIDEBAR_NAVIGATE_TO_CHATS: 'sidebar_navigate_to_chats', + SIDEBAR_NAVIGATE_TO_PROFILE: 'sidebar_navigate_to_profile', + SIDEBAR_NAVIGATE_TO_SETTINGS: 'sidebar_navigate_to_settings', + SIDEBAR_NAVIGATE_TO_ADMINPANEL: 'sidebar_navigate_to_admin_panel', + + // ROOMS LIST VIEW + RL_TOGGLE_SERVER_DROPDOWN: 'rl_toggle_server_dropdown', + RL_ADD_SERVER: 'rl_add_server', + RL_CHANGE_SERVER: 'rl_change_server', + RL_NAVIGATE_TO_NEW_MSG: 'rl_navigate_to_new_msg', + RL_SEARCH: 'rl_search', + RL_NAVIGATE_TO_DIRECTORY: 'rl_navigate_to_directory', + RL_GO_TO_ROOM: 'rl_go_to_room', + RL_FAVORITE_CHANNEL: 'rl_favorite_channel', + RL_UNFAVORITE_CHANNEL: 'rl_unfavorite_channel', + RL_TOGGLE_FAVORITE_F: 'rl_toggle_favorite_f', + RL_READ_CHANNEL: 'rl_read_channel', + RL_UNREAD_CHANNEL: 'rl_unread_channel', + RL_TOGGLE_READ_F: 'rl_toggle_read_f', + RL_HIDE_CHANNEL: 'rl_hide_channel', + RL_HIDE_CHANNEL_F: 'rl_hide_channel_f', + RL_TOGGLE_SORT_DROPDOWN: 'rl_toggle_sort_dropdown', + RL_SORT_CHANNELS_BY_NAME: 'rl_sort_channels_by_name', + RL_SORT_CHANNELS_BY_ACTIVITY: 'rl_sort_channels_by_activity', + RL_SORT_CHANNELS_FAIL: 'rl_sort_channels_fail', + RL_GROUP_CHANNELS_BY_TYPE: 'rl_group_channels_by_type', + RL_GROUP_CHANNELS_BY_FAVORITE: 'rl_group_channels_by_favorite', + RL_GROUP_CHANNELS_BY_UNREAD: 'rl_group_channels_by_unread', + + // DIRECTORY VIEW + DIRECTORY_SEARCH_USERS: 'directory_search_users', + DIRECTORY_SEARCH_CHANNELS: 'directory_search_channels', + + // NEW MESSAGE VIEW + NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel', + NEW_MSG_CREATE_GROUP_CHAT: 'new_msg_create_group_chat', + NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion', + NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user', + + // SELECTED USERS VIEW + SELECTED_USERS_ADD_USER: 'selected_users_add_user', + SELECTED_USERS_REMOVE_USER: 'selected_users_remove_user', + SELECTED_USERS_CREATE_GROUP: 'selected_users_create_group', + SELECTED_USERS_CREATE_GROUP_F: 'selected_users_create_group_f', + + // CREATE CHANNEL VIEW + CREATE_CHANNEL_CREATE: 'create_channel_create', + CREATE_CHANNEL_CREATE_F: 'create_channel_create_f', + CREATE_CHANNEL_TOGGLE_TYPE: 'create_channel_toggle_type', + CREATE_CHANNEL_TOGGLE_READ_ONLY: 'create_channel_toggle_read_only', + CREATE_CHANNEL_TOGGLE_BROADCAST: 'create_channel_toggle_broadcast', + CREATE_CHANNEL_REMOVE_USER: 'create_channel_remove_user', + + // CREATE DISCUSSION VIEW + CREATE_DISCUSSION_CREATE: 'create_discussion_create', + CREATE_DISCUSSION_CREATE_F: 'create_discussion_create_f', + CREATE_DISCUSSION_SELECT_CHANNEL: 'create_discussion_select_channel', + CREATE_DISCUSSION_SELECT_USERS: 'create_discussion_select_users', + + // PROFILE VIEW + PROFILE_PICK_AVATAR: 'profile_pick_avatar', + PROFILE_PICK_AVATAR_F: 'profile_pick_avatar_f', + PROFILE_PICK_AVATAR_WITH_URL: 'profile_pick_avatar_with_url', + PROFILE_SAVE_AVATAR: 'profile_save_avatar', + PROFILE_SAVE_AVATAR_F: 'profile_save_avatar_f', + PROFILE_SAVE_CHANGES: 'profile_save_changes', + PROFILE_SAVE_CHANGES_F: 'profile_save_changes_f' }; diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 09e32eb6b..5d0dae5bc 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -21,6 +21,7 @@ import { SWITCH_TRACK_COLOR, themes } from '../constants/colors'; import { withTheme } from '../theme'; import { Review } from '../utils/review'; import { getUserSelector } from '../selectors/login'; +import { logEvent, events } from '../utils/log'; import SafeAreaView from '../containers/SafeAreaView'; const styles = StyleSheet.create({ @@ -166,6 +167,7 @@ class CreateChannelView extends React.Component { } removeUser = (user) => { + logEvent(events.CREATE_CHANNEL_REMOVE_USER); const { removeUser } = this.props; removeUser(user); } @@ -194,7 +196,10 @@ class CreateChannelView extends React.Component { id: 'type', value: type, label: 'Private_Channel', - onValueChange: value => this.setState({ type: value }) + onValueChange: (value) => { + logEvent(events.CREATE_CHANNEL_TOGGLE_TYPE); + this.setState({ type: value }); + } }); } @@ -204,7 +209,10 @@ class CreateChannelView extends React.Component { id: 'readonly', value: readOnly, label: 'Read_Only_Channel', - onValueChange: value => this.setState({ readOnly: value }), + onValueChange: (value) => { + logEvent(events.CREATE_CHANNEL_TOGGLE_READ_ONLY); + this.setState({ readOnly: value }); + }, disabled: broadcast }); } @@ -216,6 +224,7 @@ class CreateChannelView extends React.Component { value: broadcast, label: 'Broadcast_Channel', onValueChange: (value) => { + logEvent(events.CREATE_CHANNEL_TOGGLE_BROADCAST); this.setState({ broadcast: value, readOnly: value ? true : readOnly diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js index 206a70203..7fe6e6977 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.js @@ -25,6 +25,7 @@ import SelectUsers from './SelectUsers'; import styles from './styles'; import SafeAreaView from '../../containers/SafeAreaView'; import { goRoom } from '../../utils/goRoom'; +import { logEvent, events } from '../../utils/log'; class CreateChannelView extends React.Component { propTypes = { @@ -129,6 +130,16 @@ class CreateChannelView extends React.Component { ); }; + selectChannel = ({ value }) => { + logEvent(events.CREATE_DISCUSSION_SELECT_CHANNEL); + this.setState({ channel: { rid: value } }); + } + + selectUsers = ({ value }) => { + logEvent(events.CREATE_DISCUSSION_SELECT_USERS); + this.setState({ users: value }); + } + render() { const { name, users } = this.state; const { @@ -149,7 +160,7 @@ class CreateChannelView extends React.Component { userId={user.id} token={user.token} initial={this.channel && { text: RocketChat.getRoomTitle(this.channel) }} - onChannelSelect={({ value }) => this.setState({ channel: { rid: value } })} + onChannelSelect={this.selectChannel} theme={theme} /> this.setState({ users: value })} + onUserSelect={this.selectUsers} theme={theme} /> { this.setState({ type, data: [] }, () => this.search()); + + if (type === 'users') { + logEvent(events.DIRECTORY_SEARCH_USERS); + } else if (type === 'channels') { + logEvent(events.DIRECTORY_SEARCH_CHANNELS); + } } toggleWorkspace = () => { diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js index d233c0578..319682c75 100644 --- a/app/views/ForgotPasswordView.js +++ b/app/views/ForgotPasswordView.js @@ -12,6 +12,7 @@ import RocketChat from '../lib/rocketchat'; import { withTheme } from '../theme'; import { themes } from '../constants/colors'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; +import { logEvent, events } from '../utils/log'; class ForgotPasswordView extends React.Component { static navigationOptions = ({ route }) => ({ @@ -56,6 +57,7 @@ class ForgotPasswordView extends React.Component { } resetPassword = async() => { + logEvent(events.FP_FORGOT_PASSWORD); const { email, invalidEmail } = this.state; if (invalidEmail || !email) { return; @@ -69,6 +71,7 @@ class ForgotPasswordView extends React.Component { showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert')); } } catch (e) { + logEvent(events.FP_FORGOT_PASSWORD_F); const msg = (e.data && e.data.error) || I18n.t('There_was_an_error_while_action', { action: I18n.t('resetting_password') }); showErrorAlert(msg, I18n.t('Alert')); } diff --git a/app/views/LoginView.js b/app/views/LoginView.js index 9e566b5be..95d8a90d3 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -6,7 +6,6 @@ import { import { connect } from 'react-redux'; import equal from 'deep-equal'; -import { logEvent, events } from '../utils/log'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import I18n from '../i18n'; @@ -103,7 +102,6 @@ class LoginView extends React.Component { } forgotPassword = () => { - logEvent(events.FORGOT_PASSWORD); const { navigation, Site_Name } = this.props; navigation.navigate('ForgotPasswordView', { title: Site_Name }); } diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js index 5d7cdef01..9a1042900 100644 --- a/app/views/NewMessageView.js +++ b/app/views/NewMessageView.js @@ -14,7 +14,7 @@ import RocketChat from '../lib/rocketchat'; import UserItem from '../presentation/UserItem'; import sharedStyles from './Styles'; import I18n from '../i18n'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import SearchBox from '../containers/SearchBox'; import { CustomIcon } from '../lib/Icons'; import { CloseModalButton } from '../containers/HeaderButton'; @@ -133,11 +133,13 @@ class NewMessageView extends React.Component { } createChannel = () => { + logEvent(events.NEW_MSG_CREATE_CHANNEL); const { navigation } = this.props; navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') }); } createGroupChat = () => { + logEvent(events.NEW_MSG_CREATE_GROUP_CHAT); const { createChannel, maxUsers, navigation } = this.props; navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => createChannel({ group: true }), @@ -147,6 +149,7 @@ class NewMessageView extends React.Component { } goRoom = (item) => { + logEvent(events.NEW_MSG_CHAT_WITH_USER); const { isMasterDetail, navigation } = this.props; if (isMasterDetail) { navigation.pop(); @@ -174,6 +177,7 @@ class NewMessageView extends React.Component { } createDiscussion = () => { + logEvent(events.NEW_MSG_CREATE_DISCUSSION); Navigation.navigate('CreateDiscussionView'); } diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js index 5d2726cdd..fd7009161 100644 --- a/app/views/NewServerView.js +++ b/app/views/NewServerView.js @@ -139,7 +139,7 @@ class NewServerView extends React.Component { } submit = async() => { - logEvent(events.CONNECT_TO_WORKSPACE); + logEvent(events.NEWSERVER_CONNECT_TO_WORKSPACE); const { text, certificate } = this.state; const { connectServer } = this.props; let cert = null; @@ -151,7 +151,7 @@ class NewServerView extends React.Component { try { await FileSystem.copyAsync({ from: certificate.path, to: certificatePath }); } catch (e) { - logEvent(events.CONNECT_TO_WORKSPACE_FAIL); + logEvent(events.NEWSERVER_CONNECT_TO_WORKSPACE_F); log(e); } cert = { @@ -169,7 +169,7 @@ class NewServerView extends React.Component { } connectOpen = () => { - logEvent(events.JOIN_OPEN_WORKSPACE); + logEvent(events.NEWSERVER_JOIN_OPEN_WORKSPACE); this.setState({ connectingOpen: true }); const { connectServer } = this.props; connectServer('https://open.rocket.chat'); diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js index 0d509e0d4..5be0446c1 100644 --- a/app/views/OnboardingView/index.js +++ b/app/views/OnboardingView/index.js @@ -70,17 +70,17 @@ class OnboardingView extends React.Component { } connectServer = () => { - logEvent(events.JOIN_A_WORKSPACE); + logEvent(events.ONBOARD_JOIN_A_WORKSPACE); const { navigation } = this.props; navigation.navigate('NewServerView'); } createWorkspace = async() => { - logEvent(events.CREATE_NEW_WORKSPACE); + logEvent(events.ONBOARD_CREATE_NEW_WORKSPACE); try { await Linking.openURL('https://cloud.rocket.chat/trial'); } catch { - logEvent(events.CREATE_NEW_WORKSPACE_FAIL); + logEvent(events.ONBOARD_CREATE_NEW_WORKSPACE_F); } } diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js index f6155c2a2..8d289935b 100644 --- a/app/views/ProfileView/index.js +++ b/app/views/ProfileView/index.js @@ -18,7 +18,7 @@ import { LISTENER } from '../../containers/Toast'; import EventEmitter from '../../utils/events'; import RocketChat from '../../lib/rocketchat'; import RCTextInput from '../../containers/TextInput'; -import log from '../../utils/log'; +import log, { logEvent, events } from '../../utils/log'; import I18n from '../../i18n'; import Button from '../../containers/Button'; import Avatar from '../../containers/Avatar'; @@ -223,8 +223,10 @@ class ProfileView extends React.Component { try { if (avatar.url) { try { + logEvent(events.PROFILE_SAVE_AVATAR); await RocketChat.setAvatarFromService(avatar); } catch (e) { + logEvent(events.PROFILE_SAVE_AVATAR_F); this.setState({ saving: false, currentPassword: null }); return this.handleError(e, 'setAvatarFromService', 'changing_avatar'); } @@ -233,6 +235,7 @@ class ProfileView extends React.Component { const result = await RocketChat.saveUserProfile(params, customFields); if (result.success) { + logEvent(events.PROFILE_SAVE_CHANGES); if (customFields) { setUser({ customFields, ...params }); } else { @@ -243,6 +246,7 @@ class ProfileView extends React.Component { } this.setState({ saving: false }); } catch (e) { + logEvent(events.PROFILE_SAVE_CHANGES_F); this.setState({ saving: false, currentPassword: null }); this.handleError(e, 'saveUserProfile', 'saving_profile'); } @@ -281,13 +285,20 @@ class ProfileView extends React.Component { includeBase64: true }; try { + logEvent(events.PROFILE_PICK_AVATAR); const response = await ImagePicker.openPicker(options); this.setAvatar({ url: response.path, data: `data:image/jpeg;base64,${ response.data }`, service: 'upload' }); } catch (error) { + logEvent(events.PROFILE_PICK_AVATAR_F); console.warn(error); } } + pickImageWithURL = (avatarUrl) => { + logEvent(events.PROFILE_PICK_AVATAR_WITH_URL); + this.setAvatar({ url: avatarUrl, data: avatarUrl, service: 'url' }); + } + renderAvatarButton = ({ key, child, onPress, disabled = false }) => { @@ -331,7 +342,7 @@ class ProfileView extends React.Component { })} {this.renderAvatarButton({ child: , - onPress: () => this.setAvatar({ url: avatarUrl, data: avatarUrl, service: 'url' }), + onPress: () => this.pickImageWithURL(avatarUrl), disabled: !avatarUrl, key: 'profile-view-avatar-url-button' })} diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js index d95fb7938..23711a7cd 100644 --- a/app/views/RegisterView.js +++ b/app/views/RegisterView.js @@ -114,7 +114,7 @@ class RegisterView extends React.Component { } submit = async() => { - logEvent(events.DEFAULT_SIGN_UP); + logEvent(events.REGISTER_DEFAULT_SIGN_UP); if (!this.valid()) { return; } @@ -150,7 +150,7 @@ class RegisterView extends React.Component { return loginRequest({ user: email, password }); } if (e.data?.error) { - logEvent(events.DEFAULT_SIGN_UP_FAIL); + logEvent(events.REGISTER_DEFAULT_SIGN_UP_F); showErrorAlert(e.data.error, I18n.t('Oops')); } } diff --git a/app/views/RoomsListView/Header/index.js b/app/views/RoomsListView/Header/index.js index 12a57dc00..97244fd5b 100644 --- a/app/views/RoomsListView/Header/index.js +++ b/app/views/RoomsListView/Header/index.js @@ -10,6 +10,7 @@ import { withTheme } from '../../../theme'; import EventEmitter from '../../../utils/events'; import { KEY_COMMAND, handleCommandOpenServerDropdown } from '../../../commands'; import { isTablet } from '../../../utils/deviceInfo'; +import { logEvent, events } from '../../../utils/log'; class RoomsListHeaderView extends PureComponent { static propTypes = { @@ -53,6 +54,7 @@ class RoomsListHeaderView extends PureComponent { } onPress = () => { + logEvent(events.RL_TOGGLE_SERVER_DROPDOWN); const { showServerDropdown, showSortDropdown, close, open, closeSort } = this.props; diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js index 64ae0b231..afeb15eea 100644 --- a/app/views/RoomsListView/ServerDropdown.js +++ b/app/views/RoomsListView/ServerDropdown.js @@ -23,6 +23,7 @@ import { KEY_COMMAND, handleCommandSelectServer } from '../../commands'; import { isTablet, isIOS } from '../../utils/deviceInfo'; import { localAuthenticate } from '../../utils/localAuthentication'; import { showConfirmationAlert } from '../../utils/info'; +import { logEvent, events } from '../../utils/log'; import { headerHeight } from '../../containers/Header'; import { goRoom } from '../../utils/goRoom'; @@ -134,6 +135,7 @@ class ServerDropdown extends Component { } addServer = () => { + logEvent(events.RL_ADD_SERVER); const { server } = this.props; this.close(); setTimeout(() => { @@ -147,6 +149,7 @@ class ServerDropdown extends Component { } = this.props; this.close(); if (currentServer !== server) { + logEvent(events.RL_CHANGE_SERVER); const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`); if (isMasterDetail) { goRoom({ item: {}, isMasterDetail }); diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js index b5247692e..75cc2cec8 100644 --- a/app/views/RoomsListView/SortDropdown/index.js +++ b/app/views/RoomsListView/SortDropdown/index.js @@ -10,7 +10,7 @@ import styles from '../styles'; import Touch from '../../../utils/touch'; import RocketChat from '../../../lib/rocketchat'; import { setPreference } from '../../../actions/sortPreferences'; -import log from '../../../utils/log'; +import log, { logEvent, events } from '../../../utils/log'; import I18n from '../../../i18n'; import { CustomIcon } from '../../../lib/Icons'; import { withTheme } from '../../../theme'; @@ -65,31 +65,37 @@ class Sort extends PureComponent { setSortPreference(param); RocketChat.saveSortPreference(param); } catch (e) { + logEvent(events.RL_SORT_CHANNELS_FAIL); log(e); } } sortByName = () => { + logEvent(events.RL_SORT_CHANNELS_BY_NAME); this.setSortPreference({ sortBy: 'alphabetical' }); this.close(); } sortByActivity = () => { + logEvent(events.RL_SORT_CHANNELS_BY_ACTIVITY); this.setSortPreference({ sortBy: 'activity' }); this.close(); } toggleGroupByType = () => { + logEvent(events.RL_GROUP_CHANNELS_BY_TYPE); const { groupByType } = this.props; this.setSortPreference({ groupByType: !groupByType }); } toggleGroupByFavorites = () => { + logEvent(events.RL_GROUP_CHANNELS_BY_FAVORITE); const { showFavorites } = this.props; this.setSortPreference({ showFavorites: !showFavorites }); } toggleUnread = () => { + logEvent(events.RL_GROUP_CHANNELS_BY_UNREAD); const { showUnread } = this.props; this.setSortPreference({ showUnread: !showUnread }); } diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index e095d647b..617b0fd51 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -18,7 +18,7 @@ import database from '../../lib/database'; import RocketChat from '../../lib/rocketchat'; import RoomItem, { ROW_HEIGHT } from '../../presentation/RoomItem'; import styles from './styles'; -import log from '../../utils/log'; +import log, { logEvent, events } from '../../utils/log'; import I18n from '../../i18n'; import SortDropdown from './SortDropdown'; import ServerDropdown from './ServerDropdown'; @@ -357,9 +357,7 @@ class RoomsListView extends React.Component { navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' }) - : () => navigation.navigate('NewMessageStackNavigator')} + onPress={this.goToNewMessage} testID='rooms-list-view-create-channel' /> { + logEvent(events.RL_SEARCH); const { openSearchHeader } = this.props; this.internalSetState({ searching: true }, () => { openSearchHeader(); @@ -583,6 +582,7 @@ class RoomsListView extends React.Component { } toggleSort = () => { + logEvent(events.RL_TOGGLE_SORT_DROPDOWN); const { toggleSortDropdown } = this.props; this.scrollToTop(); @@ -592,6 +592,7 @@ class RoomsListView extends React.Component { }; toggleFav = async(rid, favorite) => { + logEvent(favorite ? events.RL_UNFAVORITE_CHANNEL : events.RL_FAVORITE_CHANNEL); try { const db = database.active; const result = await RocketChat.toggleFavorite(rid, !favorite); @@ -609,11 +610,13 @@ class RoomsListView extends React.Component { }); } } catch (e) { + logEvent(events.RL_TOGGLE_FAVORITE_FAIL); log(e); } }; toggleRead = async(rid, isRead) => { + logEvent(isRead ? events.RL_UNREAD_CHANNEL : events.RL_READ_CHANNEL); try { const db = database.active; const result = await RocketChat.toggleRead(isRead, rid); @@ -631,11 +634,13 @@ class RoomsListView extends React.Component { }); } } catch (e) { + logEvent(events.RL_TOGGLE_READ_F); log(e); } }; hideChannel = async(rid, type) => { + logEvent(events.RL_HIDE_CHANNEL); try { const db = database.active; const result = await RocketChat.hideRoom(rid, type); @@ -651,11 +656,13 @@ class RoomsListView extends React.Component { }); } } catch (e) { + logEvent(events.RL_HIDE_CHANNEL_F); log(e); } }; goDirectory = () => { + logEvent(events.RL_NAVIGATE_TO_DIRECTORY); const { navigation, isMasterDetail } = this.props; if (isMasterDetail) { navigation.navigate('ModalStackNavigator', { screen: 'DirectoryView' }); @@ -665,6 +672,7 @@ class RoomsListView extends React.Component { }; goRoom = ({ item, isMasterDetail }) => { + logEvent(events.RL_GO_TO_ROOM); const { item: currentItem } = this.state; const { rooms } = this.props; if (currentItem?.rid === item.rid || rooms?.includes(item.rid)) { @@ -724,6 +732,17 @@ class RoomsListView extends React.Component { } } + goToNewMessage = () => { + logEvent(events.RL_NAVIGATE_TO_NEW_MSG); + const { navigation, isMasterDetail } = this.props; + + if (isMasterDetail) { + navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' }); + } else { + navigation.navigate('NewMessageStackNavigator'); + } + } + handleCommands = ({ event }) => { const { navigation, server, isMasterDetail } = this.props; const { input } = event; diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js index eb8c7bc8e..2856f0331 100644 --- a/app/views/SelectedUsersView.js +++ b/app/views/SelectedUsersView.js @@ -11,7 +11,7 @@ import RocketChat from '../lib/rocketchat'; import UserItem from '../presentation/UserItem'; import Loading from '../containers/Loading'; import I18n from '../i18n'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import SearchBox from '../containers/SearchBox'; import sharedStyles from './Styles'; import { Item, CustomHeaderButtons } from '../containers/HeaderButton'; @@ -184,8 +184,10 @@ class SelectedUsersView extends React.Component { return showErrorAlert(I18n.t('Max_number_of_users_allowed_is_number', { maxUsers }), I18n.t('Oops')); } + logEvent(events.SELECTED_USERS_ADD_USER); addUser(user); } else { + logEvent(events.SELECTED_USERS_REMOVE_USER); removeUser(user); } } diff --git a/app/views/SidebarView/index.js b/app/views/SidebarView/index.js index b3c684e50..a07405148 100644 --- a/app/views/SidebarView/index.js +++ b/app/views/SidebarView/index.js @@ -8,7 +8,7 @@ import { Q } from '@nozbe/watermelondb'; import Avatar from '../../containers/Avatar'; import Status from '../../containers/Status/Status'; -import log from '../../utils/log'; +import log, { logEvent, events } from '../../utils/log'; import I18n from '../../i18n'; import scrollPersistTaps from '../../utils/scrollPersistTaps'; import { CustomIcon } from '../../lib/Icons'; @@ -18,7 +18,6 @@ import { themes } from '../../constants/colors'; import database from '../../lib/database'; import { withTheme } from '../../theme'; import { getUserSelector } from '../../selectors/login'; -import Navigation from '../../lib/Navigation'; import SafeAreaView from '../../containers/SafeAreaView'; const Separator = React.memo(({ theme }) => ); @@ -135,6 +134,7 @@ class Sidebar extends Component { } sidebarNavigate = (route) => { + logEvent(events[`SIDEBAR_NAVIGATE_TO_${ route.replace('StackNavigator', '').replace('View', '').toUpperCase() }`]); const { navigation } = this.props; navigation.navigate(route); } @@ -165,7 +165,7 @@ class Sidebar extends Component { } - onPress={() => Navigation.navigate(routeName)} + onPress={() => this.sidebarNavigate(routeName)} testID='sidebar-settings' current={this.currentItemKey === routeName} /> @@ -210,7 +210,7 @@ class Sidebar extends Component { text={user.statusText || I18n.t('Edit_Status')} left={} right={} - onPress={() => Navigation.navigate('StatusView')} + onPress={() => this.sidebarNavigate('StatusView')} testID='sidebar-custom-status' /> );