This commit is contained in:
Anant Bhasin 2021-07-16 02:01:14 +05:30
commit 4cc3d8a26b
100 changed files with 1589 additions and 1050 deletions

View File

@ -1,6 +1,6 @@
__tests__
node_modules
coverage
e2e
e2e/docker
android
ios

View File

@ -29,7 +29,8 @@ module.exports = {
"commonjs": true,
"es6": true,
"node": true,
"jquery": true
"jquery": true,
"mocha": true
},
"rules": {
"react/jsx-filename-extension": [1, {
@ -155,5 +156,23 @@ module.exports = {
},
"globals": {
"__DEV__": true
},
overrides: [
{
files: ['e2e/**'],
globals: {
by: true,
detox: true,
device: true,
element: true,
expect: true,
waitFor: true
},
rules: {
'import/no-extraneous-dependencies': 0,
'no-await-in-loop': 0,
'no-restricted-syntax': 0
}
}
]
};

View File

@ -11490,7 +11490,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -11593,7 +11593,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -11696,7 +11696,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -11973,7 +11973,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12076,7 +12076,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12179,7 +12179,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12420,7 +12420,7 @@ exports[`Storyshots LoadMore black theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12692,7 +12692,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12795,7 +12795,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -12898,7 +12898,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -13175,7 +13175,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -13278,7 +13278,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -13381,7 +13381,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -13622,7 +13622,7 @@ exports[`Storyshots LoadMore dark theme 1`] = `
},
undefined,
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
]
}
@ -67631,7 +67631,7 @@ Array [
"textAlign": "left",
},
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
Object {
"backgroundColor": "transparent",
@ -67791,7 +67791,7 @@ Array [
"textAlign": "left",
},
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
Object {
"backgroundColor": "transparent",
@ -74309,7 +74309,7 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
"textAlign": "left",
},
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
Object {
"flex": 1,
@ -74661,7 +74661,7 @@ exports[`Storyshots Thread Messages.Item themes 1`] = `
"textAlign": "left",
},
Object {
"color": "#e8ebed",
"color": "#cbced1",
},
Object {
"flex": 1,

View File

@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.17.0"
versionName "4.18.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]

View File

@ -66,9 +66,10 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
'CLEAR',
...defaultTypes
]);
export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']);
export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD', 'UPDATE']);
export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']);
export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']);
export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']);
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET']);
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']);
export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']);

View File

@ -1,10 +1,11 @@
import * as types from './actionsTypes';
export function loginRequest(credentials, logoutOnError) {
export function loginRequest(credentials, logoutOnError, isFromWebView) {
return {
type: types.LOGIN.REQUEST,
credentials,
logoutOnError
logoutOnError,
isFromWebView
};
}

View File

@ -6,3 +6,10 @@ export function setPermissions(permissions) {
permissions
};
}
export function updatePermission(id, roles) {
return {
type: types.PERMISSIONS.UPDATE,
payload: { id, roles }
};
}

20
app/actions/roles.js Normal file
View File

@ -0,0 +1,20 @@
import * as types from './actionsTypes';
export function setRoles(roles) {
return {
type: types.ROLES.SET,
roles
};
}
export function updateRoles(id, desc) {
return {
type: types.ROLES.UPDATE,
payload: { id, desc }
};
}
export function removeRoles(id) {
return {
type: types.ROLES.REMOVE,
payload: { id }
};
}

View File

@ -23,11 +23,12 @@ export function leaveRoom(roomType, room, selected) {
};
}
export function deleteRoom(rid, t) {
export function deleteRoom(roomType, room, selected) {
return {
type: types.ROOM.DELETE,
rid,
t
room,
roomType,
selected
};
}

View File

@ -7,6 +7,13 @@ export function addSettings(settings) {
};
}
export function updateSettings(id, value) {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
};
}
export function clearSettings() {
return {
type: SETTINGS.CLEAR

View File

@ -74,7 +74,7 @@ export const themes = {
auxiliaryBackground: '#07101e',
bannerBackground: '#0e1f38',
titleText: '#f9f9f9',
bodyText: '#e8ebed',
bodyText: '#cbced1',
backdropColor: '#000000',
dangerColor: '#f5455c',
successColor: '#2de0a5',
@ -121,7 +121,7 @@ export const themes = {
auxiliaryBackground: '#080808',
bannerBackground: '#1f2329',
titleText: '#f9f9f9',
bodyText: '#e8ebed',
bodyText: '#cbced1',
backdropColor: '#000000',
dangerColor: '#f5455c',
successColor: '#2de0a5',

View File

@ -196,5 +196,11 @@ export default {
},
Accounts_AllowInvisibleStatusOption: {
type: 'valueAsString'
},
Jitsi_Enable_Teams: {
type: 'valueAsBoolean'
},
Jitsi_Enable_Channels: {
type: 'valuesAsBoolean'
}
};

View File

@ -76,7 +76,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
const { title = name, avatar = name } = notification;
const onPress = () => {
const { prid } = payload;
const { prid, _id } = payload;
if (!rid) {
return;
}
@ -89,7 +89,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
} else {
Navigation.navigate('RoomsListView');
}
goRoom({ item, isMasterDetail });
goRoom({ item, isMasterDetail, jumpToMessageId: _id });
hideNotification();
};

View File

@ -387,7 +387,6 @@
"Preferences_saved": "تم حفظ التفضيلات",
"Privacy_Policy": "سياسة الخصوصية",
"Private_Channel": "قناة خاصة",
"Private_Groups": "مجموعات خاصة",
"Private": "خاص",
"Processing": "جار معالجة...",
"Profile_saved_successfully": "تم حفظ الملف الشخصي بنجاح!",

View File

@ -400,7 +400,6 @@
"Preferences_saved": "Einstellungen gespeichert!",
"Privacy_Policy": " Datenschutzbestimmungen",
"Private_Channel": "Privater Kanal",
"Private_Groups": "Private Gruppen",
"Private": "Privat",
"Processing": "Bearbeite …",
"Profile_saved_successfully": "Profil erfolgreich gespeichert!",
@ -685,7 +684,7 @@
"Following": "verfolgte",
"Threads_displaying_all": "zeige alle",
"Threads_displaying_following": "zeige gefolgte",
"Threads_displaying_unread": "zeige ungelesene",
"Threads_displaying_unread": "Zeige ungelesene",
"No_threads": "Es gibt keine Threads",
"No_threads_following": "Du folgst keinen Threads",
"No_threads_unread": "Es gibt keine ungelesenen Threads",
@ -724,6 +723,7 @@
"creating_team": "Team erstellen",
"team-name-already-exists": "Ein Team mit diesem Namen existiert bereits",
"Add_Channel_to_Team": "Kanal zum Team hinzufügen",
"Left_The_Team_Successfully": "Das Team erfolgreich verlassen",
"Create_New": "Neu erstellen",
"Add_Existing": "Vorhandenes hinzufügen",
"Add_Existing_Channel": "Vorhandenen Kanal hinzufügen",

View File

@ -400,7 +400,6 @@
"Preferences_saved": "Preferences saved!",
"Privacy_Policy": " Privacy Policy",
"Private_Channel": "Private Channel",
"Private_Groups": "Private Groups",
"Private": "Private",
"Processing": "Processing...",
"Profile_saved_successfully": "Profile saved successfully!",
@ -756,11 +755,18 @@
"member-does-not-exist": "Member does not exist",
"Convert": "Convert",
"Convert_to_Team": "Convert to Team",
"Convert_to_Team_Warning": "This can't be undone. Once you convert a channel to a team, you can not turn it back to a channel.",
"Convert_to_Team_Warning": "You are converting this Channel to a Team. All Members will be kept.",
"Move_to_Team": "Move to Team",
"Move_Channel_Paragraph": "Moving a channel inside a team means that this channel will be added in the teams context, however, all channels members, which are not members of the respective team, will still have access to this channel, but will not be added as teams members. \n\nAll channels management will still be made by the owners of this channel.\n\nTeams members and even teams owners, if not a member of this channel, can not have access to the channels content. \n\nPlease notice that the Teams owner will be able remove members from the Channel.",
"Move_to_Team_Warning": "After reading the previous intructions about this behavior, do you still want to move this channel to the selected team?",
"Load_More": "Load More",
"Load_Newer": "Load Newer",
"Load_Older": "Load Older"
"Load_Older": "Load Older",
"Left_The_Room_Successfully": "Left the room successfully",
"Deleted_The_Team_Successfully": "Team deleted successfully",
"Deleted_The_Room_Successfully": "Room deleted successfully",
"Convert_to_Channel": "Convert to Channel",
"Converting_Team_To_Channel": "Converting Team to Channel",
"Select_Team_Channels_To_Delete": "Select the Teams Channels you would like to delete, the ones you do not select will be moved to the Workspace. \n\nNotice that public Channels will be public and visible to everyone.",
"You_are_converting_the_team": "You are converting this Team to a Channel"
}

View File

@ -280,7 +280,6 @@
"Preferences_saved": "¡Preferencias guardadas!",
"Privacy_Policy": "Política de privacidad",
"Private_Channel": "Canal privado",
"Private_Groups": "Grupos privados",
"Private": "Privado",
"Processing": "Procesando...",
"Profile_saved_successfully": "¡Perfil guardado correctamente!",

View File

@ -400,7 +400,6 @@
"Preferences_saved": "Préférences sauvegardées !",
"Privacy_Policy": " Politique de confidentialité",
"Private_Channel": "Canal privé",
"Private_Groups": "Groupes privés",
"Private": "Privé",
"Processing": "Traitement...",
"Profile_saved_successfully": "Profil enregistré avec succès !",
@ -724,6 +723,7 @@
"creating_team": "création de l'équipe",
"team-name-already-exists": "Une équipe portant ce nom existe déjà",
"Add_Channel_to_Team": "Ajouter un canal à l'équipe",
"Left_The_Team_Successfully": "A quitté l'équipe avec succès",
"Create_New": "Créer un nouveau",
"Add_Existing": "Ajouter existant",
"Add_Existing_Channel": "Ajouter un canal existant",
@ -755,11 +755,18 @@
"member-does-not-exist": "Le membre n'existe pas",
"Convert": "Convertir",
"Convert_to_Team": "Convertir en équipe",
"Convert_to_Team_Warning": "Ceci ne peut pas être annulé. Une fois que vous avez converti un canal en équipe, vous ne pouvez pas le retransformer en canal.",
"Convert_to_Team_Warning": "Vous convertissez ce canal en équipe. Tous les membres seront conservés.",
"Move_to_Team": "Déplacer vers l'équipe",
"Move_Channel_Paragraph": "Le déplacement d'un canal dans une équipe signifie que ce canal sera ajouté dans le contexte d'équipe. Cependant, tous les membres du canal, qui ne sont pas membres de l'équipe respective, auront toujours accès à ce canal, mais ne seront pas ajoutés comme membres de l'équipe.\n\nLa gestion de tout le canal sera toujours assurée par les propriétaires de ce canal.\n\nLes membres de l'équipe et même les propriétaires de l'équipe, s'ils ne sont pas membres de ce canal, ne peuvent pas avoir accès au contenu du canal.\n\nVeuillez noter que le propriétaire de l'équipe pourra supprimer des membres du canal.",
"Move_to_Team_Warning": "Après avoir lu les instructions précédentes sur ce comportement, voulez-vous toujours déplacer ce canal vers l'équipe sélectionnée ?",
"Load_More": "Charger plus",
"Load_Newer": "Charger plus récent",
"Load_Older": "Charger plus ancien"
"Load_Older": "Charger plus ancien",
"Left_The_Room_Successfully": "A quitté le salon avec succès",
"Deleted_The_Team_Successfully": "Equipe supprimée avec succès",
"Deleted_The_Room_Successfully": "Salon supprimé avec succès",
"Convert_to_Channel": "Convertir en canal",
"Converting_Team_To_Channel": "Conversion de léquipe en canal",
"Select_Team_Channels_To_Delete": "Sélectionnez les canaux de l'équipe que vous souhaitez supprimer, ceux que vous ne sélectionnez pas, seront déplacés vers l'espace de travail. \n\n\nNotez que les canaux publics seront publics et visibles par tous.",
"You_are_converting_the_team": "Vous convertissez cette équipe en canal"
}

View File

@ -392,7 +392,6 @@
"Preferences_saved": "Impostazioni salvate!",
"Privacy_Policy": " Privacy Policy",
"Private_Channel": "Canale privato",
"Private_Groups": "Gruppi privati",
"Private": "Privato",
"Processing": "Elaborazione...",
"Profile_saved_successfully": "Profilo salvato correttamente!",

View File

@ -296,7 +296,6 @@
"Preferences_saved": "設定が保存されました。",
"Privacy_Policy": " プライバシーポリシー",
"Private_Channel": "プライベートチャンネル",
"Private_Groups": "プライベートグループ",
"Private": "プライベート",
"Processing": "処理中...",
"Profile_saved_successfully": "プロフィールが保存されました!",

View File

@ -400,7 +400,6 @@
"Preferences_saved": "Voorkeuren opgeslagen!",
"Privacy_Policy": " Privacybeleid",
"Private_Channel": "Privékanaal",
"Private_Groups": "Privé groepen",
"Private": "Privé",
"Processing": "Verwerking...",
"Profile_saved_successfully": "Profiel succesvol opgeslagen!",
@ -724,6 +723,7 @@
"creating_team": "team maken",
"team-name-already-exists": "Er bestaat al een team met die naam",
"Add_Channel_to_Team": "Kanaal toevoegen aan team",
"Left_The_Team_Successfully": "Het team met succes verlaten",
"Create_New": "Maak nieuw",
"Add_Existing": "Voeg bestaande",
"Add_Existing_Channel": "Bestaand kanaal toevoegen",
@ -755,11 +755,18 @@
"member-does-not-exist": "Lid bestaat niet",
"Convert": "Converteren",
"Convert_to_Team": "Converteren naar team",
"Convert_to_Team_Warning": "Dit kan niet ongedaan worden gemaakt. Eens je een kanaal naar een team hebt geconverteerd, kun je het niet meer naar een kanaal terugzetten.",
"Convert_to_Team_Warning": "Je converteert dit kanaal naar een team. Alle leden blijven behouden.",
"Move_to_Team": "Verplaats naar team",
"Move_Channel_Paragraph": "Het verplaatsen van een kanaal binnen een team betekent dat dit kanaal wordt toegevoegd in de context van het team. Maar, alle leden van dit kanaal, die geen lid zijn van het respectieve team, zullen nog steeds toegang hebben tot dit kanaal, maar worden niet als teamleden toegevoegd.\n\nHet volledige beheer van dit kanaal wordt nog steeds door de eigenaren van dit kanaal gedaan.\n\nTeamleden en zelfs teameigenaren, wanneer ze geen lid zijn van dit kanaal, hebben geen toegang tot de content van het kanaal.\n\nHou er rekening mee dat de eigenaar van het team de leden uit het kanaal kan verwijderen.",
"Move_to_Team_Warning": "Wil je na het lezen van de vorige instructies over dit gedrag, dit kanaal nog steeds naar het geselecteerde team verplaatsen?",
"Load_More": "Meer laden",
"Load_Newer": "Nieuwer laden",
"Load_Older": "Ouder laden"
"Load_Older": "Ouder laden",
"Left_The_Room_Successfully": "Heeft kamer met succes verlaten",
"Deleted_The_Team_Successfully": "Team succesvol verwijderd",
"Deleted_The_Room_Successfully": "Kamer succesvol verwijderd",
"Convert_to_Channel": "Converteren naar kanaal",
"Converting_Team_To_Channel": "Team converteren naar kanaal",
"Select_Team_Channels_To_Delete": "Selecteer de teamkanalen die je wilt verwijderen, de kanalen die u niet selecteert, worden naar de werkruimte verplaatst.\n\nMerk op dat openbare kanalen openbaar en voor iedereen zichtbaar zullen zijn.",
"You_are_converting_the_team": "Je converteert dit team naar een kanaal"
}

View File

@ -370,7 +370,6 @@
"Preferences_saved": "Preferências salvas!",
"Privacy_Policy": " Política de Privacidade",
"Private_Channel": "Canal Privado",
"Private_Groups": "Grupo Privado",
"Private": "Privado",
"Processing": "Processando...",
"Profile_saved_successfully": "Perfil salvo com sucesso!",
@ -664,6 +663,11 @@
"No_team_channels_found": "Nenhum canal encontrado",
"Team_not_found": "Time não encontrado",
"Private_Team": "Equipe Privada",
"Left_The_Team_Successfully": "Saiu do time com sucesso",
"Add_Existing_Channel": "Adicionar Canal Existente",
"invalid-room": "Sala inválida"
"invalid-room": "Sala inválida",
"Left_The_Room_Successfully": "Saiu da sala com sucesso",
"Deleted_The_Team_Successfully": "Time deletado com sucesso",
"Deleted_The_Room_Successfully": "Sala deletada com sucesso",
"Convert_to_Channel": "Converter para um Canal"
}

View File

@ -10,19 +10,22 @@
"error-could-not-change-email": "Não foi possível alterar o e-mail",
"error-could-not-change-name": "Não foi possível alterar o nome",
"error-could-not-change-username": "Não foi possível alterar o nome de utilizador",
"error-could-not-change-status": "Impossível mudar estado",
"error-delete-protected-role": "Não é possível eliminar uma função protegida",
"error-department-not-found": "Departamento não encontrado",
"error-direct-message-file-upload-not-allowed": "Partilha de ficheiros não permitido em mensagens diretas",
"error-duplicate-channel-name": "Um canal com o nome {{channel_name}} existe",
"error-duplicate-channel-name": "Existe um canal com o nome {{room_name}}",
"error-email-domain-blacklisted": "O domínio de e-mail está na lista negra",
"error-email-send-failed": "Erro ao tentar enviar e-mail: {{message}}",
"error-save-image": "Erro ao salvar imagem",
"error-save-video": "Erro ao salvar vídeo",
"error-field-unavailable": "{{field}} já está em uso :(",
"error-file-too-large": "Ficheiro demasiado grande",
"error-importer-not-defined": "O importador não foi definido correctamente, a classe Import está em falta.",
"error-input-is-not-a-valid-field": "{{input}} não é um {{field}} válido",
"error-invalid-actionlink": "Link de acção inválido",
"error-invalid-arguments": "Argumentos inválidos",
"error-invalid-asset": "Ficheiro inválida",
"error-invalid-asset": "Ficheiro inválido",
"error-invalid-channel": "Canal inválido.",
"error-invalid-channel-start-with-chars": "Canal inválido. Começa por @ ou #",
"error-invalid-custom-field": "Campo personalizado inválido",
@ -58,6 +61,7 @@
"error-message-editing-blocked": "A edição de mensagens está bloqueada",
"error-message-size-exceeded": "O tamanho da mensagem excede Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "Você deve fornecer o link para cancelar a subscrição: [unsubscribe].",
"error-no-owner-channel": "Você não é dono do canal",
"error-no-tokens-for-this-user": "Não há tokens para este utilizador",
"error-not-allowed": "Não permitido",
"error-not-authorized": "Não autorizado",
@ -75,33 +79,46 @@
"error-user-registration-disabled": "O registo de utilizadores está desactivado",
"error-user-registration-secret": "O registo de utilizadores só é permitido por meio de um URL secreto",
"error-you-are-last-owner": "Você é o último proprietário. Por favor, defina novo proprietário antes de sair da sala.",
"error-status-not-allowed": "O estado invisível está desactivado",
"Actions": "Acções",
"activity": "actividade",
"Activity": "Actividade",
"Add_Reaction": "Adicionar Reacção",
"Add_Server": "Adicionar Servidor",
"Add_users": "Adicionar utilizadores",
"Admin_Panel": "Painel de Administração",
"Agent": "Agente",
"Alert": "Alerta",
"alert": "alerta",
"alerts": "alertas",
"All_users_in_the_channel_can_write_new_messages": "Todos os utilizadores no canal podem escrever novas mensagens",
"All_users_in_the_team_can_write_new_messages": "Todos os usuários da equipa podem escrever novas mensagens",
"A_meaningful_name_for_the_discussion_room": "Um nome significativo para a sala de discussão",
"All": "Todos",
"All_Messages": "Todas as Mensagens",
"Allow_Reactions": "Permitir Reacções",
"Alphabetical": "Alfabética",
"and_more": "e mais",
"and": "e",
"announcement": "anúncio",
"Announcement": "Anúncio",
"Apply_Your_Certificate": "Aplique o seu Certificado",
"ARCHIVE": "ARQUIVAR",
"archive": "arquivar",
"are_typing": "estão a escrever",
"Are_you_sure_question_mark": "Tem a certeza?",
"Are_you_sure_you_want_to_leave_the_room": "Tem certeza de que quer sair da sala {{room}}?",
"Audio": "Áudio",
"Authenticating": "Autenticando",
"Automatic": "Automático",
"Auto_Translate": "Auto-Tradução",
"Avatar_changed_successfully": "Avatar alterado com sucesso!",
"Avatar_Url": "URL do Avatar",
"Away": "Ausente",
"Back": "Voltar",
"Black": "Preto",
"Block_user": "Bloquear utilizador",
"Browser": "Navegador",
"Broadcast_channel_Description": "Apenas utilizadores autorizados podem escrever novas mensagens, mas os outros utilizadores poderão responder",
"Broadcast_Channel": "Canal de Transmissão",
"Busy": "Ocupado",
@ -111,80 +128,187 @@
"Cancel": "Cancelar",
"changing_avatar": "a alterar avatar",
"creating_channel": "a criar canal",
"creating_invite": "a criar convite",
"Channel_Name": "Nome do Canal",
"Channels": "Canais",
"Chats": "Chats",
"Call_already_ended": "Chamada já terminada!",
"Clear_cookies_alert": "Quer limpar todas as cookies?",
"Clear_cookies_desc": "Esta acção irá limpar todos os cookies de login, permitindo que você faça login em outras contas.",
"Clear_cookies_yes": "Sim, limpar cookies",
"Clear_cookies_no": "Não, guardar cookies",
"Click_to_join": "Clique para Entrar!",
"Close": "Fechar",
"Close_emoji_selector": "Fechar selector de emoticons",
"Closing_chat": "A fechar o chat",
"Change_language_loading": "Mudança de idioma.",
"Chat_closed_by_agent": "Chat fechado por agente",
"Choose": "Escolher",
"Choose_from_library": "Escolher da biblioteca",
"Choose_file": "Escolher arquivo",
"Choose_where_you_want_links_be_opened": "Escolha onde você quer que os links sejam abertos",
"Code": "Código",
"Code_or_password_invalid": "Código ou senha inválidos",
"Collaborative": "Colaborativa",
"Confirm": "Confirmar",
"Connect": "Ligar",
"Connected": "Ligado",
"connecting_server": "conexão ao servidor",
"Connecting": "A ligar...",
"Contact_us": "Contacte-nos",
"Contact_your_server_admin": "Contacte o administrador do seu servidor.",
"Continue_with": "Continuar com",
"Copied_to_clipboard": "Copiado para a área de transferência!",
"Copy": "Copiar",
"Conversation": "Conversa",
"Permalink": "Link permanente",
"Certificate_password": "Senha do Certificado",
"Clear_cache": "Limpar a cache do servidor local",
"Clear_cache_loading": "A limpar a cache.",
"Whats_the_password_for_your_certificate": "Qual é a senha para o seu certificado?",
"Create_account": "Criar uma conta",
"Create_Channel": "Criar Canal",
"Create_Direct_Messages": "Criar Mensagens Diretas",
"Create_Discussion": "Criar Discussão",
"Created_snippet": "criado um extracto",
"Create_a_new_workspace": "Criar um novo espaço de trabalho",
"Create": "Criar",
"Custom_Status": "Status Personalizado",
"Dark": "Escuro",
"Dark_level": "Nível Escuro",
"Default": "Predefinição",
"Default_browser": "Navegador predefinido",
"Delete_Room_Warning": "Apagar uma sala irá remover todas as mensagens contidas nela. Isto não pode ser desfeito.",
"Department": "Departamento",
"delete": "apagar",
"Delete": "Apagar",
"DELETE": "APAGAR",
"move": "mover",
"deleting_room": "apagando sala",
"description": "descrição",
"Description": "Descrição",
"Desktop_Options": "Opções da área de trabalho",
"Desktop_Notifications": "Notificações da área de trabalho",
"Desktop_Alert_info": "Estas notificações são entregues na área de trabalho",
"Directory": "Directório",
"Direct_Messages": "Mensagens Directas",
"Disable_notifications": "Desactivar notificações",
"Discussions": "Discussões",
"Discussion_Desc": "Ajude a manter uma visão geral sobre o que está acontecendo! Ao criar uma discussão, é criado um sub-canal do que você selecionou e ambos estão ligados.",
"Discussion_name": "Nome da discussão",
"Done": "Feito",
"Dont_Have_An_Account": "Não tem uma conta?",
"Do_you_have_an_account": "Você tem uma conta?",
"Do_you_have_a_certificate": "Você tem um certificado?",
"Do_you_really_want_to_key_this_room_question_mark": "Você quer mesmo {{key}} esta sala?",
"E2E_Encryption": "Encriptação E2E",
"E2E_How_It_Works_info1": "Agora você pode criar grupos privados criptografados e mensagens diretas. Você também pode alterar grupos privados existentes ou DMs para criptografados.",
"E2E_How_It_Works_info2": "Isto é *criptografia ponto a ponto* portanto a chave para codificar/descodificar as suas mensagens não será salva no servidor. Por essa razão *você precisa armazenar esta senha em algum lugar seguro* que você posa aceder mais tarde, se precisar.",
"E2E_How_It_Works_info3": "Se você prosseguir, uma senha E2E será gerada automaticamente.",
"E2E_How_It_Works_info4": "Você também pode configurar uma nova senha para sua chave de criptografia a qualquer momento a partir de qualquer navegador que você tenha inserido a senha existente do E2E.",
"edit": "editar",
"edited": "editado",
"Edit": "Editar",
"Edit_Status": "Editar Status",
"Edit_Invite": "Editar Convite",
"End_to_end_encrypted_room": "Sala encriptada de ponta a ponta",
"end_to_end_encryption": "encriptação de ponta a ponta",
"Email_Notification_Mode_All": "Cada Menção/DM",
"Email_Notification_Mode_Disabled": "Desactivado",
"Email_or_password_field_is_empty": "O campo de e-mail ou palavra-passe está vazio",
"Email": "E-mail",
"email": "e-mail",
"Empty_title": "Título vazio",
"Enable_Auto_Translate": "Activar Auto-Tradução",
"Enable_notifications": "Activar notificações",
"Encrypted": "Encriptado",
"Encrypted_message": "Mensagem encriptada",
"Enter_Your_E2E_Password": "Digite a sua senha E2E",
"Enter_Your_Encryption_Password_desc1": "Isto permitir-lhe-á aceder aos seus grupos privados encriptados e às suas mensagens directas.",
"Enter_Your_Encryption_Password_desc2": "Você precisa digitar a senha para codificar/descodificar mensagens em cada lugar que você usar o chat.",
"Encryption_error_title": "A sua senha de encriptação parece errada",
"Encryption_error_desc": "Não foi possível descodificar a sua chave de encriptação para ser importada.",
"Everyone_can_access_this_channel": "Todos podem aceder a este canal",
"Everyone_can_access_this_team": "Todos podem aceder a esta equipa",
"Error_uploading": "Erro ao fazer o envio",
"Expiration_Days": "Validade (Dias)",
"Favorite": "Favorito",
"Favorites": "Favoritos",
"Files": "Ficheiros",
"File_description": "Descrição do ficheiro",
"File_name": "Nome do ficheiro",
"Finish_recording": "Terminar a gravação",
"Following_thread": "Seguir discussão",
"For_your_security_you_must_enter_your_current_password_to_continue": "Para sua segurança, você deve escrever a sua palavra-passe actual para continuar",
"Forgot_password_If_this_email_is_registered": "Se este e-mail estiver registado, enviaremos instruções sobre como repor a sua palavra-passe. Se você não receber um e-mail em breve, volte e tente novamente.",
"Forgot_password": "Esquecer palavra-passe",
"Forgot_Password": "Esquecer Palavra-passe",
"Forward": "Reencaminhar",
"Forward_Chat": "Reencaminhar Chat",
"Forward_to_department": "Reencaminhar para o departamento",
"Forward_to_user": "Reencaminhar para o utilizador",
"Full_table": "Clique para ver a tabela completa",
"Generate_New_Link": "Gerar Novo Link",
"Group_by_favorites": "Agrupar por favoritos",
"Group_by_type": "Agrupar por tipo",
"Hide": "Esconder",
"Has_joined_the_channel": "entrou no canal",
"Has_joined_the_conversation": "entrou na conversa",
"Has_left_the_channel": "saiu do canal",
"Hide_System_Messages": "Esconder mensagens do sistema",
"Hide_type_messages": "Esconder mensagens \"{{type}}\"",
"How_It_Works": "Como Funciona",
"Message_HideType_uj": "Utilizador entrou",
"Message_HideType_ul": "Utilizador saiu",
"Message_HideType_ru": "Utilizador removido",
"Message_HideType_au": "Utilizador adicionado",
"Message_HideType_mute_unmute": "Utilizador silenciado/de-silenciado",
"Message_HideType_r": "Nome da sala alterado",
"Message_HideType_ut": "Utilizador entrou na conversação",
"Message_HideType_wm": "Bem-vindo",
"Message_HideType_rm": "Mensagem Removida",
"Message_HideType_subscription_role_added": "Foi definido o estatuto",
"Message_HideType_subscription_role_removed": "Definição de estatuto removida",
"Message_HideType_room_archived": "Sala arquivada",
"Message_HideType_room_unarchived": "Sala desarquivada",
"I_Saved_My_E2E_Password": "Guardei a minha senha E2E",
"IP": "IP",
"In_app": "Na aplicação",
"In_App_And_Desktop": "Na aplicação e área de trabalho",
"In_App_and_Desktop_Alert_info": "Exibe um banner no topo da tela quando a aplicação está aberto, e exibe uma notificação na área de trabalho",
"Invisible": "Invisível",
"Invite": "Convidar",
"is_a_valid_RocketChat_instance": "é uma instância válida do Rocket.Chat",
"is_not_a_valid_RocketChat_instance": "is not a valid Rocket.Chat instance",
"is_typing": "está a escrever",
"Invalid_or_expired_invite_token": "Token de convite invalido ou expirado",
"Invalid_server_version": "O servidor ao qual esta tentando ligar-se, utiliza uma versão que não é suporta pela aplicação: {{currentVersion}}.\n\nA versão mínima requerida é {{minVersion}}",
"Invite_Link": "Link de convite",
"Invite_users": "Convidar utilizadores",
"Join": "Entrar",
"Join_Code": "Código de entrada",
"Insert_Join_Code": "Insira o código de entrada",
"Join_our_open_workspace": "Junte-se ao nosso espaço de trabalho aberto",
"Join_your_workspace": "Junte-se ao seu espaço de trabalho",
"Just_invited_people_can_access_this_channel": "Apenas utilizadores convidados podem aceder a este canal",
"Just_invited_people_can_access_this_team": "Apenas pessoas convidadas podem aceder a esta equipa",
"Language": "Idioma",
"last_message": "última mensagem",
"Leave_channel": "Sair do canal",
"leaving_room": "a sair da sala",
"Leave": "Sair",
"leave": "sair",
"Legal": "Legal",
"Light": "Luz",
"License": "Licença",
"Livechat": "Livechat",
"Login": "Entrar",
"Login_error": "As suas credenciais foram rejeitadas! Por favor, tente novamente.",
"Login_with": "Entrar com",
"Logging_out": "A terminar a sessão.",
"Logout": "Sair",
"Max_number_of_uses": "Número máximo de utilizações",
"Max_number_of_users_allowed_is_number": "O número máximo de utilizadores permitido é {{maxUsers}}",
"members": "membros",
"Members": "Membros",
"Mentioned_Messages": "Mensagens Mencionadas",
@ -194,7 +318,13 @@
"Message_actions": "Acções de mensagem",
"Message_pinned": "Mensagem afixada",
"Message_removed": "Mensagem removida",
"Message_starred": "Mensagem estrelada",
"Message_unstarred": "Mensagem não estrelada",
"message": "mensagem",
"messages": "mensagens",
"Message": "Mensagem",
"Messages": "Mensagens",
"Message_Reported": "Mensagem reportada",
"Microphone_Permission_Message": "O Rocket.Chat necessita de acesso ao seu microfone para que você possa enviar mensagens de áudio.",
"Microphone_Permission": "Permissão de Microfone",
"Mute": "Silenciar",
@ -202,52 +332,91 @@
"My_servers": "Meus servidores",
"N_people_reacted": "{{n}} pessoas reagiram",
"N_users": "{{n}} utilizadores",
"N_channels": "{{n}} canais",
"name": "nome",
"Name": "Nome",
"Navigation_history": "Histórico de navegação",
"Never": "Nunca",
"New_Message": "Nova Mensagem",
"New_Password": "Nova Palavra-passe",
"New_Server": "Novo Servidor",
"Next": "Próximo",
"No_files": "Nenhum ficheiro",
"No_limit": "Sem limite",
"No_mentioned_messages": "Nenhuma mensagem mencionada",
"No_pinned_messages": "Nenhuma mensagem afixada",
"No_results_found": "Nenhum resultado encontrado",
"No_starred_messages": "Nenhuma mensagem marcada com estrela",
"No_thread_messages": "Sem mensagens de discussão ",
"No_label_provided": "{{label}} não fornecida/o",
"No_Message": "Nenhuma mensagem",
"No_messages_yet": "Ainda sem mensagens",
"No_Reactions": "Nenhuma reação",
"No_Read_Receipts": "Sem recibos de leitura",
"Not_logged": "Não ligado",
"Not_RC_Server": "Isto não é um servidor Rocket.Chat.\n{{contact}}",
"Nothing": "Nada",
"Nothing_to_save": "Nada para guardar!",
"Notify_active_in_this_room": "Notifica utilizadores activos nesta sala",
"Notify_all_in_this_room": "Notifica todos os utilizadores nesta sala",
"Notifications": "Notificações",
"Notification_Duration": "Duração da Notificação",
"Notification_Preferences": "Preferências de Notificação",
"No_available_agents_to_transfer": "Não há agentes disponíveis para transferir",
"Offline": "Desligado",
"Oops": "Oops!",
"Omnichannel": "Omnichannel",
"Open_Livechats": "Chats em andamento",
"Omnichannel_enable_alert": "Você não está disponível no Omnichannel. Você gostaria de estar disponível?",
"Onboarding_description": "Um espaço de trabalho é o espaço da sua equipa ou organização para colaborar. Peça ao administrador do espaço de trabalho um endereço para se juntar ou criar um para a sua equipa.",
"Onboarding_join_workspace": "Junte-se a um espaço de trabalho",
"Onboarding_subtitle": "Além da Colaboração da Equipe",
"Onboarding_title": "Bem vindo(a) ao Rocket.Chat",
"Onboarding_join_open_description": "Junte-se ao nosso espaço de trabalho aberto para conversar com a equipa e comunidade Rocket.Chat.",
"Onboarding_agree_terms": "Ao continuar, você concorda com Rocket.Chat",
"Onboarding_less_options": "Menos opções",
"Onboarding_more_options": "Mais opções",
"Online": "Ligado",
"Only_authorized_users_can_write_new_messages": "Apenas utilizadores autorizados podem escrever novas mensagens",
"Open_emoji_selector": "Abra o selector de emoticons",
"Open_Source_Communication": "Comunicação Open Source",
"Open_your_authentication_app_and_enter_the_code": "Abra o seu aplicativo de autenticação e digite o código.",
"OR": "OU",
"OS": "OS",
"Overwrites_the_server_configuration_and_use_room_config": "Sobrescreve a configuração do servidor e a configuração da sala de uso",
"Password": "Palavra-passe",
"Parent_channel_or_group": "Canal de origem ou grupo",
"Permalink_copied_to_clipboard": "Link permanente copiado para a área de transferência!",
"Phone": "Telefone",
"Pin": "Afixar",
"Pinned_Messages": "Mensagens Afixadas",
"pinned": "afixada",
"Pinned": "Afixada",
"Please_add_a_comment": "Por favor, acrescente um comentário",
"Please_enter_your_password": "Por favor, introduza a sua palavra-passe",
"Please_wait": "Por favor, espere.",
"Preferences": "Preferências",
"Preferences_saved": "Preferências guardadas!",
"Privacy_Policy": " Política de Privacidade",
"Private_Channel": "Canal Privado",
"Private_Groups": "Grupos Privados",
"Private": "Privado",
"Processing": "A processar...",
"Profile_saved_successfully": "Perfil actualizado com sucesso!",
"Profile": "Perfil",
"Public_Channel": "Canal Público",
"Public": "Público",
"Push_Notifications": "Notificações Push",
"Push_Notifications_Alert_Info": "Estas notificações são entregues quando o aplicativo não está aberto",
"Quote": "Citar",
"Reactions_are_disabled": "Reacções desactivadas",
"Reactions_are_enabled": "Reacções activadas",
"Reactions": "Reacções",
"Read": "Ler",
"Read_External_Permission_Message": "Rocket.Chat precisa acessar fotos, média e arquivos em seu dispositivo",
"Read_External_Permission": "Permissão de leitura da média",
"Read_Only_Channel": "Canal só de leitura",
"Read_Only": "Só de Leitura",
"Read_Receipt": "Recibos de leitura",
"Register": "Registar",
"Repeat_Password": "Repita a palavra-passe",
"Reply": "Responder",

View File

@ -400,7 +400,6 @@
"Preferences_saved": "Настройки сохранены!",
"Privacy_Policy": " Политика конфиденциальности",
"Private_Channel": "Приватный канал",
"Private_Groups": "Приватные группы",
"Private": "Приватный",
"Processing": "Обработка...",
"Profile_saved_successfully": "Профиль успешно сохранен!",
@ -724,6 +723,7 @@
"creating_team": "создание Команды",
"team-name-already-exists": "Команда с таким названием уже существует",
"Add_Channel_to_Team": "Добавить канал в Команду",
"Left_The_Team_Successfully": "Успешно покинул команду",
"Create_New": "Создать",
"Add_Existing": "Добавить существующее",
"Add_Existing_Channel": "Добавить существующий канал",

View File

@ -393,7 +393,6 @@
"Preferences_saved": "Tercihler kaydedildi!",
"Privacy_Policy": " Privacy Policy",
"Private_Channel": "Özel Kanal",
"Private_Groups": "Özel Gruplar",
"Private": "Özel",
"Processing": "İşleniyor...",
"Profile_saved_successfully": "Profil başarıyla kaydedildi!",

View File

@ -390,7 +390,6 @@
"Preferences_saved": "偏好已保存!",
"Privacy_Policy": "隐私政策",
"Private_Channel": "私人频道",
"Private_Groups": "私人群组",
"Private": "私有的",
"Processing": "处理中",
"Profile_saved_successfully": "个人资料保存成功!",

View File

@ -391,7 +391,6 @@
"Preferences_saved": "偏好設定已被儲存!",
"Privacy_Policy": "隱私政策",
"Private_Channel": "私人頻道",
"Private_Groups": "私人群組",
"Private": "私有的",
"Processing": "處理中",
"Profile_saved_successfully": "個人資料儲存成功!",

View File

@ -75,7 +75,7 @@ export default class Root extends React.Component {
theme: defaultTheme(),
themePreferences: {
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
darkLevel: 'dark'
darkLevel: 'black'
},
width,
height,

View File

@ -22,7 +22,7 @@ export default class User extends Model {
@field('avatar_etag') avatarETag;
@field('login_email_password') loginEmailPassword;
@field('show_message_in_main_thread') showMessageInMainThread;
@field('is_from_webview') isFromWebView;
}

View File

@ -95,6 +95,16 @@ export default schemaMigrations({
]
})
]
}, {
toVersion: 11,
steps: [
addColumns({
table: 'users',
columns: [
{ name: 'is_from_webview', type: 'boolean', isOptional: true }
]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 10,
version: 11,
tables: [
tableSchema({
name: 'users',
@ -15,7 +15,8 @@ export default appSchema({
{ name: 'roles', type: 'string', isOptional: true },
{ name: 'login_email_password', type: 'boolean', isOptional: true },
{ name: 'show_message_in_main_thread', type: 'boolean', isOptional: true },
{ name: 'avatar_etag', type: 'string', isOptional: true }
{ name: 'avatar_etag', type: 'string', isOptional: true },
{ name: 'is_from_webview', type: 'boolean', isOptional: true }
]
}),
tableSchema({

View File

@ -6,6 +6,7 @@ import { compareServerVersion, methods } from '../utils';
import database from '../database';
import log from '../../utils/log';
import reduxStore from '../createStore';
import RocketChat from '../rocketchat';
import protectedFunction from './helpers/protectedFunction';
import { setPermissions as setPermissionsAction } from '../../actions/permissions';
@ -46,7 +47,8 @@ const PERMISSIONS = [
'view-statistics',
'view-user-administration',
'view-all-teams',
'view-all-team-channels'
'view-all-team-channels',
'convert-team'
];
export async function setPermissions() {
@ -128,7 +130,7 @@ export function getPermissions() {
const db = database.active;
const permissionsCollection = db.get('permissions');
const allRecords = await permissionsCollection.query().fetch();
RocketChat.subscribe('stream-notify-logged', 'permissions-changed');
// if server version is lower than 0.73.0, fetches from old api
if (compareServerVersion(serverVersion, '0.73.0', methods.lowerThan)) {
// RC 0.66.0

View File

@ -2,9 +2,66 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database';
import log from '../../utils/log';
import reduxStore from '../createStore';
import protectedFunction from './helpers/protectedFunction';
import {
removeRoles, setRoles as setRolesAction, updateRoles
} from '../../actions/roles';
export default function() {
export async function setRoles() {
const db = database.active;
const rolesCollection = db.collections.get('roles');
const allRoles = await rolesCollection.query().fetch();
const parsed = allRoles.reduce((acc, item) => ({ ...acc, [item.id]: item.description || item.id }), {});
reduxStore.dispatch(setRolesAction(parsed));
}
export async function onRolesChanged(ddpMessage) {
const { type, _id, description } = ddpMessage.fields.args[0];
if (/changed/.test(type)) {
const db = database.active;
const rolesCollection = db.get('roles');
try {
const rolesRecord = await rolesCollection.find(_id);
try {
await db.action(async() => {
await rolesRecord.update((u) => {
u.description = description;
});
});
} catch (e) {
log(e);
}
reduxStore.dispatch(updateRoles(_id, description));
} catch (err) {
try {
await db.action(async() => {
await rolesCollection.create((post) => {
post._raw = sanitizedRaw({ id: _id, description }, rolesCollection.schema);
});
});
} catch (e) {
log(e);
}
reduxStore.dispatch(updateRoles(_id, description || _id));
}
}
if (/removed/.test(type)) {
const db = database.active;
const rolesCollection = db.get('roles');
try {
const rolesRecord = await rolesCollection.find(_id);
await db.action(async() => {
await rolesRecord.destroyPermanently();
});
reduxStore.dispatch(removeRoles(_id));
} catch (err) {
console.log(err);
}
}
}
export function getRoles() {
const db = database.active;
return new Promise(async(resolve) => {
try {
@ -50,6 +107,7 @@ export default function() {
} catch (e) {
log(e);
}
setRoles();
return allRecords.length;
});
return resolve();

View File

@ -131,6 +131,10 @@ export async function setSettings() {
reduxStore.dispatch(addSettings(RocketChat.parseSettings(parsed.slice(0, parsed.length))));
}
export function subscribeSettings() {
return RocketChat.subscribe('stream-notify-all', 'public-settings-changed');
}
export default async function() {
try {
const db = database.active;

View File

@ -28,7 +28,7 @@ import getUsersPresence, { getUserPresence, subscribeUsersPresence } from './met
import protectedFunction from './methods/helpers/protectedFunction';
import readMessages from './methods/readMessages';
import getSettings, { getLoginSettings, setSettings } from './methods/getSettings';
import getSettings, { getLoginSettings, setSettings, subscribeSettings } from './methods/getSettings';
import getRooms from './methods/getRooms';
import { setPermissions, getPermissions } from './methods/getPermissions';
@ -37,7 +37,7 @@ import {
getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable
} from './methods/enterpriseModules';
import getSlashCommands from './methods/getSlashCommands';
import getRoles from './methods/getRoles';
import { getRoles, setRoles, onRolesChanged } from './methods/getRoles';
import canOpenRoom from './methods/canOpenRoom';
import triggerBlockAction, { triggerSubmitView, triggerCancel } from './methods/actions';
@ -63,7 +63,9 @@ import UserPreferences from './userPreferences';
import { Encryption } from './encryption';
import EventEmitter from '../utils/events';
import { sanitizeLikeString } from './database/utils';
import { updatePermission } from '../actions/permissions';
import { TEAM_TYPE } from '../definition/ITeam';
import { updateSettings } from '../actions/settings';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const CURRENT_SERVER = 'currentServer';
@ -225,6 +227,14 @@ const RocketChat = {
this.usersListener.then(this.stopListener);
}
if (this.notifyAllListener) {
this.notifyAllListener.then(this.stopListener);
}
if (this.rolesListener) {
this.rolesListener.then(this.stopListener);
}
if (this.notifyLoggedListener) {
this.notifyLoggedListener.then(this.stopListener);
}
@ -274,6 +284,31 @@ const RocketChat = {
this.usersListener = this.sdk.onStreamData('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage)));
this.notifyAllListener = this.sdk.onStreamData('stream-notify-all', protectedFunction(async(ddpMessage) => {
const { eventName } = ddpMessage.fields;
if (/public-settings-changed/.test(eventName)) {
const { _id, value } = ddpMessage.fields.args[1];
const db = database.active;
const settingsCollection = db.get('settings');
try {
const settingsRecord = await settingsCollection.find(_id);
const { type } = defaultSettings[_id];
if (type) {
await db.action(async() => {
await settingsRecord.update((u) => {
u[type] = value;
});
});
}
reduxStore.dispatch(updateSettings(_id, value));
} catch (e) {
log(e);
}
}
}));
this.rolesListener = this.sdk.onStreamData('stream-roles', protectedFunction(ddpMessage => onRolesChanged(ddpMessage)));
this.notifyLoggedListener = this.sdk.onStreamData('stream-notify-logged', protectedFunction(async(ddpMessage) => {
const { eventName } = ddpMessage.fields;
if (/user-status/.test(eventName)) {
@ -310,6 +345,21 @@ const RocketChat = {
} catch {
// We can't create a new record since we don't receive the user._id
}
} else if (/permissions-changed/.test(eventName)) {
const { _id, roles } = ddpMessage.fields.args[1];
const db = database.active;
const permissionsCollection = db.get('permissions');
try {
const permissionsRecord = await permissionsCollection.find(_id);
await db.action(async() => {
await permissionsRecord.update((u) => {
u.roles = roles;
});
});
reduxStore.dispatch(updatePermission(_id, roles));
} catch (err) {
//
}
} else if (/Users:NameChanged/.test(eventName)) {
const userNameChanged = ddpMessage.fields.args[0];
const db = database.active;
@ -476,10 +526,10 @@ const RocketChat = {
return this.post('users.forgotPassword', { email }, false);
},
loginTOTP(params, loginEmailPassword) {
loginTOTP(params, loginEmailPassword, isFromWebView = false) {
return new Promise(async(resolve, reject) => {
try {
const result = await this.login(params, loginEmailPassword);
const result = await this.login(params, isFromWebView);
return resolve(result);
} catch (e) {
if (e.data?.error && (e.data.error === 'totp-required' || e.data.error === 'totp-invalid')) {
@ -542,15 +592,15 @@ const RocketChat = {
return this.loginTOTP(params, true);
},
async loginOAuthOrSso(params) {
const result = await this.loginTOTP(params);
reduxStore.dispatch(loginRequest({ resume: result.token }));
async loginOAuthOrSso(params, isFromWebView = true) {
const result = await this.loginTOTP(params, false, isFromWebView);
reduxStore.dispatch(loginRequest({ resume: result.token }, false, isFromWebView));
},
async login(params, loginEmailPassword) {
async login(credentials, isFromWebView = false) {
const sdk = this.shareSDK || this.sdk;
// RC 0.64.0
await sdk.login(params);
await sdk.login(credentials);
const { result } = sdk.currentLogin;
const user = {
id: result.userId,
@ -565,7 +615,7 @@ const RocketChat = {
emails: result.me.emails,
roles: result.me.roles,
avatarETag: result.me.avatarETag,
loginEmailPassword,
isFromWebView,
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true
};
return user;
@ -816,6 +866,13 @@ const RocketChat = {
};
return this.sdk.post(type === 'c' ? 'channels.convertToTeam' : 'groups.convertToTeam', params);
},
convertTeamToChannel({ teamId, selected }) {
const params = {
teamId,
...(selected.length && { roomsToRemove: selected })
};
return this.sdk.post('teams.convertToChannel', params);
},
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0
@ -833,6 +890,7 @@ const RocketChat = {
getSettings,
getLoginSettings,
setSettings,
subscribeSettings,
getPermissions,
setPermissions,
getCustomEmojis,
@ -843,6 +901,7 @@ const RocketChat = {
isOmnichannelModuleAvailable,
getSlashCommands,
getRoles,
setRoles,
parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = defaultSettings[item._id] && item[defaultSettings[item._id].type];
if (item._id === 'Hide_System_Messages') {

View File

@ -19,6 +19,7 @@ import createDiscussion from './createDiscussion';
import enterpriseModules from './enterpriseModules';
import encryption from './encryption';
import permissions from './permissions';
import roles from './roles';
import inquiry from '../ee/omnichannel/reducers/inquiry';
@ -43,5 +44,6 @@ export default combineReducers({
inquiry,
enterpriseModules,
encryption,
permissions
permissions,
roles
});

View File

@ -1,13 +1,16 @@
import { PERMISSIONS } from '../actions/actionsTypes';
const initialState = {
permissions: {}
};
const initialState = {};
export default function permissions(state = initialState, action) {
switch (action.type) {
case PERMISSIONS.SET:
return action.permissions;
case PERMISSIONS.UPDATE:
return {
...state,
[action.payload.id]: action.payload.roles
};
default:
return state;
}

22
app/reducers/roles.js Normal file
View File

@ -0,0 +1,22 @@
import { ROLES } from '../actions/actionsTypes';
const initialState = {};
export default function permissions(state = initialState, action) {
switch (action.type) {
case ROLES.SET:
return action.roles;
case ROLES.UPDATE:
return {
...state,
[action.payload.id]: action.payload.desc || action.payload.id
};
case ROLES.REMOVE: {
const newState = { ...state };
delete newState[action.payload.id];
return newState;
}
default:
return state;
}
}

View File

@ -28,7 +28,7 @@ export default function(state = initialState, action) {
case ROOM.DELETE:
return {
...state,
rid: action.rid,
rid: action.room.rid,
isDeleting: true
};
case ROOM.CLOSE:

View File

@ -9,6 +9,11 @@ export default (state = initialState, action) => {
...state,
...action.payload
};
case SETTINGS.UPDATE:
return {
...state,
[action.payload.id]: action.payload.value
};
case SETTINGS.CLEAR:
return initialState;
default:

View File

@ -41,10 +41,10 @@ const handleRequest = function* handleRequest({ data }) {
encrypted
} = data;
logEvent(events.CT_CREATE, {
type,
readOnly,
broadcast,
encrypted
type: `${ type }`,
readOnly: `${ readOnly }`,
broadcast: `${ broadcast }`,
encrypted: `${ encrypted }`
});
const result = yield call(createTeam, data);
sub = {

View File

@ -97,7 +97,7 @@ const fallbackNavigation = function* fallbackNavigation() {
const handleOAuth = function* handleOAuth({ params }) {
const { credentialToken, credentialSecret } = params;
try {
yield RocketChat.loginOAuthOrSso({ oauth: { credentialToken, credentialSecret } });
yield RocketChat.loginOAuthOrSso({ oauth: { credentialToken, credentialSecret } }, false);
} catch (e) {
log(e);
}

View File

@ -30,15 +30,15 @@ import Navigation from '../lib/Navigation';
const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
const loginCall = args => RocketChat.login(args);
const loginCall = (credentials, isFromWebView) => RocketChat.login(credentials, isFromWebView);
const logoutCall = args => RocketChat.logout(args);
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false }) {
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false, isFromWebView = false }) {
logEvent(events.LOGIN_DEFAULT_LOGIN);
try {
let result;
if (credentials.resume) {
result = yield call(loginCall, credentials);
result = yield loginCall(credentials, isFromWebView);
} else {
result = yield call(loginWithPasswordCall, credentials);
}
@ -68,7 +68,6 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
log(e);
}
});
yield put(loginSuccess(result));
}
} catch (e) {
@ -81,6 +80,10 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
}
};
const subscribeSettings = function* subscribeSettings() {
yield RocketChat.subscribeSettings();
};
const fetchPermissions = function* fetchPermissions() {
yield RocketChat.getPermissions();
};
@ -90,6 +93,7 @@ const fetchCustomEmojis = function* fetchCustomEmojis() {
};
const fetchRoles = function* fetchRoles() {
RocketChat.subscribe('stream-roles', 'roles');
yield RocketChat.getRoles();
};
@ -133,6 +137,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
yield fork(registerPushToken);
yield fork(fetchUsersPresence);
yield fork(fetchEnterpriseModules, { user });
yield fork(subscribeSettings);
yield put(encryptionInit());
setLanguage(user?.language);
@ -147,14 +152,13 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
status: user.status,
statusText: user.statusText,
roles: user.roles,
loginEmailPassword: user.loginEmailPassword,
isFromWebView: user.isFromWebView,
showMessageInMainThread: user.showMessageInMainThread,
avatarETag: user.avatarETag
};
yield serversDB.action(async() => {
try {
const userRecord = await usersCollection.find(user.id);
u.loginEmailPassword = userRecord?.loginEmailPassword;
await userRecord.update((record) => {
record._raw = sanitizedRaw({ id: user.id, ...record._raw }, usersCollection.schema);
Object.assign(record, u);

View File

@ -32,7 +32,7 @@ const watchUserTyping = function* watchUserTyping({ rid, status }) {
}
};
const handleRemovedRoom = function* handleRemovedRoom(roomType) {
const handleRemovedRoom = function* handleRemovedRoom(roomType, actionType) {
const isMasterDetail = yield select(state => state.app.isMasterDetail);
if (isMasterDetail) {
yield Navigation.navigate('DrawerNavigator');
@ -40,9 +40,13 @@ const handleRemovedRoom = function* handleRemovedRoom(roomType) {
yield Navigation.navigate('RoomsListView');
}
if (roomType === 'team') {
EventEmitter.emit(LISTENER, { message: I18n.t('Left_The_Team_Successfully') });
if (actionType === 'leave') {
EventEmitter.emit(LISTENER, { message: roomType === 'team' ? I18n.t('Left_The_Team_Successfully') : I18n.t('Left_The_Room_Successfully') });
}
if (actionType === 'delete') {
EventEmitter.emit(LISTENER, { message: roomType === 'team' ? I18n.t('Deleted_The_Team_Successfully') : I18n.t('Deleted_The_Room_Successfully') });
}
// types.ROOM.REMOVE is triggered by `subscriptions-changed` with `removed` arg
const { timeout } = yield race({
@ -66,7 +70,7 @@ const handleLeaveRoom = function* handleLeaveRoom({ room, roomType, selected })
}
if (result?.success) {
yield handleRemovedRoom(roomType);
yield handleRemovedRoom(roomType, 'leave');
}
} catch (e) {
logEvent(events.RA_LEAVE_F);
@ -80,16 +84,23 @@ const handleLeaveRoom = function* handleLeaveRoom({ room, roomType, selected })
}
};
const handleDeleteRoom = function* handleDeleteRoom({ rid, t }) {
const handleDeleteRoom = function* handleDeleteRoom({ room, roomType, selected }) {
logEvent(events.RI_EDIT_DELETE);
try {
const result = yield RocketChat.deleteRoom(rid, t);
if (result.success) {
yield handleRemovedRoom();
let result = {};
if (roomType === 'channel') {
result = yield RocketChat.deleteRoom(room.rid, room.t);
} else if (roomType === 'team') {
result = yield RocketChat.deleteTeam({ teamId: room.teamId, ...(selected && { roomsToRemove: selected }) });
}
if (result?.success) {
yield handleRemovedRoom(roomType, 'delete');
}
} catch (e) {
logEvent(events.RI_EDIT_DELETE_F);
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('deleting_room') }));
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: roomType === 'team' ? I18n.t('deleting_team') : I18n.t('deleting_room') }));
}
};

View File

@ -125,6 +125,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
RocketChat.setSettings();
RocketChat.setCustomEmojis();
RocketChat.setPermissions();
RocketChat.setRoles();
RocketChat.setEnterpriseModules();
let serverInfo;

View File

@ -124,7 +124,7 @@ class Root extends React.Component {
theme: defaultTheme(),
themePreferences: {
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
darkLevel: 'dark'
darkLevel: 'black'
},
root: '',
width,

View File

@ -259,6 +259,8 @@ export default {
RA_LEAVE_TEAM_F: 'ra_leave_team_f',
RA_CONVERT_TO_TEAM: 'ra_convert_to_team',
RA_CONVERT_TO_TEAM_F: 'ra_convert_to_team_f',
RA_CONVERT_TEAM_TO_CHANNEL: 'ra_convert_team_to_channel',
RA_CONVERT_TEAM_TO_CHANNEL_F: 'ra_convert_team_to_channel_f',
RA_MOVE_TO_TEAM: 'ra_move_to_team',
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
RA_SEARCH_TEAM: 'ra_search_team',

View File

@ -39,16 +39,16 @@ const openLink = async(url, theme = 'light') => {
try {
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
if (browser) {
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
await Linking.openURL(schemeUrl);
} else {
if (browser === 'inApp') {
await WebBrowser.openBrowserAsync(url, {
toolbarColor: themes[theme].headerBackground,
controlsColor: themes[theme].headerTintColor,
collapseToolbar: true,
showTitle: true
});
} else {
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
await Linking.openURL(schemeUrl);
}
} catch {
try {

View File

@ -83,7 +83,7 @@ class DefaultBrowserView extends React.Component {
isSelected = (value) => {
const { browser } = this.state;
if (!browser && value === 'inApp') {
if (!browser && value === 'systemDefault:') {
return true;
}
return browser === value;
@ -92,7 +92,7 @@ class DefaultBrowserView extends React.Component {
changeDefaultBrowser = async(newBrowser) => {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser });
try {
const browser = newBrowser !== 'inApp' ? newBrowser : null;
const browser = newBrowser !== 'systemDefault:' ? newBrowser : null;
await UserPreferences.setStringAsync(DEFAULT_BROWSER_KEY, browser);
this.setState({ browser });
} catch {

View File

@ -50,6 +50,8 @@ class RoomActionsView extends React.Component {
route: PropTypes.object,
leaveRoom: PropTypes.func,
jitsiEnabled: PropTypes.bool,
jitsiEnableTeams: PropTypes.bool,
jitsiEnableChannels: PropTypes.bool,
encryptionEnabled: PropTypes.bool,
setLoadingInvite: PropTypes.func,
closeRoom: PropTypes.func,
@ -65,7 +67,8 @@ class RoomActionsView extends React.Component {
viewBroadcastMemberListPermission: PropTypes.array,
transferLivechatGuestPermission: PropTypes.array,
createTeamPermission: PropTypes.array,
addTeamChannelPermission: PropTypes.array
addTeamChannelPermission: PropTypes.array,
convertTeamPermission: PropTypes.array
}
constructor(props) {
@ -89,7 +92,8 @@ class RoomActionsView extends React.Component {
canEdit: false,
canToggleEncryption: false,
canCreateTeam: false,
canAddChannelToTeam: false
canAddChannelToTeam: false,
canConvertTeam: false
};
if (room && room.observe && room.rid) {
this.roomObservable = room.observe();
@ -140,9 +144,10 @@ class RoomActionsView extends React.Component {
const canViewMembers = await this.canViewMembers();
const canCreateTeam = await this.canCreateTeam();
const canAddChannelToTeam = await this.canAddChannelToTeam();
const canConvertTeam = await this.canConvertTeam();
this.setState({
canAutoTranslate, canAddUser, canInviteUser, canEdit, canToggleEncryption, canViewMembers, canCreateTeam, canAddChannelToTeam
canAutoTranslate, canAddUser, canInviteUser, canEdit, canToggleEncryption, canViewMembers, canCreateTeam, canAddChannelToTeam, canConvertTeam
});
// livechat permissions
@ -238,6 +243,16 @@ class RoomActionsView extends React.Component {
return canAddChannelToTeam;
}
canConvertTeam = async() => {
const { room } = this.state;
const { convertTeamPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission([convertTeamPermission], rid);
const canConvertTeam = permissions[0];
return canConvertTeam;
}
canToggleEncryption = async() => {
const { room } = this.state;
const { toggleRoomE2EEncryptionPermission } = this.props;
@ -431,6 +446,59 @@ class RoomActionsView extends React.Component {
});
}
convertTeamToChannel = async() => {
const { room } = this.state;
const { navigation } = this.props;
try {
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id });
if (result.rooms?.length) {
const teamChannels = result.rooms.map(r => ({
rid: r._id,
name: r.name,
teamId: r.teamId
}));
navigation.navigate('SelectListView', {
title: 'Converting_Team_To_Channel',
data: teamChannels,
infoText: 'Select_Team_Channels_To_Delete',
nextAction: data => this.convertTeamToChannelConfirmation(data)
});
} else {
this.convertTeamToChannelConfirmation();
}
} catch (e) {
this.convertTeamToChannelConfirmation();
}
}
handleConvertTeamToChannel = async(selected) => {
logEvent(events.RA_CONVERT_TEAM_TO_CHANNEL);
try {
const { room } = this.state;
const { navigation } = this.props;
const result = await RocketChat.convertTeamToChannel({ teamId: room.teamId, selected });
if (result.success) {
navigation.navigate('RoomView');
}
} catch (e) {
logEvent(events.RA_CONVERT_TEAM_TO_CHANNEL_F);
log(e);
}
}
convertTeamToChannelConfirmation = (selected = []) => {
showConfirmationAlert({
title: I18n.t('Confirmation'),
message: I18n.t('You_are_converting_the_team'),
confirmationText: I18n.t('Convert'),
onPress: () => this.handleConvertTeamToChannel(selected)
});
}
leaveTeam = async() => {
const { room } = this.state;
const { navigation, leaveRoom } = this.props;
@ -655,10 +723,15 @@ class RoomActionsView extends React.Component {
renderJitsi = () => {
const { room } = this.state;
const { jitsiEnabled } = this.props;
if (!jitsiEnabled || room.teamMain) {
const { jitsiEnabled, jitsiEnableTeams, jitsiEnableChannels } = this.props;
const isJitsiDisabledForTeams = room.teamMain && !jitsiEnableTeams;
const isJitsiDisabledForChannels = !room.teamMain && (room.t === 'p' || room.t === 'c') && !jitsiEnableChannels;
if (!jitsiEnabled || isJitsiDisabledForTeams || isJitsiDisabledForChannels) {
return null;
}
return (
<List.Section>
<List.Separator />
@ -799,6 +872,32 @@ class RoomActionsView extends React.Component {
);
}
teamToChannelActions = (t, room) => {
const { canEdit, canConvertTeam } = this.state;
const canConvertTeamToChannel = canEdit && canConvertTeam && !!room?.teamMain;
return (
<>
{['c', 'p'].includes(t) && canConvertTeamToChannel
? (
<>
<List.Item
title='Convert_to_Channel'
onPress={() => this.onPressTouchable({
event: this.convertTeamToChannel
})}
testID='room-actions-convert-channel-to-team'
left={() => <List.Icon name='channel-public' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
</>
);
}
render() {
const {
room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue
@ -1000,7 +1099,9 @@ class RoomActionsView extends React.Component {
)
: null}
{ this.teamChannelActions(t, room) }
{this.teamToChannelActions(t, room)}
{['l'].includes(t) && !this.isOmnichannelPreview
? (
@ -1078,6 +1179,8 @@ class RoomActionsView extends React.Component {
const mapStateToProps = state => ({
jitsiEnabled: state.settings.Jitsi_Enabled || false,
jitsiEnableTeams: state.settings.Jitsi_Enable_Teams || false,
jitsiEnableChannels: state.settings.Jitsi_Enable_Channels || false,
encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail,
@ -1090,7 +1193,8 @@ const mapStateToProps = state => ({
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest'],
createTeamPermission: state.permissions['create-team'],
addTeamChannelPermission: state.permissions['add-team-channel']
addTeamChannelPermission: state.permissions['add-team-channel'],
convertTeamPermission: state.permissions['convert-team']
});
const mapDispatchToProps = dispatch => ({

View File

@ -292,34 +292,11 @@ class RoomInfoEditView extends React.Component {
}, 100);
}
handleDeleteTeam = async(selected) => {
logEvent(events.RI_EDIT_DELETE_TEAM);
const { navigation, isMasterDetail } = this.props;
const { room } = this.state;
try {
const result = await RocketChat.deleteTeam({ teamId: room.teamId, ...(selected && { roomsToRemove: selected }) });
if (result.success) {
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
} else {
navigation.navigate('RoomsListView');
}
}
} catch (e) {
logEvent(events.RI_EDIT_DELETE_TEAM_F);
log(e);
showErrorAlert(
e.data.error
? I18n.t(e.data.error)
: I18n.t('There_was_an_error_while_action', { action: I18n.t('deleting_team') }),
I18n.t('Cannot_delete')
);
}
}
deleteTeam = async() => {
const { room } = this.state;
const { navigation } = this.props;
const {
navigation, deleteCPermission, deletePPermission, deleteRoom
} = this.props;
try {
const db = database.active;
@ -329,16 +306,27 @@ class RoomInfoEditView extends React.Component {
Q.where('team_main', Q.notEq(true))
);
if (teamChannels.length) {
const teamChannelOwner = [];
for (let i = 0; i < teamChannels.length; i += 1) {
const permissionType = teamChannels[i].t === 'c' ? deleteCPermission : deletePPermission;
// eslint-disable-next-line no-await-in-loop
const permissions = await RocketChat.hasPermission([
permissionType
], teamChannels[i].rid);
if (permissions[0]) { teamChannelOwner.push(teamChannels[i]); }
}
if (teamChannelOwner.length) {
navigation.navigate('SelectListView', {
title: 'Delete_Team',
data: teamChannels,
data: teamChannelOwner,
infoText: 'Select_channels_to_delete',
nextAction: (selected) => {
showConfirmationAlert({
message: I18n.t('You_are_deleting_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
onPress: () => this.handleDeleteTeam(selected)
onPress: () => deleteRoom('team', room, selected)
});
}
});
@ -346,7 +334,7 @@ class RoomInfoEditView extends React.Component {
showConfirmationAlert({
message: I18n.t('You_are_deleting_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
onPress: () => this.handleDeleteTeam()
onPress: () => deleteRoom('team', room)
});
}
} catch (e) {
@ -375,7 +363,7 @@ class RoomInfoEditView extends React.Component {
{
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
style: 'destructive',
onPress: () => deleteRoom(room.rid, room.t)
onPress: () => deleteRoom('channel', room)
}
],
{ cancelable: false }
@ -767,7 +755,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
deleteRoom: (rid, t) => dispatch(deleteRoomAction(rid, t))
deleteRoom: (roomType, room, selected) => dispatch(deleteRoomAction(roomType, room, selected))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RoomInfoEditView));

View File

@ -6,7 +6,6 @@ import { connect } from 'react-redux';
import UAParser from 'ua-parser-js';
import isEmpty from 'lodash/isEmpty';
import database from '../../lib/database';
import { CustomIcon } from '../../lib/Icons';
import Status from '../../containers/Status';
import Avatar from '../../containers/Avatar';
@ -55,7 +54,8 @@ class RoomInfoView extends React.Component {
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
jitsiEnabled: PropTypes.bool,
editRoomPermission: PropTypes.array
editRoomPermission: PropTypes.array,
roles: PropTypes.array
}
constructor(props) {
@ -133,18 +133,9 @@ class RoomInfoView extends React.Component {
return room.t === 'l';
}
getRoleDescription = async(id) => {
const db = database.active;
try {
const rolesCollection = db.get('roles');
const role = await rolesCollection.find(id);
if (role) {
return role.description;
}
return null;
} catch (e) {
return null;
}
getRoleDescription = (id) => {
const { roles } = this.props;
return roles[id];
};
loadVisitor = async() => {
@ -378,7 +369,8 @@ const mapStateToProps = state => ({
rooms: state.room.rooms,
isMasterDetail: state.app.isMasterDetail,
jitsiEnabled: state.settings.Jitsi_Enabled || false,
editRoomPermission: state.permissions['edit-room']
editRoomPermission: state.permissions['edit-room'],
roles: state.roles
});
export default connect(mapStateToProps)(withTheme(RoomInfoView));

View File

@ -49,7 +49,8 @@ class RoomMembersView extends React.Component {
room: PropTypes.object,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
token: PropTypes.string,
roles: PropTypes.array
}),
showActionSheet: PropTypes.func,
theme: PropTypes.string,

View File

@ -269,7 +269,7 @@ class ListContainer extends React.Component {
const { listRef } = this.props;
const index = messages.findIndex(item => item.id === messageId);
if (index > -1) {
listRef.current.getNode().scrollToIndex({ index, viewPosition: 0.5 });
listRef.current.getNode().scrollToIndex({ index, viewPosition: 0.5, viewOffset: 100 });
await new Promise(res => setTimeout(res, 300));
if (!this.viewableItems.map(vi => vi.key).includes(messageId)) {
if (!this.jumping) {

View File

@ -71,7 +71,6 @@ const DISCUSSIONS_HEADER = 'Discussions';
const TEAMS_HEADER = 'Teams';
const CHANNELS_HEADER = 'Channels';
const DM_HEADER = 'Direct_Messages';
const GROUPS_HEADER = 'Private_Groups';
const OMNICHANNEL_HEADER = 'Open_Livechats';
const QUERY_SIZE = 20;
@ -480,13 +479,11 @@ class RoomsListView extends React.Component {
if (groupByType) {
const teams = chats.filter(s => filterIsTeam(s));
const discussions = chats.filter(s => filterIsDiscussion(s));
const channels = chats.filter(s => s.t === 'c' && !filterIsDiscussion(s) && !filterIsTeam(s));
const privateGroup = chats.filter(s => s.t === 'p' && !filterIsDiscussion(s) && !filterIsTeam(s));
const channels = chats.filter(s => (s.t === 'c' || s.t === 'p') && !filterIsDiscussion(s) && !filterIsTeam(s));
const direct = chats.filter(s => s.t === 'd' && !filterIsDiscussion(s) && !filterIsTeam(s));
tempChats = this.addRoomsGroup(teams, TEAMS_HEADER, tempChats);
tempChats = this.addRoomsGroup(discussions, DISCUSSIONS_HEADER, tempChats);
tempChats = this.addRoomsGroup(channels, CHANNELS_HEADER, tempChats);
tempChats = this.addRoomsGroup(privateGroup, GROUPS_HEADER, tempChats);
tempChats = this.addRoomsGroup(direct, DM_HEADER, tempChats);
} else if (showUnread || showFavorites || isOmnichannelAgent) {
tempChats = this.addRoomsGroup(chats, CHATS_HEADER, tempChats);

View File

@ -65,7 +65,7 @@ class SettingsView extends React.Component {
const usersCollection = db.get('users');
try {
const userRecord = await usersCollection.find(user.id);
if (!userRecord.loginEmailPassword) {
if (userRecord.isFromWebView) {
showConfirmationAlert({
title: I18n.t('Clear_cookies_alert'),
message: I18n.t('Clear_cookies_desc'),

View File

@ -30,14 +30,14 @@ const THEMES = [
label: 'Dark',
value: 'dark',
group: THEME_GROUP
}, {
label: 'Dark',
value: 'dark',
group: DARK_GROUP
}, {
label: 'Black',
value: 'black',
group: DARK_GROUP
}, {
label: 'Dark',
value: 'dark',
group: DARK_GROUP
}
];

View File

@ -1,4 +1,5 @@
const random = require('./helpers/random');
const value = random(20);
const data = {
server: 'https://mobile.rocket.chat',
@ -71,5 +72,5 @@ const data = {
email: `mobile+registeringfour${ value }@rocket.chat`
},
random: value
}
};
module.exports = data;

View File

@ -1,4 +1,6 @@
// eslint-disable-next-line import/no-unresolved
const random = require('./helpers/random');
const value = random(20);
const data = {
server: 'https://mobile.rocket.chat',
@ -68,5 +70,5 @@ const data = {
email: `mobile+registeringfour${ value }@rocket.chat`
},
random: value
}
};
module.exports = data;

View File

@ -1,4 +1,6 @@
// eslint-disable-next-line import/no-unresolved
const random = require('./helpers/random');
const value = random(20);
const data = {
server: 'http://localhost:3000',
@ -71,5 +73,5 @@ const data = {
email: `mobile+registeringfour${ value }@rocket.chat`
},
random: value
}
};
module.exports = data;

View File

@ -8,7 +8,7 @@ async function navigateToWorkspace(server = data.server) {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(10000);
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).typeText(`${server}\n`);
await element(by.id('new-server-view-input')).typeText(`${ server }\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible();
}
@ -17,7 +17,7 @@ async function navigateToLogin(server) {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await navigateToWorkspace(server);
await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(4000);
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible();
}
@ -55,7 +55,7 @@ async function logout() {
}
async function mockMessage(message, isThread = false) {
let input = isThread ? 'messagebox-input-thread' : 'messagebox-input';
const input = isThread ? 'messagebox-input-thread' : 'messagebox-input';
await element(by.id(input)).tap();
await element(by.id(input)).typeText(`${ data.random }${ message }`);
await element(by.id('messagebox-send-message')).tap();
@ -107,7 +107,7 @@ async function tapBack() {
await element(by.id('header-back')).atIndex(0).tap();
}
async function sleep(ms) {
function sleep(ms) {
return new Promise(res => setTimeout(res, ms));
}
@ -120,19 +120,19 @@ async function searchRoom(room) {
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible().withTimeout(60000);
}
async function tryTapping(theElement, timeout, longtap = false){
async function tryTapping(theElement, timeout, longtap = false) {
try {
if(longtap){
await theElement.longPress()
if (longtap) {
await theElement.longPress();
} else {
await theElement.tap()
await theElement.tap();
}
} catch(e) {
if(timeout <= 0){ //TODO: Maths. How closely has the timeout been honoured here?
throw e
} catch (e) {
if (timeout <= 0) { // TODO: Maths. How closely has the timeout been honoured here?
throw e;
}
await sleep(100)
await tryTapping(theElement, timeout - 100)
await sleep(100);
await tryTapping(theElement, timeout - 100);
}
}
@ -142,7 +142,7 @@ const checkServer = async(server) => {
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(10000);
await element(by.id('sidebar-close-drawer')).tap();
}
};
async function closeKeyboard() {
if(device.getPlatform() === 'android')

View File

@ -6,99 +6,99 @@ const TEAM_TYPE = {
PRIVATE: 1
};
let server = data.server
const { server } = data;
const rocketchat = axios.create({
baseURL: `${server}/api/v1/`,
baseURL: `${ server }/api/v1/`,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Content-Type': 'application/json;charset=UTF-8'
}
})
});
const login = async (username, password) => {
console.log(`Logging in as user ${username}`)
const login = async(username, password) => {
console.log(`Logging in as user ${ username }`);
const response = await rocketchat.post('login', {
"user": username,
"password": password
})
const userId = response.data.data.userId
const authToken = response.data.data.authToken
rocketchat.defaults.headers.common['X-User-Id'] = userId
rocketchat.defaults.headers.common['X-Auth-Token'] = authToken
user: username,
password
});
const { userId } = response.data.data;
const { authToken } = response.data.data;
rocketchat.defaults.headers.common['X-User-Id'] = userId;
rocketchat.defaults.headers.common['X-Auth-Token'] = authToken;
return { authToken, userId };
}
};
const createUser = async (username, password, name, email) => {
console.log(`Creating user ${username}`)
const createUser = async(username, password, name, email) => {
console.log(`Creating user ${ username }`);
try {
await rocketchat.post('users.create', {
"username": username,
"password": password,
"name": name,
"email": email
})
username,
password,
name,
email
});
} catch (error) {
console.log(JSON.stringify(error))
throw "Failed to create user"
console.log(JSON.stringify(error));
throw new Error('Failed to create user');
}
}
};
const createChannelIfNotExists = async (channelname) => {
console.log(`Creating public channel ${channelname}`)
const createChannelIfNotExists = async(channelname) => {
console.log(`Creating public channel ${ channelname }`);
try {
const room = await rocketchat.post('channels.create', {
"name": channelname
})
return room
name: channelname
});
return room;
} catch (createError) {
try { //Maybe it exists already?
const room = rocketchat.get(`channels.info?roomName=${channelname}`)
return room
try { // Maybe it exists already?
const room = rocketchat.get(`channels.info?roomName=${ channelname }`);
return room;
} catch (infoError) {
console.log(JSON.stringify(createError))
console.log(JSON.stringify(infoError))
throw "Failed to find or create public channel"
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create public channel');
}
}
}
};
const createTeamIfNotExists = async (teamname) => {
console.log(`Creating private team ${teamname}`)
const createTeamIfNotExists = async(teamname) => {
console.log(`Creating private team ${ teamname }`);
try {
await rocketchat.post('teams.create', {
"name": teamname,
"type": TEAM_TYPE.PRIVATE
})
name: teamname,
type: TEAM_TYPE.PRIVATE
});
} catch (createError) {
try { //Maybe it exists already?
await rocketchat.get(`teams.info?teamName=${teamname}`)
try { // Maybe it exists already?
await rocketchat.get(`teams.info?teamName=${ teamname }`);
} catch (infoError) {
console.log(JSON.stringify(createError))
console.log(JSON.stringify(infoError))
throw "Failed to find or create private team"
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private team');
}
}
}
};
const createGroupIfNotExists = async (groupname) => {
console.log(`Creating private group ${groupname}`)
const createGroupIfNotExists = async(groupname) => {
console.log(`Creating private group ${ groupname }`);
try {
await rocketchat.post('groups.create', {
"name": groupname
})
name: groupname
});
} catch (createError) {
try { //Maybe it exists already?
await rocketchat.get(`groups.info?roomName=${groupname}`)
try { // Maybe it exists already?
await rocketchat.get(`groups.info?roomName=${ groupname }`);
} catch (infoError) {
console.log(JSON.stringify(createError))
console.log(JSON.stringify(infoError))
throw "Failed to find or create private group"
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private group');
}
}
}
};
const changeChannelJoinCode = async (roomId, joinCode) => {
console.log(`Changing channel Join Code ${roomId}`)
const changeChannelJoinCode = async(roomId, joinCode) => {
console.log(`Changing channel Join Code ${ roomId }`);
try {
await rocketchat.post('method.call/saveRoomSettings', {
message: JSON.stringify({
@ -108,38 +108,38 @@ const changeChannelJoinCode = async (roomId, joinCode) => {
{ joinCode }
]
})
})
});
} catch (createError) {
console.log(JSON.stringify(createError))
throw "Failed to create protected channel"
console.log(JSON.stringify(createError));
throw new Error('Failed to create protected channel');
}
}
};
const sendMessage = async (user, channel, msg) => {
console.log(`Sending message to ${channel}`)
const sendMessage = async(user, channel, msg) => {
console.log(`Sending message to ${ channel }`);
try {
await login(user.username, user.password);
await rocketchat.post('chat.postMessage', { channel, msg });
} catch (infoError) {
console.log(JSON.stringify(infoError))
throw "Failed to find or create private group"
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private group');
}
}
};
const setup = async () => {
await login(data.adminUser, data.adminPassword)
const setup = async() => {
await login(data.adminUser, data.adminPassword);
for (var userKey in data.users) {
if (data.users.hasOwnProperty(userKey)) {
const user = data.users[userKey]
await createUser(user.username, user.password, user.username, user.email)
for (const userKey in data.users) {
if (Object.prototype.hasOwnProperty.call(data.users, userKey)) {
const user = data.users[userKey];
await createUser(user.username, user.password, user.username, user.email);
}
}
for (var channelKey in data.channels) {
if (data.channels.hasOwnProperty(channelKey)) {
const channel = data.channels[channelKey]
const { data: { channel: { _id } } } = await createChannelIfNotExists(channel.name)
for (const channelKey in data.channels) {
if (Object.prototype.hasOwnProperty.call(data.channels, channelKey)) {
const channel = data.channels[channelKey];
const { data: { channel: { _id } } } = await createChannelIfNotExists(channel.name);
if (channel.joinCode) {
await changeChannelJoinCode(_id, channel.joinCode);
@ -147,35 +147,33 @@ const setup = async () => {
}
}
await login(data.users.regular.username, data.users.regular.password)
await login(data.users.regular.username, data.users.regular.password);
for (var groupKey in data.groups) {
if (data.groups.hasOwnProperty(groupKey)) {
const group = data.groups[groupKey]
await createGroupIfNotExists(group.name)
for (const groupKey in data.groups) {
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
const group = data.groups[groupKey];
await createGroupIfNotExists(group.name);
}
}
for (var teamKey in data.teams) {
if (data.teams.hasOwnProperty(teamKey)) {
const team = data.teams[teamKey]
await createTeamIfNotExists(team.name)
for (const teamKey in data.teams) {
if (Object.prototype.hasOwnProperty.call(data.teams, teamKey)) {
const team = data.teams[teamKey];
await createTeamIfNotExists(team.name);
}
}
return
}
};
const get = (endpoint) => {
console.log(`GET /${ endpoint }`)
console.log(`GET /${ endpoint }`);
return rocketchat.get(endpoint);
}
};
const post = (endpoint, body) => {
console.log(`POST /${ endpoint } ${ JSON.stringify(body) }`)
console.log(`POST /${ endpoint } ${ JSON.stringify(body) }`);
return rocketchat.post(endpoint, body);
}
};
module.exports = {
setup, sendMessage, get, post, login
}
};

View File

@ -1,15 +1,14 @@
const {
expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, sleep, tapBack, mockMessage, searchRoom, logout } = require('../../helpers/app');
navigateToLogin, login, sleep, tapBack, mockMessage, searchRoom, logout
} = require('../../helpers/app');
const platformTypes = require('../../helpers/platformTypes');
const data = require('../../data');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const testuser = data.users.regular
const otheruser = data.users.alternate
const testuser = data.users.regular;
const otheruser = data.users.alternate;
const checkServer = async(server) => {
const label = `Connected to ${ server }`;
@ -17,7 +16,7 @@ const checkServer = async(server) => {
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000);
await element(by.id('sidebar-close-drawer')).tap();
}
};
const checkBanner = async() => {
await waitFor(element(by.id('listheader-encryption').withDescendant(by.text('Save Your Encryption Password')))).toBeVisible().withTimeout(10000);
@ -49,7 +48,7 @@ describe('E2E Encryption', () => {
const newPassword = 'abc';
let alertButtonType, scrollViewType;
before(async () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await prepareAndroid();
({ alertButtonType, scrollViewType } = platformTypes[device.getPlatform()]);
@ -57,14 +56,14 @@ describe('E2E Encryption', () => {
await login(testuser.username, testuser.password);
});
describe('Banner', async() => {
describe('Render', async () => {
it('should have encryption badge', async () => {
describe('Banner', () => {
describe('Render', () => {
it('should have encryption badge', async() => {
await checkBanner();
});
});
describe('Usage', async () => {
describe('Usage', () => {
it('should tap encryption badge and open save password modal', async() => {
await element(by.id('listheader-encryption')).tap();
await waitFor(element(by.id('e2e-save-password-view'))).toBeVisible().withTimeout(2000);
@ -104,9 +103,9 @@ describe('E2E Encryption', () => {
await tapBack();
});
});
})
});
describe('Security and Privacy', async() => {
describe('Security and Privacy', () => {
it('should navigate to security privacy', async() => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await element(by.id('rooms-list-view-sidebar')).tap();
@ -126,7 +125,7 @@ describe('E2E Encryption', () => {
});
});
describe('E2E Encryption Security', async() => {
describe('E2E Encryption Security', () => {
it('should navigate to e2e encryption security', async() => {
await element(by.id('security-privacy-view-e2e-encryption')).tap();
await waitFor(element(by.id('e2e-encryption-security-view'))).toBeVisible().withTimeout(2000);
@ -139,9 +138,9 @@ describe('E2E Encryption', () => {
await expect(element(by.id('e2e-encryption-security-view-change-password').and(by.label('Save Changes')))).toExist();
await expect(element(by.id('e2e-encryption-security-view-reset-key').and(by.label('Reset E2E Key')))).toExist();
});
})
});
describe('Change password', async() => {
describe('Change password', () => {
it('should change password', async() => {
await element(by.id('e2e-encryption-security-view-password')).typeText(newPassword);
await element(by.id('e2e-encryption-security-view-change-password')).tap();
@ -190,7 +189,7 @@ describe('E2E Encryption', () => {
});
});
describe('Reset E2E key', async() => {
describe('Reset E2E key', () => {
it('should reset e2e key', async() => {
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
@ -200,10 +199,8 @@ describe('E2E Encryption', () => {
await element(by.id('e2e-encryption-security-view-reset-key').and(by.label('Reset E2E Key'))).tap();
await waitFor(element(by.text('Are you sure?'))).toExist().withTimeout(2000);
await expect(element(by.text('You\'re going to be logged out.'))).toExist();
await element(by.text('Yes, reset it').and(by.type(alertButtonType))).tap();
await sleep(2000)
await waitFor(element(by.text('OK').and(by.type(alertButtonType)))).toExist().withTimeout(2000);
await element(by.text('OK').and(by.type(alertButtonType))).tap();
await element(by.label('Yes, reset it').and(by.type(alertButtonType))).tap();
await sleep(2000);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(10000);
await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
@ -217,7 +214,7 @@ describe('E2E Encryption', () => {
it('check save banner', async() => {
await checkServer(data.server);
await checkBanner();
})
});
it('should add server and create new user', async() => {
await sleep(5000);
@ -227,7 +224,7 @@ describe('E2E Encryption', () => {
// TODO: refactor
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`);
await element(by.id('new-server-view-input')).typeText(`${ data.alternateServer }\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);

View File

@ -1,14 +1,14 @@
// const OTP = require('otp.js');
// const GA = OTP.googleAuthenticator;
const {
device, expect, element, by, waitFor
} = require('detox');
const OTP = require('otp.js');
const GA = OTP.googleAuthenticator;
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
navigateToLogin, login, mockMessage, tapBack, searchRoom
} = require('../../helpers/app');
const data = require('../../data');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const testuser = data.users.regular
const otheruser = data.users.alternate
const testuser = data.users.regular;
const otheruser = data.users.alternate;
describe('Broadcast room', () => {
before(async() => {
@ -30,7 +30,7 @@ describe('Broadcast room', () => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(`broadcast${ data.random }`);
await element(by.id('create-channel-broadcast')).longPress(); //https://github.com/facebook/react-native/issues/28032
await element(by.id('create-channel-broadcast')).longPress(); // https://github.com/facebook/react-native/issues/28032
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
@ -56,11 +56,11 @@ describe('Broadcast room', () => {
await navigateToLogin();
await login(otheruser.username, otheruser.password);
//await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000);
//await expect(element(by.id('two-factor'))).toBeVisible();
//const code = GA.gen(data.alternateUserTOTPSecret);
//await element(by.id('two-factor-input')).replaceText(code);
//await element(by.id('two-factor-send')).tap();
// await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000);
// await expect(element(by.id('two-factor'))).toBeVisible();
// const code = GA.gen(data.alternateUserTOTPSecret);
// await element(by.id('two-factor-input')).replaceText(code);
// await element(by.id('two-factor-send')).tap();
await searchRoom(`broadcast${ data.random }`);
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();

View File

@ -1,13 +1,10 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, sleep } = require('../../helpers/app');
const data = require('../../data');
const platformTypes = require('../../helpers/platformTypes');
const { closeKeyboardAndroid, prepareAndroid } = require('../../helpers/platformFunctions');
const profileChangeUser = data.users.profileChanges
const profileChangeUser = data.users.profileChanges;
const scrollDown = 200;
@ -35,7 +32,7 @@ describe('Profile screen', () => {
await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000);
});
describe('Render', async() => {
describe('Render', () => {
it('should have profile view', async() => {
await expect(element(by.id('profile-view'))).toBeVisible();
});
@ -81,7 +78,7 @@ describe('Profile screen', () => {
});
});
describe('Usage', async() => {
describe('Usage', () => {
it('should change name and username', async() => {
await element(by.id('profile-view-name')).replaceText(`${ profileChangeUser.username }new`);
await element(by.id('profile-view-username')).typeText(`${ profileChangeUser.username }new`);
@ -96,7 +93,8 @@ describe('Profile screen', () => {
await element(by.id('profile-view-new-password')).replaceText(`${ profileChangeUser.password }new`);
await element(by.id('profile-view-submit')).tap();
await element(by.type(textInputType)).typeText(`${ profileChangeUser.password }\n`);
await element(by.text('SAVE')).tap();
// TODO: Check if this is fine on iOS
if(device.getPlatform() === 'android') await element(by.text('SAVE')).tap();
await waitForToast();
});

View File

@ -1,14 +1,11 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, tapBack } = require('../../helpers/app');
const { navigateToLogin, login } = require('../../helpers/app');
const platformTypes = require('../../helpers/platformTypes');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const data = require('../../data');
const testuser = data.users.regular
const testuser = data.users.regular;
describe('Settings screen', () => {
let alertButtonType;
@ -26,7 +23,7 @@ describe('Settings screen', () => {
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
});
describe('Render', async() => {
describe('Render', () => {
it('should have settings view', async() => {
await expect(element(by.id('settings-view'))).toBeVisible();
});
@ -68,7 +65,7 @@ describe('Settings screen', () => {
});
});
describe('Usage', async() => {
describe('Usage', () => {
it('should tap clear cache and navigate to roomslistview', async() => {
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
await element(by.id('settings-view-clear-cache')).tap();
@ -76,6 +73,6 @@ describe('Settings screen', () => {
await element(by.text('Clear').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${ data.groups.private.name }`))).toExist().withTimeout(10000);
})
});
});
});

View File

@ -1,13 +1,12 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
const {
navigateToLogin, login, mockMessage, tapBack, searchRoom
} = require('../../helpers/app');
const platformTypes = require('../../helpers/platformTypes');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const testuser = data.users.regular
const testuser = data.users.regular;
const room = data.channels.detoxpublic.name;
async function navigateToRoom() {
@ -32,7 +31,7 @@ describe('Join public room', () => {
await navigateToRoom();
});
describe('Render', async() => {
describe('Render', () => {
it('should have room screen', async() => {
await expect(element(by.id('room-view'))).toBeVisible();
});
@ -42,14 +41,14 @@ describe('Join public room', () => {
// });
// Render - Header
describe('Header', async() => {
describe('Header', () => {
it('should have actions button ', async() => {
await expect(element(by.id('room-header'))).toBeVisible();
});
});
// Render - Join
describe('Join', async() => {
describe('Join', () => {
it('should have join', async() => {
await expect(element(by.id('room-view-join'))).toBeVisible();
});
@ -67,7 +66,7 @@ describe('Join public room', () => {
});
});
describe('Room Actions', async() => {
describe('Room Actions', () => {
before(async() => {
await navigateToRoomActions();
});
@ -123,11 +122,11 @@ describe('Join public room', () => {
after(async() => {
await tapBack();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
})
});
});
});
describe('Usage', async() => {
describe('Usage', () => {
it('should join room', async() => {
await element(by.id('room-view-join-button')).tap();
await tapBack();

View File

@ -1,18 +1,16 @@
const {
expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, sleep } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const data = require('../../data');
const testuser = data.users.regular
const testuser = data.users.regular;
async function waitForToast() {
await sleep(300);
}
describe('Status screen', () => {
before(async () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await prepareAndroid();
await navigateToLogin();
@ -26,8 +24,8 @@ describe('Status screen', () => {
await waitFor(element(by.id('status-view'))).toBeVisible().withTimeout(2000);
});
describe('Render', async () => {
it('should have status input', async () => {
describe('Render', () => {
it('should have status input', async() => {
await expect(element(by.id('status-view-input'))).toBeVisible();
await expect(element(by.id('status-view-online'))).toExist();
await expect(element(by.id('status-view-busy'))).toExist();
@ -36,13 +34,13 @@ describe('Status screen', () => {
});
});
describe('Usage', async () => {
it('should change status', async () => {
describe('Usage', () => {
it('should change status', async() => {
await element(by.id('status-view-busy')).tap();
await waitFor(element(by.id('status-view-current-busy'))).toExist().withTimeout(2000);
});
it('should change status text', async () => {
it('should change status text', async() => {
await element(by.id('status-view-input')).typeText('status-text-new');
await element(by.id('status-view-submit')).tap();
await waitForToast();

View File

@ -1,6 +1,3 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, checkServer } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -10,7 +7,7 @@ const reopenAndCheckServer = async(server) => {
await device.launchApp({ permissions: { notifications: 'YES' } });
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(6000);
await checkServer(server);
}
};
describe('Change server', () => {
before(async() => {
@ -27,7 +24,7 @@ describe('Change server', () => {
await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(6000);
await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`);
await element(by.id('new-server-view-input')).typeText(`${ data.alternateServer }\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(10000);
await reopenAndCheckServer(data.server);
});
@ -67,5 +64,5 @@ describe('Change server', () => {
it('should reopen the app and show main server', async() => {
await reopenAndCheckServer(data.server);
})
});
});

View File

@ -1,13 +1,10 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
const { navigateToLogin, login, mockMessage, searchRoom } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const testuser = data.users.regular
const room = data.channels.detoxpublicprotected.name
const joinCode = data.channels.detoxpublicprotected.joinCode
const testuser = data.users.regular;
const room = data.channels.detoxpublicprotected.name;
const { joinCode } = data.channels.detoxpublicprotected;
async function navigateToRoom() {
await searchRoom(room);
@ -29,10 +26,10 @@ describe('Join protected room', () => {
await navigateToRoom();
});
describe('Usage', async() => {
describe('Usage', () => {
it('should tap join and ask for join code', async() => {
await openJoinCode();
})
});
it('should cancel join room', async() => {
await element(by.id('join-code-cancel')).tap();

View File

@ -1,11 +1,10 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, tapBack, sleep } = require('../../helpers/app');
const {
navigateToLogin, login, tapBack, sleep
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const testuser = data.users.regular
const testuser = data.users.regular;
async function navigateToRoom(search) {
await element(by.id('directory-view-search')).replaceText(search);
@ -24,15 +23,15 @@ describe('Join room from directory', () => {
await login(testuser.username, testuser.password);
});
describe('Usage', async() => {
describe('Usage', () => {
it('should tap directory', async() => {
await element(by.id('rooms-list-view-directory')).tap();
await waitFor(element(by.id('directory-view'))).toExist().withTimeout(2000);
})
});
it('should search public channel and navigate', async() => {
await navigateToRoom(data.channels.detoxpublic.name);
})
});
it('should search user and navigate', async() => {
await tapBack();
@ -42,7 +41,7 @@ describe('Join room from directory', () => {
await element(by.label('Users')).tap();
await element(by.label('Search by')).tap();
await navigateToRoom(data.users.alternate.username);
})
});
it('should search user and navigate', async() => {
await tapBack();
@ -52,6 +51,6 @@ describe('Join room from directory', () => {
await element(by.label('Teams')).tap();
await element(by.label('Search by')).tap();
await navigateToRoom(data.teams.private.name);
})
});
});
});

View File

@ -1,8 +1,7 @@
const {
device, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { sleep, navigateToLogin, login, checkServer } = require('../../helpers/app');
const {
sleep, navigateToLogin, login, checkServer
} = require('../../helpers/app');
const platformTypes = require('../../helpers/platformTypes');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -19,7 +18,7 @@ describe('Delete server', () => {
it('should be logged in main server', async() => {
await checkServer(data.server);
})
});
it('should add server', async() => {
await sleep(5000);
@ -28,7 +27,7 @@ describe('Delete server', () => {
await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(10000);
await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}\n`);
await element(by.id('new-server-view-input')).typeText(`${ data.alternateServer }\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(10000);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);

View File

@ -1,9 +1,6 @@
const {
device, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, checkServer, navigateToRegister } = require('../../helpers/app');
const { post, get, login } = require('../../helpers/data_setup');
const { get, login } = require('../../helpers/data_setup');
const platformTypes = require('../../helpers/platformTypes');
const { closeKeyboardAndroid, prepareAndroid } = require('../../helpers/platformFunctions');
@ -50,7 +47,7 @@ describe('Deep linking', () => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await checkServer(data.server);
await waitFor(element(by.id(`rooms-list-view-item-${ data.groups.private.name }`))).toBeVisible().withTimeout(2000);
}
};
it('should authenticate and navigate', async() => {
await authAndNavigate();
@ -72,7 +69,7 @@ describe('Deep linking', () => {
});
describe('Room', () => {
describe('While logged in', async() => {
describe('While logged in', () => {
it('should navigate to the room using path', async() => {
await device.launchApp({
permissions: { notifications: 'YES' },
@ -84,7 +81,7 @@ describe('Deep linking', () => {
});
it('should navigate to the room using rid', async() => {
const roomResult = await get(`groups.info?roomName=${ data.groups.private.name }`)
const roomResult = await get(`groups.info?roomName=${ data.groups.private.name }`);
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
@ -96,7 +93,7 @@ describe('Deep linking', () => {
});
});
describe('Others', async() => {
describe('Others', () => {
it('should change server', async() => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();

View File

@ -1,13 +1,13 @@
const detox = require('detox');
const config = require('../../package.json').detox;
const { setup } = require('../helpers/data_setup')
const adapter = require('detox/runners/mocha/adapter');
const config = require('../../package.json').detox;
const { setup } = require('../helpers/data_setup');
before(async() => {
await Promise.all([setup(), detox.init(config, { launchApp: false })])
//await dataSetup()
//await detox.init(config, { launchApp: false });
//await device.launchApp({ permissions: { notifications: 'YES' } });
await Promise.all([setup(), detox.init(config, { launchApp: false })]);
// await dataSetup()
// await detox.init(config, { launchApp: false });
// await device.launchApp({ permissions: { notifications: 'YES' } });
});
beforeEach(async function() {

View File

@ -1,6 +1,3 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -52,7 +49,7 @@ describe('Onboarding', () => {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).typeText(`${data.server}\n`);
await element(by.id('new-server-view-input')).typeText(`${ data.server }\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
});
});

View File

@ -1,11 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { navigateToRegister, navigateToLogin } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
describe('Legal screen', () => {
describe('From Login', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
@ -20,7 +16,7 @@ describe('Legal screen', () => {
it('should navigate to legal from login', async() => {
await expect(element(by.id('login-view-more'))).toBeVisible();
await element(by.id('login-view-more')).tap();
await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000)
await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000);
});
});

View File

@ -1,6 +1,3 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -14,7 +11,7 @@ describe('Forgot password screen', () => {
await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000);
});
describe('Render', async() => {
describe('Render', () => {
it('should have forgot password screen', async() => {
await expect(element(by.id('forgot-password-view'))).toExist();
});
@ -28,7 +25,7 @@ describe('Forgot password screen', () => {
});
});
describe('Usage', async() => {
describe('Usage', () => {
it('should reset password and navigate to login', async() => {
await element(by.id('forgot-password-view-email')).replaceText(data.users.existing.email);
await element(by.id('forgot-password-view-submit')).tap();

View File

@ -1,7 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { navigateToRegister, sleep } = require('../../helpers/app');
const { navigateToRegister } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const data = require('../../data');
@ -39,7 +39,6 @@ describe('Create user screen', () => {
});
describe('Usage', () => {
// FIXME: Detox isn't able to check if it's tappable: https://github.com/wix/Detox/issues/246
// it('should submit invalid email and do nothing', async() => {
// const invalidEmail = 'invalidemail';

View File

@ -1,7 +1,7 @@
const {
expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, tapBack, sleep } = require('../../helpers/app');
const { navigateToLogin, tapBack } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const data = require('../../data');

View File

@ -1,17 +1,16 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { login, navigateToLogin, logout, tapBack, sleep, searchRoom } = require('../../helpers/app');
const { login, navigateToLogin, logout, tapBack, searchRoom } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const data = require('../../data');
describe('Rooms list screen', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
await prepareAndroid();
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password)
await login(data.users.regular.username, data.users.regular.password);
});
describe('Render', () => {

View File

@ -18,7 +18,7 @@ describe('Server history', () => {
await logout();
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
})
});
it('should show servers history', async() => {
await element(by.id('new-server-view-input')).tap();

View File

@ -1,8 +1,5 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app');
const { tapBack, navigateToLogin, login, tryTapping } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -15,12 +12,12 @@ describe('Create room screen', () => {
await login(data.users.regular.username, data.users.regular.password);
});
describe('New Message', async() => {
describe('New Message', () => {
before(async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
});
describe('Render', async() => {
describe('Render', () => {
it('should have new message screen', async() => {
await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
});
@ -28,9 +25,9 @@ describe('Create room screen', () => {
it('should have search input', async() => {
await waitFor(element(by.id('new-message-view-search'))).toBeVisible().withTimeout(2000);
});
})
});
describe('Usage', async() => {
describe('Usage', () => {
it('should back to rooms list', async() => {
await waitFor(element(by.id('new-message-view-close'))).toBeVisible().withTimeout(5000);
await element(by.id('new-message-view-close')).tap();
@ -38,7 +35,7 @@ describe('Create room screen', () => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(5000);
await tryTapping(element(by.id('rooms-list-view-create-channel')), 3000);
//await element(by.id('rooms-list-view-create-channel')).tap();
// await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(5000);
});
@ -58,13 +55,13 @@ describe('Create room screen', () => {
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(5000);
});
})
});
});
describe('Select Users', async() => {
describe('Select Users', () => {
it('should search users', async() => {
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id(`select-users-view-item-rocket.cat`))).toBeVisible().withTimeout(10000);
await waitFor(element(by.id('select-users-view-item-rocket.cat'))).toBeVisible().withTimeout(10000);
});
it('should select/unselect user', async() => {
@ -82,24 +79,24 @@ describe('Create room screen', () => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(10000);
});
})
});
describe('Create Channel', async() => {
describe('Render', async() => {
describe('Create Channel', () => {
describe('Render', () => {
it('should render all fields', async() => {
await expect(element(by.id('create-channel-name'))).toBeVisible();
await expect(element(by.id('create-channel-type'))).toBeVisible();
await expect(element(by.id('create-channel-readonly'))).toBeVisible();
await expect(element(by.id('create-channel-broadcast'))).toBeVisible();
})
})
});
});
describe('Usage', async() => {
describe('Usage', () => {
it('should get invalid room', async() => {
await element(by.id('create-channel-name')).typeText('general');
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.text(`A channel with name general exists`))).toExist().withTimeout(60000);
await expect(element(by.text(`A channel with name general exists`))).toExist();
await waitFor(element(by.text('A channel with name general exists'))).toExist().withTimeout(60000);
await expect(element(by.text('A channel with name general exists'))).toExist();
await element(by.text('OK')).tap();
});
@ -163,6 +160,6 @@ describe('Create room screen', () => {
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toExist();
});
})
});
});
});

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping, logout } = require('../../helpers/app');
const {
navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
async function navigateToRoom(roomName) {
@ -22,14 +21,14 @@ describe('Room screen', () => {
await navigateToRoom(mainRoom);
});
describe('Render', async() => {
describe('Render', () => {
it('should have room screen', async() => {
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ mainRoom }`))).toExist().withTimeout(5000);
});
// Render - Header
describe('Header', async() => {
describe('Header', () => {
it('should have actions button ', async() => {
await expect(element(by.id('room-header'))).toExist();
});
@ -40,7 +39,7 @@ describe('Room screen', () => {
});
// Render - Messagebox
describe('Messagebox', async() => {
describe('Messagebox', () => {
it('should have messagebox', async() => {
await expect(element(by.id('messagebox'))).toExist();
});
@ -65,15 +64,15 @@ describe('Room screen', () => {
});
});
describe('Usage', async() => {
describe('Messagebox', async() => {
describe('Usage', () => {
describe('Messagebox', () => {
it('should send message', async() => {
await mockMessage('message')
await mockMessage('message');
await expect(element(by.text(`${ data.random }message`)).atIndex(0)).toExist();
});
it('should show/hide emoji keyboard', async () => {
it('should show/hide emoji keyboard', async() => {
if (device.getPlatform() === 'android') {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji'))).toExist().withTimeout(10000);
@ -113,14 +112,14 @@ describe('Room screen', () => {
});
it('should show and tap on user autocomplete and send mention', async() => {
const username = data.users.regular.username
const { username } = data.users.regular;
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`@${ username }`);
await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000);
await waitFor(element(by.id(`mention-item-${ username }`))).toBeVisible().withTimeout(4000)
await waitFor(element(by.id(`mention-item-${ username }`))).toBeVisible().withTimeout(4000);
await tryTapping(element(by.id(`mention-item-${ username }`)), 2000, true);
await expect(element(by.id('messagebox-input'))).toHaveText(`@${ username } `);
await tryTapping(element(by.id('messagebox-input')), 2000)
await tryTapping(element(by.id('messagebox-input')), 2000);
await element(by.id('messagebox-input')).typeText(`${ data.random }mention`);
await element(by.id('messagebox-send-message')).tap();
// await waitFor(element(by.label(`@${ data.user } ${ data.random }mention`)).atIndex(0)).toExist().withTimeout(60000);
@ -128,7 +127,7 @@ describe('Room screen', () => {
it('should not show user autocomplete on @ in the middle of a string', async() => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`email@gmail`);
await element(by.id('messagebox-input')).typeText('email@gmail');
await waitFor(element(by.id('messagebox-container'))).toNotExist().withTimeout(4000);
await element(by.id('messagebox-input')).clearText();
});
@ -136,9 +135,9 @@ describe('Room screen', () => {
it('should show and tap on room autocomplete', async() => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText('#general');
//await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000);
// await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000);
await waitFor(element(by.id('mention-item-general'))).toBeVisible().withTimeout(4000);
await tryTapping(element(by.id('mention-item-general')), 2000, true)
await tryTapping(element(by.id('mention-item-general')), 2000, true);
await expect(element(by.id('messagebox-input'))).toHaveText('#general ');
await element(by.id('messagebox-input')).clearText();
});
@ -149,7 +148,7 @@ describe('Room screen', () => {
await waitFor(element(by.id('messagebox-container'))).toNotExist().withTimeout(4000);
await element(by.id('messagebox-input')).clearText();
});
it('should draft message', async () => {
it('should draft message', async() => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`${ data.random }draft`);
await tapBack();
@ -164,7 +163,7 @@ describe('Room screen', () => {
});
});
describe('Message', async() => {
describe('Message', () => {
it('should copy permalink', async() => {
await element(by.text(`${ data.random }message`)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
@ -186,9 +185,9 @@ describe('Room screen', () => {
});
it('should star message', async() => {
await starMessage('message')
await starMessage('message');
await sleep(1000) //https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324
await sleep(1000); //https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324
await element(by.text(`${ data.random }message`)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
@ -231,9 +230,9 @@ describe('Room screen', () => {
await waitFor(element(by.id('message-reaction-:grimacing:'))).toExist().withTimeout(60000);
});
// it('should ask for review', async() => {
// //await dismissReviewNag(); //TODO: Create a proper test for this elsewhere.
// })
it('should ask for review', async() => {
await dismissReviewNag(); // TODO: Create a proper test for this elsewhere.
});
it('should remove reaction', async() => {
await element(by.id('message-reaction-:grinning:')).tap();
@ -266,8 +265,8 @@ describe('Room screen', () => {
});
it('should pin message', async() => {
await mockMessage('pin')
await pinMessage('pin')
await mockMessage('pin');
await pinMessage('pin');
await waitFor(element(by.text(`${ data.random }pin`)).atIndex(0)).toExist().withTimeout(5000);
await waitFor(element(by.text(`${ data.users.regular.username } Message pinned`)).atIndex(0)).toExist().withTimeout(5000);
@ -280,7 +279,7 @@ describe('Room screen', () => {
});
it('should delete message', async() => {
await mockMessage('delete')
await mockMessage('delete');
await waitFor(element(by.text(`${ data.random }delete`)).atIndex(0)).toBeVisible();
await element(by.text(`${ data.random }delete`)).atIndex(0).longPress();

View File

@ -1,6 +1,3 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, tapBack, sleep, searchRoom, mockMessage, starMessage, pinMessage } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -48,8 +45,8 @@ describe('Room actions screen', () => {
({ alertButtonType } = platformTypes[device.getPlatform()]);
});
describe('Render', async() => {
describe('Direct', async() => {
describe('Render', () => {
describe('Direct', () => {
before(async() => {
await navigateToRoomActions('d');
});
@ -107,7 +104,7 @@ describe('Room actions screen', () => {
});
});
describe('Channel/Group', async() => {
describe('Channel/Group', () => {
before(async() => {
await navigateToRoomActions('c');
});
@ -170,7 +167,7 @@ describe('Room actions screen', () => {
});
});
describe('Usage', async() => {
describe('Usage', () => {
describe('TDB', async() => {
// TODO: test into a jitsi call
// it('should NOT navigate to voice call', async() => {
@ -196,7 +193,7 @@ describe('Room actions screen', () => {
// });
});
describe('Common', async() => {
describe('Common', () => {
it('should show mentioned messages', async() => {
await element(by.id('room-actions-mentioned')).tap();
await waitFor(element(by.id('mentioned-messages-view'))).toExist().withTimeout(2000);
@ -205,15 +202,14 @@ describe('Room actions screen', () => {
});
it('should show starred message and unstar it', async() => {
//Go back to room and send a message
// Go back to room and send a message
await tapBack();
await mockMessage('messageToStar');
//Star the message
await starMessage('messageToStar')
// Star the message
await starMessage('messageToStar');
//Back into Room Actions
// Back into Room Actions
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
@ -235,15 +231,14 @@ describe('Room actions screen', () => {
});
it('should show pinned message and unpin it', async() => {
//Go back to room and send a message
// Go back to room and send a message
await tapBack();
await mockMessage('messageToPin');
//Pin the message
await pinMessage('messageToPin')
// Pin the message
await pinMessage('messageToPin');
//Back into Room Actions
// Back into Room Actions
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
@ -280,7 +275,7 @@ describe('Room actions screen', () => {
// });
});
describe('Notification', async() => {
describe('Notification', () => {
it('should navigate to notification preference view', async() => {
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000);
@ -325,13 +320,13 @@ describe('Room actions screen', () => {
after(async() => {
await backToActions();
});
})
});
describe('Channel/Group', async() => {
describe('Channel/Group', () => {
// Currently, there's no way to add more owners to the room
// So we test only for the 'You are the last owner...' message
const user = data.users.alternate
const user = data.users.alternate;
it('should tap on leave channel and raise alert', async() => {
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
@ -370,12 +365,25 @@ describe('Room actions screen', () => {
await backToActions();
});
describe('Room Members', async() => {
describe('Room Members', () => {
before(async() => {
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view'))).toExist().withTimeout(2000);
});
const openActionSheet = async(username) => {
await waitFor(element(by.id(`room-members-view-item-${ username }`))).toExist().withTimeout(5000);
await element(by.id(`room-members-view-item-${ username }`)).tap();
await sleep(300);
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up');
};
const closeActionSheet = async() => {
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6);
};
it('should show all users', async() => {
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
@ -404,19 +412,6 @@ describe('Room actions screen', () => {
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
});
const openActionSheet = async(username) => {
await waitFor(element(by.id(`room-members-view-item-${ username }`))).toExist().withTimeout(5000);
await element(by.id(`room-members-view-item-${ username }`)).tap();
await sleep(300);
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up');
}
const closeActionSheet = async() => {
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6);
}
it('should set/remove as owner', async() => {
await openActionSheet(user.username);
await element(by.id('action-sheet-set-owner')).tap();
@ -512,9 +507,9 @@ describe('Room actions screen', () => {
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
});
});
})
});
describe('Direct', async() => {
describe('Direct', () => {
before(async() => {
await navigateToRoomActions('d');
});

View File

@ -1,7 +1,6 @@
const {
expect, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, mockMessage, tapBack, searchRoom } = require('../../helpers/app');
navigateToLogin, login, mockMessage, tapBack, searchRoom
} = require('../../helpers/app');
const data = require('../../data');
const platformTypes = require('../../helpers/platformTypes');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -12,7 +11,7 @@ const navigateToRoom = async() => {
await searchRoom(channel);
await element(by.id(`rooms-list-view-item-${ channel }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
};
describe('Discussion', () => {
let scrollViewType;
@ -26,18 +25,18 @@ describe('Discussion', () => {
});
it('should create discussion from NewMessageView', async() => {
const discussionName = `${data.random} Discussion NewMessageView`;
const discussionName = `${ data.random } Discussion NewMessageView`;
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
await element(by.label('Create Discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view'))).toExist().withTimeout(60000);
await expect(element(by.id('create-discussion-view'))).toExist();
await element(by.text('Select a Channel...')).tap();
await element(by.id('multi-select-search')).replaceText(`${channel}`);
await waitFor(element(by.id(`multi-select-item-${channel}`))).toExist().withTimeout(10000);
await element(by.id(`multi-select-item-${channel}`)).tap();
await element(by.id('multi-select-search')).replaceText(`${ channel }`);
await waitFor(element(by.id(`multi-select-item-${ channel }`))).toExist().withTimeout(10000);
await element(by.id(`multi-select-item-${ channel }`)).tap();
await element(by.id('multi-select-discussion-name')).replaceText(discussionName);
await waitFor(element(by.id(`create-discussion-submit`))).toExist().withTimeout(10000);
await waitFor(element(by.id('create-discussion-submit'))).toExist().withTimeout(10000);
await element(by.id('create-discussion-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${ discussionName }`))).toExist().withTimeout(5000);
@ -46,20 +45,20 @@ describe('Discussion', () => {
});
it('should create discussion from action button', async() => {
const discussionName = `${data.random} Discussion Action Button`;
const discussionName = `${ data.random } Discussion Action Button`;
await navigateToRoom();
await element(by.id('messagebox-actions')).tap();
await waitFor(element(by.id('action-sheet'))).toExist().withTimeout(2000);
await element(by.text('Create Discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view'))).toExist().withTimeout(2000);
await element(by.id('multi-select-discussion-name')).replaceText(discussionName);
await waitFor(element(by.id(`create-discussion-submit`))).toExist().withTimeout(10000);
await waitFor(element(by.id('create-discussion-submit'))).toExist().withTimeout(10000);
await element(by.id('create-discussion-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${ discussionName }`))).toExist().withTimeout(5000);
});
describe('Create Discussion from action sheet', async() => {
describe('Create Discussion from action sheet', () => {
it('should send a message', async() => {
await waitFor(element(by.id('messagebox'))).toBeVisible().withTimeout(60000);
await mockMessage('message');
@ -77,7 +76,7 @@ describe('Discussion', () => {
});
});
describe('Check RoomActionsView render', async() => {
describe('Check RoomActionsView render', () => {
it('should navigete to RoomActionsView', async() => {
await waitFor(element(by.id('room-header'))).toBeVisible().withTimeout(5000);
await element(by.id('room-header')).tap();

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping, mockMessageWithNag } = require('../../helpers/app');
const {
navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, mockMessageWithNag
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
async function navigateToRoom(roomName) {
@ -22,14 +21,14 @@ describe('Threads', () => {
await navigateToRoom(mainRoom);
});
describe('Render', async() => {
describe('Render', () => {
it('should have room screen', async() => {
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ mainRoom }`))).toExist().withTimeout(5000);
});
// Render - Header
describe('Header', async() => {
describe('Header', () => {
it('should have actions button ', async() => {
await expect(element(by.id('room-header'))).toExist();
});
@ -40,7 +39,7 @@ describe('Threads', () => {
});
// Render - Messagebox
describe('Messagebox', async() => {
describe('Messagebox', () => {
it('should have messagebox', async() => {
await expect(element(by.id('messagebox'))).toExist();
});
@ -65,8 +64,8 @@ describe('Threads', () => {
});
});
describe('Usage', async() => {
describe('Thread', async() => {
describe('Usage', () => {
describe('Thread', () => {
const thread = `${ data.random }thread`;
it('should create thread', async() => {
await mockMessage('thread');
@ -107,8 +106,8 @@ describe('Threads', () => {
await mockMessage(messageText, true);
await tapBack();
await waitFor(element(by.id('room-header').and(by.label(`${ mainRoom }`)))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-header').and(by.text(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await sleep(500) //TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
await waitFor(element(by.id('room-header').and(by.label(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await sleep(500); // TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
await waitFor(element(by.text(`${ data.random }${ messageText }`)).atIndex(0)).toNotExist().withTimeout(2000);
});
@ -120,8 +119,8 @@ describe('Threads', () => {
await element(by.id('messagebox-send-message')).tap();
await tapBack();
await waitFor(element(by.id('room-header').and(by.label(`${ mainRoom }`)))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-header').and(by.text(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await sleep(500) //TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
await waitFor(element(by.id('room-header').and(by.label(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await sleep(500); // TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
await waitFor(element(by.label(messageText)).atIndex(0)).toExist().withTimeout(2000);
});
@ -134,7 +133,7 @@ describe('Threads', () => {
await element(by.id('messagebox-send-message')).tap();
await tapBack();
await waitFor(element(by.id('room-header').and(by.label(`${ mainRoom }`)))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-header').and(by.text(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await waitFor(element(by.id('room-header').and(by.label(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
await waitFor(element(by.id(`message-thread-replied-on-${ thread }`))).toBeVisible().withTimeout(2000);
await element(by.id(`message-thread-replied-on-${ thread }`)).tap();
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
@ -156,7 +155,7 @@ describe('Threads', () => {
await tapBack();
});
it('should draft thread message', async () => {
it('should draft thread message', async() => {
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await element(by.id('messagebox-input-thread')).typeText(`${ thread }draft`);

View File

@ -1,12 +1,10 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app');
const {
navigateToLogin, login
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
describe('Group DM', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
@ -15,12 +13,12 @@ describe('Group DM', () => {
await login(data.users.regular.username, data.users.regular.password);
});
describe('Create Group DM', async() => {
describe('Create Group DM', () => {
before(async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
});
describe('Render', async() => {
describe('Render', () => {
it('should have new message screen', async() => {
await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
});
@ -28,28 +26,26 @@ describe('Group DM', () => {
it('should have search input', async() => {
await waitFor(element(by.id('new-message-view-search'))).toBeVisible().withTimeout(2000);
});
})
});
describe('Usage', async() => {
describe('Usage', () => {
it('should navigate to create DM', async() => {
await element(by.label('Create Direct Messages')).tap();
});
it('should add users', async() => {
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id(`select-users-view-item-rocket.cat`))).toBeVisible().withTimeout(10000);
await waitFor(element(by.id('select-users-view-item-rocket.cat'))).toBeVisible().withTimeout(10000);
await element(by.id('select-users-view-item-rocket.cat')).tap();
await element(by.id('select-users-view-search')).replaceText(data.users.existing.username);
await waitFor(element(by.id(`select-users-view-item-${data.users.existing.username}`))).toBeVisible().withTimeout(10000);
await element(by.id(`select-users-view-item-${data.users.existing.username}`)).tap();
await waitFor(element(by.id(`select-users-view-item-${ data.users.existing.username }`))).toBeVisible().withTimeout(10000);
await element(by.id(`select-users-view-item-${ data.users.existing.username }`)).tap();
await element(by.id('selected-users-view-submit')).tap();
});
it('check Group DM exist', async() => {
await waitFor(element(by.id(`room-view-title-${data.users.existing.username}, rocket.cat`))).toExist().withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${ data.users.existing.username }, rocket.cat`))).toExist().withTimeout(10000);
});
});
})
});
});

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, searchRoom, sleep } = require('../../helpers/app');
const {
navigateToLogin, login, searchRoom, sleep
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const { sendMessage } = require('../../helpers/data_setup')
@ -13,7 +12,7 @@ async function navigateToRoom(user) {
}
describe('Mark as unread', () => {
const user = data.users.alternate.username
const user = data.users.alternate.username;
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
@ -23,9 +22,8 @@ describe('Mark as unread', () => {
await navigateToRoom(user);
});
// TODO: Fix flakiness. If it fails, run it solo.
describe('Usage', async() => {
describe('Mark message as unread', async() => {
describe('Usage', () => {
describe('Mark message as unread', () => {
it('should mark message as unread', async() => {
const message = `${ data.random }message-mark-as-unread`;
const channelName = `@${ data.users.regular.username }`;
@ -37,7 +35,7 @@ describe('Mark as unread', () => {
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.label('Mark Unread')).atIndex(0).tap();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(5000);
await expect(element(by.id(`rooms-list-view-item-${data.users.alternate.username}`))).toExist();
await expect(element(by.id(`rooms-list-view-item-${ data.users.alternate.username }`))).toExist();
});
});
});

View File

@ -1,11 +1,10 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app');
const {
navigateToLogin, login, tapBack, sleep, searchRoom
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const privateRoomName = data.groups.private.name
const privateRoomName = data.groups.private.name;
async function navigateToRoomInfo(type) {
let room;
@ -32,7 +31,6 @@ async function waitForToast() {
}
describe('Room info screen', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await prepareAndroid();
@ -40,7 +38,7 @@ describe('Room info screen', () => {
await login(data.users.regular.username, data.users.regular.password);
});
describe('Direct', async() => {
describe('Direct', () => {
before(async() => {
await navigateToRoomInfo('d');
});
@ -51,18 +49,18 @@ describe('Room info screen', () => {
});
after(async() => {
await tapBack()
await tapBack()
await tapBack()
})
await tapBack();
await tapBack();
await tapBack();
});
});
describe('Channel/Group', async() => {
describe('Channel/Group', () => {
before(async() => {
await navigateToRoomInfo('c');
});
describe('Render', async() => {
describe('Render', () => {
it('should have room info view', async() => {
await expect(element(by.id('room-info-view'))).toExist();
});
@ -88,7 +86,7 @@ describe('Room info screen', () => {
});
});
describe('Render Edit', async() => {
describe('Render Edit', () => {
before(async() => {
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
@ -151,7 +149,7 @@ describe('Room info screen', () => {
});
});
describe('Usage', async() => {
describe('Usage', () => {
// it('should enter "invalid name" and get error', async() => {
// await element(by.type('UIScrollView')).atIndex(1).swipe('down');
// await element(by.id('room-info-edit-view-name')).replaceText('invalid name');
@ -190,7 +188,7 @@ describe('Room info screen', () => {
await element(by.id('room-info-edit-view-password')).replaceText('abc');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.3);
await element(by.id('room-info-edit-view-t')).tap();
await element(by.id('room-info-edit-view-ro')).longPress(); //https://github.com/facebook/react-native/issues/28032
await element(by.id('room-info-edit-view-ro')).longPress(); // https://github.com/facebook/react-native/issues/28032
await element(by.id('room-info-edit-view-react-when-ro')).tap();
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.2);
await element(by.id('room-info-edit-view-reset')).tap();

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, mockMessage, tapBack, login, sleep, searchRoom } = require('../../helpers/app');
const {
navigateToLogin, tapBack, login, searchRoom
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
async function navigateToRoom(roomName) {
@ -23,7 +22,7 @@ async function clearCache() {
await waitFor(element(by.text('This will clear all your offline data.'))).toExist().withTimeout(2000);
await element(by.label('Clear').and(by.type('_UIAlertControllerActionView'))).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-jumping`))).toExist().withTimeout(10000);
await waitFor(element(by.id('rooms-list-view-item-jumping'))).toExist().withTimeout(10000);
}
async function waitForLoading() {
@ -53,7 +52,7 @@ describe('Room', () => {
await element(by.id('nav-jump-to-bottom')).tap();
await waitFor(element(by.label('Quote first message'))).toExist().withTimeout(5000);
await clearCache();
})
});
it('should load messages on scroll', async() => {
await navigateToRoom('jumping');
@ -66,6 +65,7 @@ describe('Room', () => {
await expect(element(by.label('249'))).toExist();
found = true;
} catch {
//
}
}
await clearCache();
@ -82,7 +82,7 @@ describe('Room', () => {
await expect(element(by.label('30'))).toExist();
await expect(element(by.label('31'))).toExist();
await expect(element(by.label('32'))).toExist();
})
});
it('should load newer and older messages', async() => {
await element(by.id('room-view-messages')).atIndex(0).swipe('down', 'fast', 0.8);
@ -116,9 +116,9 @@ describe('Room', () => {
const expectThreadMessages = async(message) => {
await waitFor(element(by.id('room-view-title-jumping-thread'))).toExist().withTimeout(5000);
await expect(element(by.label(message))).toExist();
}
};
describe('Threads', async() => {
describe('Threads', () => {
it('should navigate to a thread from another room', async() => {
await navigateToRoom('jumping');
await waitFor(element(by.label('Go to jumping-thread\'s thread')).atIndex(0)).toExist().withTimeout(5000);
@ -152,5 +152,5 @@ describe('Threads', async() => {
await expectThreadMessages('to be searched');
});
//TODO: Threads pagination
// TODO: Threads pagination
});

View File

@ -1,6 +1,3 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login } = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
@ -15,7 +12,7 @@ describe('Create team screen', () => {
await login(data.users.regular.username, data.users.regular.password);
});
describe('New Message', async() => {
describe('New Message', () => {
before(async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
});
@ -30,17 +27,17 @@ describe('Create team screen', () => {
});
});
describe('Select Users', async() => {
describe('Select Users', () => {
it('should nav to create team', async() => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(10000);
});
})
});
describe('Create Team', async() => {
describe('Usage', async() => {
describe('Create Team', () => {
describe('Usage', () => {
it('should get invalid team name', async() => {
await element(by.id('create-channel-name')).typeText(`${data.teams.private.name}`);
await element(by.id('create-channel-name')).typeText(`${ data.teams.private.name }`);
await element(by.id('create-channel-submit')).tap();
await element(by.text('OK')).tap();
});
@ -54,10 +51,10 @@ describe('Create team screen', () => {
await waitFor(element(by.id(`room-view-title-${ teamName }`))).toExist().withTimeout(6000);
await expect(element(by.id(`room-view-title-${ teamName }`))).toExist();
});
})
});
});
describe('Delete Team', async() => {
describe('Delete Team', () => {
it('should navigate to room info edit view', async() => {
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app');
const {
navigateToLogin, login, tapBack, sleep, searchRoom
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
async function navigateToRoom(roomName) {
@ -51,8 +50,8 @@ describe('Team', () => {
await navigateToRoom(team);
});
describe('Team Room', async() => {
describe('Team Header', async() => {
describe('Team Room', () => {
describe('Team Header', () => {
it('should have actions button ', async() => {
await expect(element(by.id('room-header'))).toExist();
});
@ -71,14 +70,14 @@ describe('Team', () => {
});
});
describe('Team Header Usage', async() => {
describe('Team Header Usage', () => {
it('should navigate to team channels view', async() => {
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id('team-channels-view'))).toExist().withTimeout(5000);
});
})
});
describe('Team Channels Header', async() => {
describe('Team Channels Header', () => {
it('should have actions button ', async() => {
await expect(element(by.id('room-header'))).toExist();
});
@ -92,7 +91,7 @@ describe('Team', () => {
});
});
describe('Team Channels Header Usage', async() => {
describe('Team Channels Header Usage', () => {
it('should navigate to add team channels view', async() => {
await element(by.id('team-channels-view-create')).tap();
await waitFor(element(by.id('add-channel-team-view'))).toExist().withTimeout(5000);
@ -105,11 +104,10 @@ describe('Team', () => {
it('should add existing button', async() => {
await waitFor(element(by.id('add-channel-team-view-add-existing'))).toExist().withTimeout(5000);
});
})
});
describe('Channels', async() => {
describe('Channels', () => {
it('should create new channel for team', async() => {
await element(by.id('add-channel-team-view-create-channel')).tap();
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
@ -140,12 +138,11 @@ describe('Team', () => {
});
it('should add existing channel to team', async() => {
await element(by.id('team-channels-view-create')).tap();
await waitFor(element(by.id('add-channel-team-view'))).toExist().withTimeout(5000);
await element(by.id('add-channel-team-view-add-existing')).tap();
await waitFor(element(by.id('add-existing-channel-view'))).toExist().withTimeout(60000)
await waitFor(element(by.id('add-existing-channel-view'))).toExist().withTimeout(60000);
await expect(element(by.id(`add-existing-channel-view-item-${ existingRoom }`))).toExist();
await element(by.id(`add-existing-channel-view-item-${ existingRoom }`)).tap();
await waitFor(element(by.id('add-existing-channel-view-submit'))).toExist().withTimeout(6000);
@ -176,7 +173,7 @@ describe('Team', () => {
await waitFor(element(by.id('auto-join-tag'))).toBeNotVisible().withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${ existingRoom }`))).toExist().withTimeout(6000);
});
})
});
describe('Team actions', () => {
before(async() => {
@ -216,9 +213,9 @@ describe('Team', () => {
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.id('select-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${room}`))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${existingRoom}`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(element(by.id(`select-list-view-item-${ room }`))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${ existingRoom }`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${ room }`)).tap();
await waitFor(element(by.label('You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.'))).toExist().withTimeout(2000);
await element(by.text('OK')).tap();
@ -230,7 +227,7 @@ describe('Team', () => {
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000);
});
describe('Room Members', async() => {
describe('Room Members', () => {
before(async() => {
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view'))).toExist().withTimeout(2000);
@ -280,9 +277,9 @@ describe('Team', () => {
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.id('select-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${room}`))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${existingRoom}`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(element(by.id(`select-list-view-item-${ room }`))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${ existingRoom }`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${ room }`)).tap();
await waitFor(element(by.label('You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.'))).toExist().withTimeout(2000);
await element(by.text('OK')).tap();

View File

@ -1,8 +1,7 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, tapBack, searchRoom, sleep } = require('../../helpers/app');
const {
navigateToLogin, login, tapBack, searchRoom, sleep
} = require('../../helpers/app');
const { prepareAndroid } = require('../../helpers/platformFunctions');
const toBeConverted = `to-be-converted-${ data.random }`;
@ -22,7 +21,7 @@ const createChannel = async(room) => {
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
}
};
async function navigateToRoom(room) {
await searchRoom(`${ room }`);
@ -44,7 +43,7 @@ describe('Move/Convert Team', () => {
await login(data.users.regular.username, data.users.regular.password);
});
describe('Convert', async() => {
describe('Convert', () => {
before(async() => {
await createChannel(toBeConverted);
});
@ -54,7 +53,7 @@ describe('Move/Convert Team', () => {
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-convert-to-team'))).toExist().withTimeout(2000);
await element(by.id('room-actions-convert-to-team')).tap();
await waitFor(element(by.label('This can\'t be undone. Once you convert a channel to a team, you can not turn it back to a channel.'))).toExist().withTimeout(2000);
await waitFor(element(by.label('You are converting this Channel to a Team. All Members will be kept.'))).toExist().withTimeout(2000);
await element(by.text('Convert')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(20000);
await waitFor(element(by.id(`room-view-title-${ toBeConverted }`))).toExist().withTimeout(6000);
@ -63,10 +62,10 @@ describe('Move/Convert Team', () => {
after(async() => {
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
})
});
});
describe('Move', async() => {
describe('Move', () => {
before(async() => {
await createChannel(toBeMoved);
});
@ -80,12 +79,39 @@ describe('Move/Convert Team', () => {
await element(by.id('select-list-view-submit')).tap();
await sleep(2000);
await waitFor(element(by.id('select-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${toBeConverted}`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${toBeConverted}`)).tap();
await waitFor(element(by.id(`select-list-view-item-${ toBeConverted }`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${ toBeConverted }`)).tap();
await element(by.id('select-list-view-submit')).atIndex(0).tap();
await waitFor(element(by.label('After reading the previous intructions about this behavior, do you still want to move this channel to the selected team?'))).toExist().withTimeout(2000);
await element(by.text('Yes, move it!')).tap();
await waitFor(element(by.id('room-view-header-team-channels'))).toExist().withTimeout(10000);
});
})
after(async() => {
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
});
});
describe('Convert Team to Channel and Delete toBeMoved channel within the Converted', () => {
it('should convert a team to a channel', async() => {
await navigateToRoomActions(toBeConverted);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-convert-channel-to-team'))).toExist().withTimeout(2000);
await element(by.id('room-actions-convert-channel-to-team')).tap();
await sleep(2000);
await waitFor(element(by.id('select-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${ toBeMoved }`))).toExist().withTimeout(2000);
await element(by.id(`select-list-view-item-${ toBeMoved }`)).tap();
await waitFor(element(by.id('select-list-view-submit'))).toExist().withTimeout(2000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by.label('You are converting this Team to a Channel'))).toExist().withTimeout(2000);
await element(by.text('Convert')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(20000);
await waitFor(element(by.id(`room-view-title-${ toBeConverted }`))).toExist().withTimeout(6000);
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ toBeMoved }`))).toBeNotVisible().withTimeout(60000);
});
});
});

View File

@ -1694,7 +1694,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.17.0;
MARKETING_VERSION = 4.18.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
@ -1731,7 +1731,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.17.0;
MARKETING_VERSION = 4.18.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@ -23,7 +23,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.17.0</string>
<string>4.18.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>4.17.0</string>
<string>4.18.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>KeychainGroup</key>

View File

@ -1,6 +1,6 @@
{
"name": "rocket-chat-reactnative",
"version": "4.17.0",
"version": "4.18.0",
"private": true,
"scripts": {
"start": "react-native start",

View File

@ -0,0 +1,55 @@
diff --git a/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js b/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js
index c344ac4..479c8c0 100644
--- a/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js
+++ b/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js
@@ -1244,9 +1244,17 @@ class ScrollView extends React.Component<Props, State> {
// Note: we should split props.style on the inner and outer props
// however, the ScrollView still needs the baseStyle to be scrollable
const {outer, inner} = splitLayoutProps(flattenStyle(props.style));
+
+ // Workaround for RefreshControl inverted: https://github.com/facebook/react-native/issues/30034
+ let inverted;
+ if (inner.scaleY) {
+ inverted = { scaleY: -1 };
+ delete inner.scaleY;
+ }
+
return React.cloneElement(
refreshControl,
- {style: [baseStyle, outer]},
+ {style: [baseStyle, outer, inverted]},
<ScrollViewClass
{...props}
style={[baseStyle, inner]}
diff --git a/node_modules/react-native/Libraries/Lists/VirtualizedList.js b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
index 9ec105f..6bb6989 100644
--- a/node_modules/react-native/Libraries/Lists/VirtualizedList.js
+++ b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
@@ -11,6 +11,7 @@
'use strict';
const Batchinator = require('../Interaction/Batchinator');
+const Platform = require('../Utilities/Platform');
const FillRateHelper = require('./FillRateHelper');
const PropTypes = require('prop-types');
const React = require('react');
@@ -2185,9 +2186,16 @@ function describeNestedLists(childList: {
}
const styles = StyleSheet.create({
- verticallyInverted: {
- transform: [{scaleY: -1}],
- },
+ // Workaround found on https://github.com/facebook/react-native/issues/30034#issuecomment-806396274
+ // Note: Check ScrollView for a workaround on RefreshControl
+ verticallyInverted:
+ Platform.OS === 'android'
+ ? {
+ scaleY: -1,
+ }
+ : {
+ transform: [{scaleY: -1}]
+ },
horizontallyInverted: {
transform: [{scaleX: -1}],
},