From 06730814659506d9999d98bb39f0fad277f617b1 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 28 Jan 2020 10:22:35 -0300 Subject: [PATCH] [NEW] Invite links (#1534) --- app/actions/actionsTypes.js | 8 ++ app/actions/inviteLinks.js | 55 ++++++++ app/constants/settings.js | 3 + app/i18n/locales/de.js | 2 +- app/i18n/locales/en.js | 17 ++- app/i18n/locales/fr.js | 2 +- app/i18n/locales/pt-BR.js | 18 ++- app/i18n/locales/pt-PT.js | 2 +- app/i18n/locales/ru.js | 2 +- app/i18n/locales/zh-CN.js | 2 +- app/index.js | 8 +- app/lib/rocketchat.js | 17 +++ app/reducers/index.js | 4 +- app/reducers/inviteLinks.js | 37 ++++++ app/sagas/deepLinking.js | 18 ++- app/sagas/index.js | 4 +- app/sagas/inviteLinks.js | 72 +++++++++++ app/sagas/login.js | 13 +- app/views/InviteUsersEditView/index.js | 160 ++++++++++++++++++++++++ app/views/InviteUsersEditView/styles.js | 45 +++++++ app/views/InviteUsersView/index.js | 151 ++++++++++++++++++++++ app/views/InviteUsersView/styles.js | 16 +++ app/views/RoomActionsView/index.js | 35 ++++-- app/views/RoomsListView/index.js | 6 +- 24 files changed, 671 insertions(+), 26 deletions(-) create mode 100644 app/actions/inviteLinks.js create mode 100644 app/reducers/inviteLinks.js create mode 100644 app/sagas/inviteLinks.js create mode 100644 app/views/InviteUsersEditView/index.js create mode 100644 app/views/InviteUsersEditView/styles.js create mode 100644 app/views/InviteUsersView/index.js create mode 100644 app/views/InviteUsersView/styles.js diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index ced5b25e..15718b17 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -54,3 +54,11 @@ export const TOGGLE_CRASH_REPORT = 'TOGGLE_CRASH_REPORT'; export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS'; export const SET_ACTIVE_USERS = 'SET_ACTIVE_USERS'; export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']); +export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [ + 'SET_TOKEN', + 'SET_PARAMS', + 'SET_INVITE', + 'CREATE', + 'CLEAR', + ...defaultTypes +]); diff --git a/app/actions/inviteLinks.js b/app/actions/inviteLinks.js new file mode 100644 index 00000000..b456603c --- /dev/null +++ b/app/actions/inviteLinks.js @@ -0,0 +1,55 @@ +import * as types from './actionsTypes'; + +export function inviteLinksSetToken(token) { + return { + type: types.INVITE_LINKS.SET_TOKEN, + token + }; +} + +export function inviteLinksRequest(token) { + return { + type: types.INVITE_LINKS.REQUEST, + token + }; +} + +export function inviteLinksSuccess() { + return { + type: types.INVITE_LINKS.SUCCESS + }; +} + +export function inviteLinksFailure() { + return { + type: types.INVITE_LINKS.FAILURE + }; +} + +export function inviteLinksClear() { + return { + type: types.INVITE_LINKS.CLEAR + }; +} + + +export function inviteLinksCreate(rid) { + return { + type: types.INVITE_LINKS.CREATE, + rid + }; +} + +export function inviteLinksSetParams(params) { + return { + type: types.INVITE_LINKS.SET_PARAMS, + params + }; +} + +export function inviteLinksSetInvite(invite) { + return { + type: types.INVITE_LINKS.SET_INVITE, + invite + }; +} diff --git a/app/constants/settings.js b/app/constants/settings.js index d4697a70..74c6234f 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -59,6 +59,9 @@ export default { Message_TimeFormat: { type: 'valueAsString' }, + Message_TimeAndDateFormat: { + type: 'valueAsString' + }, Site_Name: { type: 'valueAsString' }, diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js index f55fa165..f60a45b6 100644 --- a/app/i18n/locales/de.js +++ b/app/i18n/locales/de.js @@ -80,7 +80,7 @@ export default { Activity: 'Aktivität', Add_Reaction: 'Reaktion hinzufügen', Add_Server: 'Server hinzufügen', - Add_user: 'Nutzer hinzufügen', + Add_users: 'Nutzer hinzufügen', Admin_Panel: 'Admin Panel', Alert: 'Warnen', alert: 'warnen', diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index ebe96061..630b613c 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -81,7 +81,7 @@ export default { Activity: 'Activity', Add_Reaction: 'Add Reaction', Add_Server: 'Add Server', - Add_user: 'Add user', + Add_users: 'Add users', Admin_Panel: 'Admin Panel', Alert: 'Alert', alert: 'alert', @@ -121,6 +121,7 @@ export default { Cancel: 'Cancel', changing_avatar: 'changing avatar', creating_channel: 'creating channel', + creating_invite: 'creating invite', Channel_Name: 'Channel Name', Channels: 'Channels', Chats: 'Chats', @@ -172,6 +173,7 @@ export default { edit: 'edit', edited: 'edited', Edit: 'Edit', + Edit_Invite: 'Edit Invite', Email_or_password_field_is_empty: 'Email or password field is empty', Email: 'Email', EMAIL: 'EMAIL', @@ -182,6 +184,7 @@ export default { Everyone_can_access_this_channel: 'Everyone can access this channel', erasing_room: 'erasing room', Error_uploading: 'Error uploading', + Expiration_Days: 'Expiration (Days)', Favorite: 'Favorite', Favorites: 'Favorites', Files: 'Files', @@ -195,6 +198,7 @@ export default { Forgot_password: 'Forgot password', Forgot_Password: 'Forgot Password', Full_table: 'Click to see full table', + Generate_New_Link: 'Generate New Link', Group_by_favorites: 'Group favorites', Group_by_type: 'Group by type', Hide: 'Hide', @@ -208,7 +212,10 @@ export default { is_a_valid_RocketChat_instance: 'is a valid Rocket.Chat instance', is_not_a_valid_RocketChat_instance: 'is not a valid Rocket.Chat instance', is_typing: 'is typing', + Invalid_or_expired_invite_token: 'Invalid or expired invite token', Invalid_server_version: 'The server you\'re trying to connect is using a version that\'s not supported by the app anymore: {{currentVersion}}.\n\nWe require version {{minVersion}}', + Invite_Link: 'Invite Link', + Invite_users: 'Invite users', Join_the_community: 'Join the community', Join: 'Join', Just_invited_people_can_access_this_channel: 'Just invited people can access this channel', @@ -225,6 +232,7 @@ export default { Login_error: 'Your credentials were rejected! Please try again.', Login_with: 'Login with', Logout: 'Logout', + Max_number_of_uses: 'Max number of uses', members: 'members', Members: 'Members', Mentioned_Messages: 'Mentioned Messages', @@ -247,11 +255,13 @@ export default { N_users: '{{n}} users', name: 'name', Name: 'Name', + Never: 'Never', New_Message: 'New Message', New_Password: 'New Password', New_Server: 'New Server', Next: 'Next', No_files: 'No files', + No_limit: 'No limit', No_mentioned_messages: 'No mentioned messages', No_pinned_messages: 'No pinned messages', No_results_found: 'No results found', @@ -362,6 +372,7 @@ export default { Settings: 'Settings', Settings_succesfully_changed: 'Settings succesfully changed!', Share: 'Share', + Share_Link: 'Share Link', Share_this_app: 'Share this app', Show_Unread_Counter: 'Show Unread Counter', Show_Unread_Counter_Info: 'Unread counter is displayed as a badge on the right of the channel, in the list', @@ -449,6 +460,10 @@ export default { You: 'You', You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.', Your_certificate: 'Your Certificate', + Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.', + Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Your invite link will expire on {{date}} or after {{usesLeft}} uses.', + Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.', + Your_invite_link_will_never_expire: 'Your invite link will never expire.', Version_no: 'Version: {{version}}', You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!', Change_Language: 'Change Language', diff --git a/app/i18n/locales/fr.js b/app/i18n/locales/fr.js index cf5129e6..1903cedf 100644 --- a/app/i18n/locales/fr.js +++ b/app/i18n/locales/fr.js @@ -80,7 +80,7 @@ export default { Activity: 'Activité', Add_Reaction: 'Ajouter une réaction', Add_Server: 'Ajouter un serveur', - Add_user: 'Ajouter un utilisateur', + Add_users: 'Ajouter des utilisateurs', Alert: 'Alerte', alert: 'alerte', alerts: 'alertes', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index b41deb38..9087ed04 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -88,7 +88,7 @@ export default { Activity: 'Atividade', Add_Reaction: 'Reagir', Add_Server: 'Adicionar servidor', - Add_user: 'Adicionar usuário', + Add_users: 'Adicionar usuário', Alert: 'Alerta', alert: 'alerta', alerts: 'alertas', @@ -123,6 +123,7 @@ export default { Cancel: 'Cancelar', changing_avatar: 'trocando avatar', creating_channel: 'criando canal', + creating_invite: 'criando convite', Channel_Name: 'Nome do Canal', Channels: 'Canais', Chats: 'Conversas', @@ -169,6 +170,7 @@ export default { edited: 'editado', erasing_room: 'apagando sala', Edit: 'Editar', + Edit_Invite: 'Editar convite', Email_or_password_field_is_empty: 'Email ou senha estão vazios', Email: 'Email', email: 'e-mail', @@ -176,6 +178,7 @@ export default { Enable_notifications: 'Habilitar notificações', Everyone_can_access_this_channel: 'Todos podem acessar este canal', Error_uploading: 'Erro subindo', + Expiration_Days: 'Expira em (dias)', Favorites: 'Favoritos', Files: 'Arquivos', File_description: 'Descrição do arquivo', @@ -188,6 +191,7 @@ export default { Forgot_password: 'Esqueci minha senha', Forgot_Password: 'Esqueci minha senha', Full_table: 'Clique para ver a tabela completa', + Generate_New_Link: 'Gerar novo convite', Group_by_favorites: 'Agrupar favoritos', Group_by_type: 'Agrupar por tipo', Has_joined_the_channel: 'Entrou no canal', @@ -196,7 +200,10 @@ export default { Invisible: 'Invisível', Invite: 'Convidar', is_typing: 'está digitando', + Invalid_or_expired_invite_token: 'Token de convite inválido ou vencido', Invalid_server_version: 'O servidor que você está conectando não é suportado mais por esta versão do aplicativo: {{currentVersion}}.\n\nEsta versão do aplicativo requer a versão {{minVersion}} do servidor para funcionar corretamente.', + Invite_Link: 'Link de Convite', + Invite_users: 'Convidar usuários', Join_the_community: 'Junte-se à comunidade', Join: 'Entrar', Just_invited_people_can_access_this_channel: 'Apenas as pessoas convidadas podem acessar este canal', @@ -212,6 +219,7 @@ export default { Login_error: 'Suas credenciais foram rejeitadas. Tente novamente por favor!', Login_with: 'Login with', Logout: 'Sair', + Max_number_of_uses: 'Número máximo de usos', Members: 'Membros', Mentioned_Messages: 'Mensagens mencionadas', mentioned: 'mencionado', @@ -231,11 +239,13 @@ export default { N_users: '{{n}} usuários', name: 'nome', Name: 'Nome', + Never: 'Nunca', New_in_RocketChat_question_mark: 'Novo no Rocket.Chat?', New_Message: 'Nova Mensagem', New_Password: 'Nova Senha', Next: 'Próximo', No_files: 'Não há arquivos', + No_limit: 'Sem limite', No_mentioned_messages: 'Não há menções', No_pinned_messages: 'Não há mensagens fixadas', No_results_found: 'Nenhum resultado encontrado', @@ -328,6 +338,7 @@ export default { Settings: 'Configurações', Settings_succesfully_changed: 'Configurações salvas com sucesso!', Share: 'Compartilhar', + Share_Link: 'Share Link', Sign_in_your_server: 'Entrar no seu servidor', Sign_Up: 'Registrar', Some_field_is_invalid_or_empty: 'Algum campo está inválido ou vazio', @@ -401,7 +412,10 @@ export default { you_were_mentioned: 'você foi mencionado', you: 'você', You: 'Você', - You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Você precisa acessar ao menos um servidor Rocket.Chat para compartilhar.', + Your_invite_link_will_expire_after__usesLeft__uses: 'Seu link de convite irá vencer depois de {{usesLeft}} usos.', + Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Seu link de convite irá vencer em {{date}} ou depois de {{usesLeft}} usos.', + Your_invite_link_will_expire_on__date__: 'Seu link de convite irá vencer em {{date}}.', + Your_invite_link_will_never_expire: 'Seu link de convite nunca irá vencer.', You_will_not_be_able_to_recover_this_message: 'Você não será capaz de recuperar essa mensagem!', Write_External_Permission_Message: 'Rocket Chat precisa de acesso à sua galeria para salvar imagens', Write_External_Permission: 'Acesso à Galeria', diff --git a/app/i18n/locales/pt-PT.js b/app/i18n/locales/pt-PT.js index c8167569..1c32d4d6 100644 --- a/app/i18n/locales/pt-PT.js +++ b/app/i18n/locales/pt-PT.js @@ -80,7 +80,7 @@ export default { Activity: 'Actividade', Add_Reaction: 'Adicionar Reacção', Add_Server: 'Adicionar Servidor', - Add_user: 'Adicionar utilizador', + Add_users: 'Adicionar utilizadores', Alert: 'Alerta', alert: 'alerta', alerts: 'alertas', diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js index dcc54533..02f7f46b 100644 --- a/app/i18n/locales/ru.js +++ b/app/i18n/locales/ru.js @@ -80,7 +80,7 @@ export default { Activity: 'Активность', Add_Reaction: 'Добавить реакцию', Add_Server: 'Добавить сервер', - Add_user: 'Добавить пользователя', + Add_users: 'Добавить пользователей', Admin_Panel: 'Панель админа', Alert: 'Оповещение', alert: 'оповещение', diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js index 56c18f1b..a977bf2b 100644 --- a/app/i18n/locales/zh-CN.js +++ b/app/i18n/locales/zh-CN.js @@ -80,7 +80,7 @@ export default { Activity: '按活动排序', Add_Reaction: '增加回复', Add_Server: '添加服务器', - Add_user: '添加用户', + Add_users: '添加用户', Alert: '警告', alert: '警告', alerts: '警告', diff --git a/app/index.js b/app/index.js index bf88e477..6c906e2e 100644 --- a/app/index.js +++ b/app/index.js @@ -49,7 +49,7 @@ if (isIOS) { const parseDeepLinking = (url) => { if (url) { url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); - const regex = /^(room|auth)\?/; + const regex = /^(room|auth|invite)\?/; if (url.match(regex)) { url = url.replace(regex, '').trim(); if (url) { @@ -146,6 +146,12 @@ const ChatsStack = createStackNavigator({ SelectedUsersView: { getScreen: () => require('./views/SelectedUsersView').default }, + InviteUsersView: { + getScreen: () => require('./views/InviteUsersView').default + }, + InviteUsersEditView: { + getScreen: () => require('./views/InviteUsersEditView').default + }, MessagesView: { getScreen: () => require('./views/MessagesView').default }, diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index fb79d6b4..718c73c7 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1098,6 +1098,23 @@ const RocketChat = { }, translateMessage(message, targetLanguage) { return this.sdk.methodCall('autoTranslate.translateMessage', message, targetLanguage); + }, + getRoomTitle(room) { + const { UI_Use_Real_Name: useRealName } = reduxStore.getState().settings; + return ((room.prid || useRealName) && room.fname) || room.name; + }, + + findOrCreateInvite({ rid, days, maxUses }) { + // RC 2.4.0 + return this.sdk.post('findOrCreateInvite', { rid, days, maxUses }); + }, + validateInviteToken(token) { + // RC 2.4.0 + return this.sdk.post('validateInviteToken', { token }); + }, + useInviteToken(token) { + // RC 2.4.0 + return this.sdk.post('useInviteToken', { token }); } }; diff --git a/app/reducers/index.js b/app/reducers/index.js index 6a010df3..5d8a3df3 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -15,6 +15,7 @@ import crashReport from './crashReport'; import customEmojis from './customEmojis'; import activeUsers from './activeUsers'; import usersTyping from './usersTyping'; +import inviteLinks from './inviteLinks'; export default combineReducers({ settings, @@ -32,5 +33,6 @@ export default combineReducers({ crashReport, customEmojis, activeUsers, - usersTyping + usersTyping, + inviteLinks }); diff --git a/app/reducers/inviteLinks.js b/app/reducers/inviteLinks.js new file mode 100644 index 00000000..2126c489 --- /dev/null +++ b/app/reducers/inviteLinks.js @@ -0,0 +1,37 @@ +import { INVITE_LINKS } from '../actions/actionsTypes'; + +const initialState = { + token: '', + days: 1, + maxUses: 0, + invite: {} +}; + +export default (state = initialState, action) => { + switch (action.type) { + case INVITE_LINKS.SET_TOKEN: + return { + token: action.token + }; + case INVITE_LINKS.SET_PARAMS: + return { + ...state, + ...action.params + }; + case INVITE_LINKS.SET_INVITE: + return { + ...state, + invite: action.invite + }; + case INVITE_LINKS.REQUEST: + return state; + case INVITE_LINKS.SUCCESS: + return initialState; + case INVITE_LINKS.FAILURE: + return initialState; + case INVITE_LINKS.CLEAR: + return initialState; + default: + return state; + } +}; diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 969a42ca..ef66fac4 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -6,6 +6,7 @@ import RNUserDefaults from 'rn-user-defaults'; import Navigation from '../lib/Navigation'; import * as types from '../actions/actionsTypes'; import { selectServerRequest } from '../actions/server'; +import { inviteLinksSetToken, inviteLinksRequest } from '../actions/inviteLinks'; import database from '../lib/database'; import RocketChat from '../lib/rocketchat'; import EventEmitter from '../utils/events'; @@ -15,6 +16,17 @@ const roomTypes = { channel: 'c', direct: 'd', group: 'p' }; +const handleInviteLink = function* handleInviteLink({ params, requireLogin = false }) { + if (params.path && params.path.startsWith('invite/')) { + const token = params.path.replace('invite/', ''); + if (requireLogin) { + yield put(inviteLinksSetToken(token)); + } else { + yield put(inviteLinksRequest(token)); + } + } +}; + const navigate = function* navigate({ params }) { yield put(appStart('inside')); if (params.rid) { @@ -24,6 +36,8 @@ const navigate = function* navigate({ params }) { yield Navigation.navigate('RoomsListView'); Navigation.navigate('RoomView', { rid: params.rid, name, t: roomTypes[type] }); } + } else { + yield handleInviteLink({ params }); } }; @@ -63,7 +77,7 @@ const handleOpen = function* handleOpen({ params }) { const servers = yield serversCollection.find(host); if (servers && user) { yield put(selectServerRequest(host)); - yield take(types.SERVER.SELECT_SUCCESS); + yield take(types.LOGIN.SUCCESS); yield navigate({ params }); return; } @@ -82,6 +96,8 @@ const handleOpen = function* handleOpen({ params }) { if (params.token) { yield take(types.SERVER.SELECT_SUCCESS); yield RocketChat.connect({ server: host, user: { token: params.token } }); + } else { + yield handleInviteLink({ params, requireLogin: true }); } } }; diff --git a/app/sagas/index.js b/app/sagas/index.js index 004656dc..38c18435 100644 --- a/app/sagas/index.js +++ b/app/sagas/index.js @@ -8,6 +8,7 @@ import createChannel from './createChannel'; import init from './init'; import state from './state'; import deepLinking from './deepLinking'; +import inviteLinks from './inviteLinks'; const root = function* root() { yield all([ @@ -19,7 +20,8 @@ const root = function* root() { messages(), selectServer(), state(), - deepLinking() + deepLinking(), + inviteLinks() ]); }; diff --git a/app/sagas/inviteLinks.js b/app/sagas/inviteLinks.js new file mode 100644 index 00000000..d3ecfbba --- /dev/null +++ b/app/sagas/inviteLinks.js @@ -0,0 +1,72 @@ +import { + put, takeLatest, delay, select +} from 'redux-saga/effects'; +import { Alert } from 'react-native'; + +import { INVITE_LINKS } from '../actions/actionsTypes'; +import { inviteLinksSuccess, inviteLinksFailure, inviteLinksSetInvite } from '../actions/inviteLinks'; +import RocketChat from '../lib/rocketchat'; +import log from '../utils/log'; +import Navigation from '../lib/Navigation'; +import I18n from '../i18n'; + +const handleRequest = function* handleRequest({ token }) { + try { + const validateResult = yield RocketChat.validateInviteToken(token); + if (!validateResult.valid) { + yield put(inviteLinksFailure()); + return; + } + + const result = yield RocketChat.useInviteToken(token); + if (!result.success) { + yield put(inviteLinksFailure()); + return; + } + + if (result.room && result.room.rid) { + yield delay(1000); + yield Navigation.navigate('RoomsListView'); + const { room } = result; + Navigation.navigate('RoomView', { + rid: room.rid, + name: RocketChat.getRoomTitle(room), + t: room.t + }); + } + + yield put(inviteLinksSuccess()); + } catch (e) { + yield put(inviteLinksFailure()); + log(e); + } +}; + +const handleFailure = function handleFailure() { + Alert.alert(I18n.t('Oops'), I18n.t('Invalid_or_expired_invite_token')); +}; + +const handleCreateInviteLink = function* handleCreateInviteLink({ rid }) { + try { + const inviteLinks = yield select(state => state.inviteLinks); + const result = yield RocketChat.findOrCreateInvite({ + rid, days: inviteLinks.days, maxUses: inviteLinks.maxUses + }); + if (!result.success) { + Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_invite') })); + return; + } + + yield put(inviteLinksSetInvite(result)); + } catch (e) { + log(e); + } +}; + +const root = function* root() { + yield takeLatest(INVITE_LINKS.REQUEST, handleRequest); + yield takeLatest(INVITE_LINKS.FAILURE, handleFailure); + yield takeLatest(INVITE_LINKS.CREATE, handleCreateInviteLink); +}; + +export default root; diff --git a/app/sagas/login.js b/app/sagas/login.js index ac2a9b9a..7762ce4c 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -20,6 +20,7 @@ import I18n from '../i18n'; import database from '../lib/database'; import EventEmitter from '../utils/events'; import Navigation from '../lib/Navigation'; +import { inviteLinksRequest } from '../actions/inviteLinks'; const getServer = state => state.server.server; const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); @@ -115,17 +116,27 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { yield put(setUser(user)); EventEmitter.emit('connected'); + let currentRoot; if (!user.username) { yield put(appStart('setUsername')); } else if (adding) { yield put(serverFinishAdd()); yield put(appStart('inside')); } else { - const currentRoot = yield select(state => state.app.root); + currentRoot = yield select(state => state.app.root); if (currentRoot !== 'inside') { yield put(appStart('inside')); } } + + // after a successful login, check if it's been invited via invite link + currentRoot = yield select(state => state.app.root); + if (currentRoot === 'inside') { + const inviteLinkToken = yield select(state => state.inviteLinks.token); + if (inviteLinkToken) { + yield put(inviteLinksRequest(inviteLinkToken)); + } + } } catch (e) { log(e); } diff --git a/app/views/InviteUsersEditView/index.js b/app/views/InviteUsersEditView/index.js new file mode 100644 index 00000000..5287358a --- /dev/null +++ b/app/views/InviteUsersEditView/index.js @@ -0,0 +1,160 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { ScrollView, View } from 'react-native'; +import { SafeAreaView } from 'react-navigation'; +import { connect } from 'react-redux'; +import RNPickerSelect from 'react-native-picker-select'; + +import { + inviteLinksSetParams as inviteLinksSetParamsAction, + inviteLinksCreate as inviteLinksCreateAction +} from '../../actions/inviteLinks'; +import ListItem from '../../containers/ListItem'; +import styles from './styles'; +import Button from '../../containers/Button'; +import scrollPersistTaps from '../../utils/scrollPersistTaps'; +import I18n from '../../i18n'; +import StatusBar from '../../containers/StatusBar'; +import { themes } from '../../constants/colors'; +import { withTheme } from '../../theme'; +import { themedHeader } from '../../utils/navigation'; +import Separator from '../../containers/Separator'; + +const OPTIONS = { + days: [{ + label: I18n.t('Never'), value: 0 + }, + { + label: '1', value: 1 + }, + { + label: '7', value: 7 + }, + { + label: '15', value: 15 + }, + { + label: '30', value: 30 + }], + maxUses: [{ + label: I18n.t('No_limit'), value: 0 + }, + { + label: '1', value: 1 + }, + { + label: '5', value: 5 + }, + { + label: '10', value: 10 + }, + { + label: '25', value: 25 + }, + { + label: '50', value: 50 + }, + { + label: '100', value: 100 + }] +}; + +class InviteUsersView extends React.Component { + static navigationOptions = ({ screenProps }) => ({ + title: I18n.t('Invite_users'), + ...themedHeader(screenProps.theme) + }) + + static propTypes = { + navigation: PropTypes.object, + theme: PropTypes.string, + timeDateFormat: PropTypes.string, + createInviteLink: PropTypes.func, + inviteLinksSetParams: PropTypes.func + } + + constructor(props) { + super(props); + this.rid = props.navigation.getParam('rid'); + } + + onValueChangePicker = (key, value) => { + const { inviteLinksSetParams } = this.props; + const params = { + [key]: value + }; + inviteLinksSetParams(params); + } + + createInviteLink = () => { + const { createInviteLink, navigation } = this.props; + createInviteLink(this.rid); + navigation.pop(); + } + + renderPicker = (key) => { + const { props } = this; + const { theme } = props; + return ( + this.onValueChangePicker(key, value)} + items={OPTIONS[key]} + /> + ); + } + + render() { + const { theme } = this.props; + return ( + + + + + this.renderPicker('days')} + theme={theme} + /> + + this.renderPicker('maxUses')} + theme={theme} + /> + + + +