Merge branch 'develop' into detoxRunner

This commit is contained in:
Anant Bhasin 2021-05-19 10:58:14 +05:30
commit 6063add9ae
73 changed files with 34431 additions and 40101 deletions

View File

@ -290,6 +290,24 @@ commands:
command: bundle exec fastlane android beta official:<< parameters.official >>
working_directory: android
# EXPERIMENTAL ONLY
# No plans to do it for Official
upload-to-google-play-production:
description: "Upload to Google Play production"
steps:
- checkout
- attach_workspace:
at: android
- run:
name: Store the google service account key
command: echo "$FASTLANE_GOOGLE_SERVICE_ACCOUNT" | base64 --decode > service_account.json
working_directory: android
- run: *update-fastlane-android
- run:
name: Fastlane Play Store Upload
command: bundle exec fastlane android production
working_directory: android
upload-to-testflight:
description: "Upload to TestFlight"
parameters:
@ -382,6 +400,13 @@ jobs:
- upload-to-google-play-beta:
official: false
android-google-play-production-experimental:
<<: *defaults
docker:
- image: circleci/android:api-28-node
steps:
- upload-to-google-play-production
android-google-play-beta-official:
<<: *defaults
docker:
@ -466,6 +491,13 @@ workflows:
- android-google-play-beta-experimental:
requires:
- android-hold-google-play-beta-experimental
- android-hold-google-play-production-experimental:
type: approval
requires:
- android-build-experimental
- android-google-play-production-experimental:
requires:
- android-hold-google-play-production-experimental
# Android Official
- android-hold-build-official:

View File

@ -46,6 +46,7 @@ module.exports = {
"react/forbid-prop-types": 0,
"jsx-quotes": [2, "prefer-single"],
"jsx-a11y/href-no-hash": 0,
"jsx-a11y/aria-role": 0,
"import/prefer-default-export": 0,
"import/no-cycle": 0,
"camelcase": 0,

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -32,4 +32,13 @@ platform :android do
)
end
end
desc "Upload App to Play Store Production"
lane :production do |options|
upload_to_play_store(
package_name: 'chat.rocket.reactnative',
track: 'production',
aab: 'android/app/build/outputs/bundle/experimentalPlayRelease/app-experimental-play-release.aab'
)
end
end

View File

@ -876,7 +876,7 @@ class MessageBox extends Component {
recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview
} = this.state;
const {
editing, message, replying, replyCancel, user, getCustomEmoji, theme, Message_AudioRecorderEnabled, children, isActionsEnabled
editing, message, replying, replyCancel, user, getCustomEmoji, theme, Message_AudioRecorderEnabled, children, isActionsEnabled, tmid
} = this.props;
const isAndroidTablet = isTablet && isAndroid ? {
@ -936,7 +936,7 @@ class MessageBox extends Component {
underlineColorAndroid='transparent'
defaultValue=''
multiline
testID='messagebox-input'
testID={`messagebox-input${ tmid ? '-thread' : '' }`}
theme={theme}
{...isAndroidTablet}
/>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import {
View, Text, StyleSheet, TouchableOpacity
@ -149,11 +149,13 @@ const Header = React.memo(({
);
}
const handleOnPress = useCallback(() => onPress(), []);
return (
<TouchableOpacity
testID='room-header'
accessibilityLabel={title}
onPress={onPress}
onPress={handleOnPress}
style={styles.container}
disabled={tmid}
hitSlop={HIT_SLOP}

View File

@ -18,6 +18,7 @@ const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
username: {
@ -30,7 +31,7 @@ const styles = StyleSheet.create({
...sharedStyles.textMedium
},
titleContainer: {
flex: 1,
flexShrink: 1,
flexDirection: 'row',
alignItems: 'center'
},

5
app/definition/ITeam.js Normal file
View File

@ -0,0 +1,5 @@
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
export const TEAM_TYPE = {
PUBLIC: 0,
PRIVATE: 1
};

View File

@ -79,19 +79,23 @@ export const setLanguage = (l) => {
return;
}
// server uses lowercase pattern (pt-br), but we're forced to use standard pattern (pt-BR)
const locale = LANGUAGES.find(ll => ll.value.toLowerCase() === l.toLowerCase())?.value;
let locale = LANGUAGES.find(ll => ll.value.toLowerCase() === l.toLowerCase())?.value;
if (!locale) {
locale = 'en';
}
// don't go forward if it's the same language and default language (en) was setup already
if (i18n.locale === locale && i18n.translations?.en) {
return;
}
i18n.locale = locale;
i18n.translations = { ...i18n.translations, [locale]: translations[locale]() };
i18n.translations = { ...i18n.translations, [locale]: translations[locale]?.() };
I18nManager.forceRTL(isRTL(locale));
I18nManager.swapLeftAndRightInRTL(isRTL(locale));
i18n.isRTL = I18nManager.isRTL;
moment.locale(toMomentLocale(locale));
};
i18n.translations = { en: translations.en?.() };
const defaultLanguage = { languageTag: 'en', isRTL: false };
const availableLanguages = Object.keys(translations);
const { languageTag } = RNLocalize.findBestAvailableLanguage(availableLanguages) || defaultLanguage;

View File

@ -33,7 +33,7 @@
"error-invalid-date": "التاريخ غير صالح",
"error-invalid-description": "الوصف غير صالح",
"error-invalid-domain": "عنوان الموقع غير صالح",
"error-invalid-email": "عنوان البريد اﻹلكتروني غير صالح {{emai}}",
"error-invalid-email": "عنوان البريد اﻹلكتروني غير صالح {{email}}",
"error-invalid-email-address": "عنوان البريد اﻹلكتروني غير صالح",
"error-invalid-file-height": "ارتفاع الملف غير صالح",
"error-invalid-file-type": "نوع الملف غير صالح",
@ -100,7 +100,6 @@
"announcement": "إعلان",
"Announcement": "إعلان",
"Apply_Your_Certificate": "طبق شهادتك",
"Applying_a_theme_will_change_how_the_app_looks": "سيؤدي تطبيق السمة إلى تغيير شكل التطبيق",
"ARCHIVE": "أرشفة",
"archive": "أرشفة",
"are_typing": "يكتب",
@ -184,8 +183,6 @@
"deleting_room": "حذف الغرفة",
"description": "وصف",
"Description": "وصف",
"DESKTOP_OPTIONS": "خيارات سطح المكتب",
"DESKTOP_NOTIFICATIONS": "إشعارات سطح المكتب",
"Desktop_Alert_info": "هذه الإشعارات ترسل لسطح المكتب",
"Directory": "مجلد",
"Direct_Messages": "رسالة مباشرة",
@ -213,7 +210,6 @@
"Email_Notification_Mode_Disabled": "معطل",
"Email_or_password_field_is_empty": "حقل البريد الإلكتروني أو كلمة المرور فارغ",
"Email": "البريد الإلكتروني",
"EMAIL": "البريد الإلكتروني",
"email": "البريد الإلكتروني",
"Empty_title": "عنوان فارغ",
"Enable_Auto_Translate": "تمكين الترجمة التلقائية",
@ -270,7 +266,6 @@
"I_Saved_My_E2E_Password": "قمت بحفظ كلمة المرور الطرفية",
"IP": " عنوان بروتوكول الإنترنت (الآيبي)",
"In_app": "في التطبيق",
"IN_APP_AND_DESKTOP": "داخل التطبيق وسطح المكتب",
"In_App_and_Desktop_Alert_info": "يعرض شعاراً أعلى الشاشة عندما يكون التطبيق مفتوحًا، ويعرض إشعاراً على سطح المكتب",
"Invisible": "غير مرئي",
"Invite": "دعوة",
@ -398,7 +393,6 @@
"Profile": "الملف الشخصي",
"Public_Channel": "قناة عامة",
"Public": "عام",
"PUSH_NOTIFICATIONS": "الإشعارات",
"Push_Notifications_Alert_Info": "يتم إرسال هذه الإشعارات إليك عندما لا يكون التطبيق مفتوحاً",
"Quote": "اقتباس",
"Reactions_are_disabled": "التفاعل معطل",
@ -446,9 +440,9 @@
"Room_Members": "أعضاء الغرفة",
"Room_name_changed": "تم تغيير اسم الغرفة إلى: {{name}} من قبل {{userBy}}",
"SAVE": "حفظ",
"Saved": "تم الحفظ",
"Save_Changes": "حفظ التغيرات",
"Save": "حفظ",
"Saved": "تم الحفظ",
"saving_preferences": "حفظ التفضيلات",
"saving_profile": "حفظ الملف الشخصي",
"saving_settings": "حفظ الإعدادات",

View File

@ -33,7 +33,7 @@
"error-invalid-date": "Ungültiges Datum angegeben",
"error-invalid-description": "Ungültige Beschreibung",
"error-invalid-domain": "Ungültige Domain",
"error-invalid-email": "Ungültige E-Mail {{emai}}",
"error-invalid-email": "Ungültige E-Mail {{email}}",
"error-invalid-email-address": "Ungültige E-Mail-Adresse",
"error-invalid-file-height": "Ungültige Dateihöhe",
"error-invalid-file-type": "Ungültiger Dateityp",
@ -69,7 +69,7 @@
"error-role-in-use": "Rolle kann nicht gelöscht werden, da sie gerade verwendet wird",
"error-role-name-required": "Der Rollenname ist erforderlich",
"error-the-field-is-required": "Das Feld {{field}} ist erforderlich.",
"error-too-many-requests": "Fehler, zu viele Anfragen. Du musst {{Sekunden}} Sekunden warten, bevor du es erneut versuchst.",
"error-too-many-requests": "Fehler, zu viele Anfragen. Du musst {{seconds}} Sekunden warten, bevor du es erneut versuchst.",
"error-user-is-not-activated": "Benutzer ist nicht aktiviert",
"error-user-has-no-roles": "Benutzer hat keine Rollen",
"error-user-limit-exceeded": "Die Anzahl der Benutzer, die du zu #channel_name einladen möchtest, überschreitet die vom Administrator festgelegte Grenze",
@ -185,6 +185,7 @@
"Description": "Beschreibung",
"Desktop_Options": "Desktop-Einstellungen",
"Desktop_Notifications": "Desktop-Benachrichtigungen",
"Desktop_Alert_info": "Diese Benachrichtigungen werden auf dem Desktop angezeigt",
"Directory": "Verzeichnis",
"Direct_Messages": "Direkte Nachrichten",
"Disable_notifications": "Benachrichtigungen deaktiveren",
@ -276,7 +277,7 @@
"is_not_a_valid_RocketChat_instance": "ist keine gültige Rocket.Chat-Instanz",
"is_typing": "schreibt",
"Invalid_or_expired_invite_token": "Ungültiger oder abgelaufener Einladungscode",
"Invalid_server_version": "Der Server, zu dem du dich verbinden möchtest, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{MinVersion}}.",
"Invalid_server_version": "Der Server, zu dem du dich verbinden möchtest, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{minVersion}}.",
"Invite_Link": "Einladungs-Link",
"Invite_users": "Benutzer einladen",
"Join": "Beitreten",
@ -400,7 +401,6 @@
"Public": "Öffentlich",
"Push_Notifications": "Push-Benachrichtigungen",
"Push_Notifications_Alert_Info": "Diese Benachrichtigungen werden dir zugestellt, wenn die App nicht geöffnet ist.",
"Desktop_Alert_info": "Diese Benachrichtigungen werden auf dem Desktop angezeigt",
"Quote": "Zitat",
"Reactions_are_disabled": "Reaktionen sind deaktiviert",
"Reactions_are_enabled": "Reaktionen sind aktiviert",

View File

@ -33,7 +33,7 @@
"error-invalid-date": "Invalid date provided.",
"error-invalid-description": "Invalid description",
"error-invalid-domain": "Invalid domain",
"error-invalid-email": "Invalid email {{emai}}",
"error-invalid-email": "Invalid email {{email}}",
"error-invalid-email-address": "Invalid email address",
"error-invalid-file-height": "Invalid file height",
"error-invalid-file-type": "Invalid file type",
@ -709,5 +709,12 @@
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
"Teams": "Teams",
"No_team_channels_found": "No channels found",
"Team_not_found": "Team not found"
"Team_not_found": "Team not found",
"Create_Team": "Create Team",
"Team_Name": "Team Name",
"Private_Team": "Private Team",
"Read_Only_Team": "Read Only Team",
"Broadcast_Team": "Broadcast Team",
"creating_team": "creating team",
"team-name-already-exists": "A team with that name already exists"
}

View File

@ -30,7 +30,7 @@
"error-invalid-date": "La fecha proporcionada no es correcta.",
"error-invalid-description": "La descipción no es correcta",
"error-invalid-domain": "El dominio no es correcto",
"error-invalid-email": "El email {{emai}} no es correcto",
"error-invalid-email": "El email {{email}} no es correcto",
"error-invalid-email-address": "La dirección de correo no es correcta",
"error-invalid-file-height": "La altura de la imagen no es correcta",
"error-invalid-file-type": "El formato del archivo no es correcto",
@ -44,7 +44,7 @@
"error-invalid-redirectUri": "La URL de redirección no es correcta.",
"error-invalid-role": "El rol no es correcto",
"error-invalid-room": "La sala no es correcta",
"error-invalid-room-name": "No se puede asignar el nombre {{name}} a una sala.",
"error-invalid-room-name": "No se puede asignar el nombre {{room_name}} a una sala.",
"error-invalid-room-type": "No se puede asginar el tipo {{type}} a una sala.",
"error-invalid-settings": "La configuración proporcionada no es correcta",
"error-invalid-subscription": "La subscripción no es correcta",
@ -80,7 +80,6 @@
"Activity": "Actividad",
"Add_Reaction": "Reaccionar",
"Add_Server": "Añadir servidor",
"Add_user": "Añadir usuario",
"Admin_Panel": "Panel de Control",
"Alert": "Alerta",
"alert": "alerta",

View File

@ -33,7 +33,7 @@
"error-invalid-date": "Date fournie invalide.",
"error-invalid-description": "Description invalide",
"error-invalid-domain": "Domaine invalide",
"error-invalid-email": "Adresse e-mail non valide {{emai}}",
"error-invalid-email": "Adresse e-mail non valide {{email}}",
"error-invalid-email-address": "Adresse e-mail invalide",
"error-invalid-file-height": "Hauteur de fichier non valide",
"error-invalid-file-type": "Type de fichier invalide",

View File

@ -33,7 +33,7 @@
"error-invalid-date": "Data fornita non valida.",
"error-invalid-description": "Descrizione non valida",
"error-invalid-domain": "Dominio non valido",
"error-invalid-email": "E-mail {{emai}} non valida",
"error-invalid-email": "E-mail {{email}} non valida",
"error-invalid-email-address": "Indirizzo e-mail non valido",
"error-invalid-file-height": "Altezza del file non valida",
"error-invalid-file-type": "Tipo di file non valido",
@ -157,8 +157,8 @@
"Continue_with": "Continua con",
"Copied_to_clipboard": "Copiato negli appunti!",
"Copy": "Copia",
"Permalink": "Permalink",
"Conversation": "Conversazione",
"Permalink": "Permalink",
"Certificate_password": "Password certificato",
"Clear_cache": "Cancella la cache locale",
"Clear_cache_loading": "Cancellando la cache.",

View File

@ -31,7 +31,7 @@
"error-invalid-date": "不正な日時です",
"error-invalid-description": "不正な詳細です",
"error-invalid-domain": "不正なドメインです",
"error-invalid-email": "不正なメールアドレスです。 {{emai}}",
"error-invalid-email": "不正なメールアドレスです。 {{email}}",
"error-invalid-email-address": "不正なメールアドレスです",
"error-invalid-file-height": "ファイルの高さが不正です",
"error-invalid-file-type": "ファイルの種類が不正です",
@ -179,7 +179,6 @@
"Email": "メールアドレス",
"email": "メールアドレス",
"Enable_Auto_Translate": "自動翻訳を有効にする",
"Enable_markdown": "マークダウンを有効にする",
"Enable_notifications": "通知を有効にする",
"Everyone_can_access_this_channel": "全員このチャンネルにアクセスできます",
"Error_uploading": "アップロードエラー",
@ -432,7 +431,7 @@
"Users": "ユーザー",
"User_added_by": "{{userBy}} が {{userAdded}} を追加しました",
"User_Info": "ユーザー情報",
"User_has_been_key": "ユーザーは{{ key }}",
"User_has_been_key": "ユーザーは{{key}}",
"User_is_no_longer_role_by_": "{{userBy}} は {{user}} のロール {{role}} を削除しました。",
"User_muted_by": "{{userBy}} は {{userMuted}} をミュートしました。",
"User_removed_by": "{{userBy}} は {{userRemoved}} を退出させました。",

View File

@ -1,7 +1,7 @@
{
"1_person_reacted": "1 persoon heeft gereageerd",
"1_user": "1 gebruiker",
"error-action-not-allowed": "{{actie}} is niet toegestaan",
"error-action-not-allowed": "{{action}} is niet toegestaan",
"error-application-not-found": "Applicatie niet gevonden",
"error-archived-duplicate-name": "Er is een gearchiveerd kanaal met de naam {{room_name}}",
"error-avatar-invalid-url": "Foutieve avatar URL: {{url}}",
@ -31,7 +31,7 @@
"error-invalid-date": "Ongeldige datum opgegeven.",
"error-invalid-description": "Ongeldige beschrijving",
"error-invalid-domain": "Ongeldig domein",
"error-invalid-email": "Ongeldige email {{emai}}",
"error-invalid-email": "Ongeldige email {{email}}",
"error-invalid-email-address": "Ongeldig emailadres",
"error-invalid-file-height": "Ongeldige file height",
"error-invalid-file-type": "Ongeldig bestandstype",
@ -123,7 +123,6 @@
"creating_invite": "uitnodiging maken",
"Channel_Name": "Kanaal Name",
"Channels": "Kanalen",
"Chats": "Chats",
"Call_already_ended": "Gesprek al beeïndigd!",
"Click_to_join": "Klik om lid te worden!",
"Close": "Sluiten",

View File

@ -62,22 +62,14 @@
"error-no-tokens-for-this-user": "Não existem tokens para este usuário",
"error-not-allowed": "Não permitido",
"error-not-authorized": "Não autorizado",
"error-password-policy-not-met": "A senha não atende a política do servidor",
"error-password-policy-not-met-maxLength": "A senha não está de acordo com a política de comprimento máximo do servidor (senha muito longa)",
"error-password-policy-not-met-minLength": "A senha não está de acordo com a política de comprimento mínimo do servidor (senha muito curta)",
"error-password-policy-not-met-oneLowercase": "A senha não está de acordo com a política do servidor de pelo menos um caractere minúsculo.",
"error-password-policy-not-met-oneNumber": "A senha não está de acordo com a política do servidor, de pelo menos um caractere numérico.",
"error-password-policy-not-met-oneSpecial": "A senha não está de acordo com a política do servidor, de pelo menos um caractere especial.",
"error-password-policy-not-met-oneUppercase": "A senha não está de acordo com a política do servidor, de pelo menos um caractere maiúsculo.",
"error-password-policy-not-met-repeatingCharacters": "A senha não está de acordo com a política do servidor, relativamente aos caracteres proibidos repetidos (existem vários caracteres proibidos próximos uns dos outros)",
"error-push-disabled": "Notificações push desativadas",
"error-remove-last-owner": "Este é o último proprietário. Por favor, defina um novo proprietário antes de remover este.",
"error-role-in-use": "Não é possível remover o papel pois ele está em uso",
"error-role-name-required": "Nome do papel é obrigatório",
"error-the-field-is-required": "O campo {{field}} é obrigatório.",
"error-too-many-requests": "Erro, muitas solicitações. Por favor, diminua a velocidade. Você deve esperar {{seconds}} segundos antes de tentar novamente.",
"error-user-has-no-roles": "O usuário não possui permissões",
"error-user-is-not-activated": "O usuário não está ativo",
"error-user-has-no-roles": "O usuário não possui permissões",
"error-user-limit-exceeded": "O número de usuários que você está tentando convidar para #channel_name excede o limite determindado pelo administrador",
"error-user-not-in-room": "O usuário não está nesta sala",
"error-user-registration-disabled": "O registro do usuário está desativado",
@ -102,6 +94,7 @@
"and": "e",
"announcement": "anúncio",
"Announcement": "Anúncio",
"Apply_Your_Certificate": "Aplicar certificado",
"ARCHIVE": "ARQUIVAR",
"archive": "arquivar",
"are_typing": "estão digitando",
@ -131,10 +124,7 @@
"Channel_Name": "Nome do Canal",
"Channels": "Canais",
"Chats": "Conversas",
"Change_Language": "Alterar idioma",
"Change_language_loading": "Alterando idioma.",
"Call_already_ended": "A chamada já terminou!",
"Clear_cache_loading": "Limpando cache.",
"Clear_cookies_alert": "Você quer limpar seus cookies?",
"Clear_cookies_desc": "Esta ação limpará todos os cookies de login permitindo que você faça login em outras contas.",
"Clear_cookies_yes": "Sim, limpar cookies",
@ -143,8 +133,9 @@
"Close": "Fechar",
"Close_emoji_selector": "Fechar seletor de emojis",
"Closing_chat": "Fechando conversa",
"Choose": "Escolher",
"Change_language_loading": "Alterando idioma.",
"Chat_closed_by_agent": "Conversa fechada por agente",
"Choose": "Escolher",
"Choose_from_library": "Escolha da biblioteca",
"Choose_file": "Enviar arquivo",
"Choose_where_you_want_links_be_opened": "Escolha onde deseja que os links sejam abertos",
@ -154,15 +145,16 @@
"Confirm": "Confirmar",
"Connect": "Conectar",
"Connected": "Conectado",
"Conversation": "Conversação",
"connecting_server": "conectando no servidor",
"Connecting": "Conectando...",
"Contact_us": "Entre em contato",
"Continue_with": "Entrar com",
"Contact_your_server_admin": "Contate o administrador do servidor.",
"Continue_with": "Entrar com",
"Copied_to_clipboard": "Copiado para a área de transferência!",
"Copy": "Copiar",
"Conversation": "Conversação",
"Permalink": "Link-Permanente",
"Clear_cache_loading": "Limpando cache.",
"Create_account": "Criar conta",
"Create_Channel": "Criar Canal",
"Create_Direct_Messages": "Criar Mensagens Diretas",
@ -172,19 +164,21 @@
"Create": "Criar",
"Dark": "Escuro",
"Dark_level": "Nível escuro",
"Default": "Padrão",
"Default_browser": "Navegador padrão",
"Delete_Room_Warning": "A exclusão de uma sala irá apagar todas as mensagens postadas na sala. Isso não pode ser desfeito.",
"Department": "Departamento",
"delete": "excluir",
"Delete": "Excluir",
"DELETE": "EXCLUIR",
"deleting_room": "excluindo sala",
"Direct_Messages": "Mensagens Diretas",
"description": "descrição",
"Description": "Descrição",
"Desktop_Options": "Opções De Área De Trabalho",
"Desktop_Notifications": "Notificações da Área de Trabalho",
"Desktop_Alert_info": "Essas notificações são entregues a você na área de trabalho",
"Directory": "Diretório",
"description": "descrição",
"Description": "Descrição",
"Direct_Messages": "Mensagens Diretas",
"Disable_notifications": "Desabilitar notificações",
"Discussions": "Discussões",
"Discussion_Desc": "Ajude a manter uma visão geral sobre o que está acontecendo! Ao criar uma discussão, um sub-canal do que você selecionou é criado e os dois são vinculados.",
@ -192,6 +186,7 @@
"Done": "Pronto",
"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 realmente {{key}} esta sala?",
"E2E_Encryption": "Encriptação ponta a ponta",
"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.",
@ -201,16 +196,16 @@
"edit": "editar",
"edited": "editado",
"Edit": "Editar",
"Edit_Invite": "Editar convite",
"Edit_Status": "Editar Status",
"Edit_Invite": "Editar convite",
"End_to_end_encrypted_room": "Sala criptografada de ponta a ponta",
"end_to_end_encryption": "criptografia de ponta a ponta",
"Email_Notification_Mode_All": "Cada Menção / Mensagem Direta",
"Email_Notification_Mode_Disabled": "Desativado",
"Email_or_password_field_is_empty": "Email ou senha estão vazios",
"Email": "E-mail",
"email": "e-mail",
"Empty_title": "Título vazio",
"Email_Notification_Mode_All": "Cada Menção / Mensagem Direta",
"Email_Notification_Mode_Disabled": "Desativado",
"Enable_Auto_Translate": "Ativar a tradução automática",
"Enable_notifications": "Habilitar notificações",
"Encrypted": "Criptografado",
@ -223,6 +218,7 @@
"Everyone_can_access_this_channel": "Todos podem acessar este canal",
"Error_uploading": "Erro subindo",
"Expiration_Days": "Expira em (dias)",
"Favorite": "Adicionar aos Favoritos",
"Favorites": "Favoritos",
"Files": "Arquivos",
"File_description": "Descrição do arquivo",
@ -241,6 +237,7 @@
"Generate_New_Link": "Gerar novo convite",
"Group_by_favorites": "Agrupar favoritos",
"Group_by_type": "Agrupar por tipo",
"Hide": "Ocultar",
"Has_joined_the_channel": "entrou no canal",
"Has_joined_the_conversation": "entrou na conversa",
"Has_left_the_channel": "saiu da conversa",
@ -286,8 +283,8 @@
"Login": "Entrar",
"Login_error": "Suas credenciais foram rejeitadas. Tente novamente por favor!",
"Login_with": "Login with",
"Logout": "Sair",
"Logging_out": "Saindo.",
"Logout": "Sair",
"Max_number_of_uses": "Número máximo de usos",
"Max_number_of_users_allowed_is_number": "Número máximo de usuários é {{maxUsers}}",
"Members": "Membros",
@ -300,6 +297,7 @@
"Message_removed": "Mensagem removida",
"message": "mensagem",
"messages": "mensagens",
"Message": "Mensagem",
"Messages": "Mensagens",
"Microphone_Permission_Message": "Rocket.Chat precisa de acesso ao seu microfone para enviar mensagens de áudio.",
"Microphone_Permission": "Acesso ao Microfone",
@ -311,7 +309,6 @@
"Name": "Nome",
"Navigation_history": "Histórico de navegação",
"Never": "Nunca",
"New_in_RocketChat_question_mark": "Novo no Rocket.Chat?",
"New_Message": "Nova Mensagem",
"New_Password": "Nova Senha",
"Next": "Próximo",
@ -326,19 +323,20 @@
"No_Message": "Não há mensagens",
"No_messages_yet": "Não há mensagens ainda",
"No_Reactions": "Sem reações",
"Not_RC_Server": "Este não é um servidor Rocket.Chat.\n{{contact}}",
"Nothing": "Nada",
"Nothing_to_save": "Nada para salvar!",
"Notify_active_in_this_room": "Notificar usuários ativos nesta sala",
"Notify_all_in_this_room": "Notificar todos nesta sala",
"Notifications": "Notificações",
"Notification_Duration": "Duração da notificação",
"Notification_Preferences": "Preferências de notificação",
"Not_RC_Server": "Este não é um servidor Rocket.Chat.\n{{contact}}",
"No_available_agents_to_transfer": "Nenhum agente disponível para transferência",
"Offline": "Offline",
"Oops": "Ops!",
"Omnichannel": "Omnichannel",
"Open_Livechats": "Bate-papos em Andamento",
"Omnichannel_enable_alert": "Você não está disponível no Omnichannel. Você quer ficar disponível?",
"Oops": "Ops!",
"Onboarding_description": "Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.",
"Onboarding_join_workspace": "Entre numa workspace",
"Onboarding_subtitle": "Além da colaboração em equipe",
@ -358,13 +356,14 @@
"Password": "Senha",
"Parent_channel_or_group": "Canal ou grupo pai",
"Permalink_copied_to_clipboard": "Link-permanente copiado para a área de transferência!",
"Phone": "Telefone",
"Pin": "Fixar",
"Pinned_Messages": "Mensagens Fixadas",
"pinned": "fixada",
"Pinned": "Mensagens Fixadas",
"Please_wait": "Por favor, aguarde.",
"Please_enter_your_password": "Por favor, digite sua senha",
"Please_add_a_comment": "Por favor, adicione um comentário",
"Please_enter_your_password": "Por favor, digite sua senha",
"Please_wait": "Por favor, aguarde.",
"Preferences": "Preferências",
"Preferences_saved": "Preferências salvas!",
"Privacy_Policy": " Política de Privacidade",
@ -386,15 +385,16 @@
"Read_External_Permission": "Permissão de acesso à arquivos",
"Read_Only_Channel": "Canal Somente Leitura",
"Read_Only": "Somente Leitura",
"Read_Receipt": "Lida por",
"Receive_Group_Mentions": "Receber menções de grupo",
"Receive_Group_Mentions_Info": "Receber menções @all e @here",
"Register": "Registrar",
"Read_Receipt": "Lida por",
"Repeat_Password": "Repetir Senha",
"Replied_on": "Respondido em:",
"replies": "respostas",
"reply": "resposta",
"Reply": "Responder",
"Report": "Reportar",
"Receive_Notification": "Receber Notificação",
"Receive_notifications_from": "Receber notificação de {{name}}",
"Resend": "Reenviar",
@ -424,6 +424,7 @@
"SAVE": "SALVAR",
"Save_Changes": "Salvar Alterações",
"Save": "Salvar",
"Saved": "Salvo",
"saving_preferences": "salvando preferências",
"saving_profile": "salvando perfil",
"saving_settings": "salvando configurações",
@ -472,12 +473,14 @@
"starred": "favoritou",
"Starred": "Mensagens Favoritas",
"Start_of_conversation": "Início da conversa",
"Started_call": "Chamada iniciada por {{userBy}}",
"Start_a_Discussion": "Iniciar uma Discussão",
"Started_discussion": "Iniciou uma discussão:",
"Started_call": "Chamada iniciada por {{userBy}}",
"Submit": "Enviar",
"Table": "Tabela",
"Take_a_photo": "Tirar uma foto",
"Take_a_video": "Gravar um vídeo",
"Take_it": "Pegue!",
"Terms_of_Service": " Termos de Serviço ",
"Theme": "Tema",
"The_user_wont_be_able_to_type_in_roomName": "O usuário não poderá digitar em {{roomName}}",
@ -491,12 +494,14 @@
"To": "Para",
"topic": "tópico",
"Topic": "Tópico",
"Translate": "Traduzir",
"Try_again": "Tentar novamente",
"Two_Factor_Authentication": "Autenticação de dois fatores",
"Type_the_channel_name_here": "Digite o nome do canal",
"unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR",
"Unblock_user": "Desbloquear usuário",
"Unfavorite": "Remover dos Favoritos",
"Unfollowed_thread": "Parou de seguir tópico",
"Unmute": "Permitir que o usuário fale",
"unmuted": "permitiu que o usuário fale",
@ -511,6 +516,7 @@
"User": "Usuário",
"Users": "Usuários",
"User_added_by": "Usuário {{userAdded}} adicionado por {{userBy}}",
"User_Info": "Informações do usuário",
"User_has_been_key": "Usuário foi {{key}}",
"User_is_no_longer_role_by_": "{{user}} não pertence mais à {{role}} por {{userBy}}",
"User_muted_by": "User {{userMuted}} muted por {{userBy}}",
@ -527,25 +533,31 @@
"Verify_email_desc": "Nós lhe enviamos um e-mail para confirmar o seu registro. Se você não receber um e-mail em breve, por favor retorne e tente novamente.",
"Verify_your_email_for_the_code_we_sent": "Verifique em seu e-mail o código que enviamos",
"Video_call": "Chamada de vídeo",
"View_Original": "Visualizar original",
"Voice_call": "Chamada de voz",
"Waiting_for_network": "Aguardando rede...",
"Websocket_disabled": "Websocket está desativado para esse servidor.\n{{contact}}",
"Welcome": "Bem vindo",
"Whats_your_2fa": "Qual seu código de autenticação?",
"What_are_you_doing_right_now": "O que você está fazendo agora?",
"Whats_your_2fa": "Qual seu código de autenticação?",
"Without_Servers": "Sem Servidores",
"Workspaces": "Workspaces",
"Would_you_like_to_return_the_inquiry": "Deseja retornar a consulta?",
"Write_External_Permission_Message": "Rocket.Chat precisa de acesso à sua galeria para salvar imagens",
"Write_External_Permission": "Acesso à Galeria",
"Yes": "Sim",
"Yes_action_it": "Sim, {{action}}!",
"Yesterday": "Ontem",
"You_are_in_preview_mode": "Está é uma prévia do canal",
"You_are_offline": "Você está offline",
"You_can_search_using_RegExp_eg": "Você pode usar expressões regulares, por exemplo `/^text$/i`",
"You_need_to_verifiy_your_email_address_to_get_notications": "Você precisa confirmar seu endereço de e-mail para obter notificações",
"You_colon": "Você: ",
"you_were_mentioned": "você foi mencionado",
"You_were_removed_from_channel": "Você foi removido de {{channel}}",
"you": "você",
"You": "Você",
"You_need_to_verifiy_your_email_address_to_get_notications": "Você precisa confirmar seu endereço de e-mail para obter notificações",
"Your_certificate": "Seu certificado",
"Your_invite_link_will_expire_after__usesLeft__uses": "Seu link de convite irá vencer depois de {{usesLeft}} usos.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Seu link de convite irá vencer em {{date}} ou depois de {{usesLeft}} usos.",
"Your_invite_link_will_expire_on__date__": "Seu link de convite irá vencer em {{date}}.",
@ -553,10 +565,7 @@
"Your_workspace": "Sua workspace",
"You_will_not_be_able_to_recover_this_message": "Você não será capaz de recuperar essa mensagem!",
"You_will_unset_a_certificate_for_this_server": "Você cancelará a configuração de um certificado para este servidor",
"Would_you_like_to_return_the_inquiry": "Deseja retornar a consulta?",
"Write_External_Permission_Message": "Rocket.Chat precisa de acesso à sua galeria para salvar imagens",
"Write_External_Permission": "Acesso à Galeria",
"Yes": "Sim",
"Change_Language": "Alterar idioma",
"Crash_report_disclaimer": "Nós não rastreamos o conteúdo das suas conversas. O relatório de erros e os eventos do analytics apenas contém informações relevantes para identificarmos problemas e corrigí-los.",
"Type_message": "Digitar mensagem",
"Room_search": "Busca de sala",
@ -568,6 +577,7 @@
"Search_messages": "Buscar mensagens",
"Scroll_messages": "Rolar mensagens",
"Reply_latest": "Responder para última mensagem",
"Reply_in_Thread": "Responder por Tópico",
"Server_selection": "Seleção de servidor",
"Server_selection_numbers": "Selecionar servidor 1...9",
"Add_server": "Adicionar servidor",
@ -654,10 +664,8 @@
"Workspace_URL_Example": "Ex. sua-empresa.rocket.chat",
"This_room_encryption_has_been_enabled_by__username_": "A criptografia para essa sala foi habilitada por {{username}}",
"This_room_encryption_has_been_disabled_by__username_": "A criptografia para essa sala foi desabilitada por {{username}}",
"Apply_Your_Certificate": "Aplicar certificado",
"Do_you_have_a_certificate": "Você tem um certificado?",
"Your_certificate": "Seu certificado",
"Teams": "Times",
"No_team_channels_found": "Nenhum canal encontrado",
"Team_not_found": "Time não encontrado"
"Team_not_found": "Time não encontrado",
"Private_Team": "Equipe Privada"
}

View File

@ -30,7 +30,7 @@
"error-invalid-date": "Data inválida fornecida.",
"error-invalid-description": "Descrição inválida",
"error-invalid-domain": "Domínio inválido",
"error-invalid-email": "E-mail inválido {{emai}}",
"error-invalid-email": "E-mail inválido {{email}}",
"error-invalid-email-address": "Endereço de e-mail invalido",
"error-invalid-file-height": "Altura de ficheiro inválida",
"error-invalid-file-type": "Tipo de ficheiro inválido",
@ -137,14 +137,14 @@
"delete": "apagar",
"Delete": "Apagar",
"DELETE": "APAGAR",
"deleting_room": "apagando sala",
"description": "descrição",
"Description": "Descrição",
"Disable_notifications": "Desactivar notificações",
"Direct_Messages": "Mensagens Directas",
"Disable_notifications": "Desactivar notificações",
"Dont_Have_An_Account": "Não tem uma conta?",
"Do_you_really_want_to_key_this_room_question_mark": "Você quer mesmo {{key}} esta sala?",
"edit": "editar",
"deleting_room": "apagando sala",
"Edit": "Editar",
"Email_or_password_field_is_empty": "O campo de e-mail ou palavra-passe está vazio",
"Email": "E-mail",

View File

@ -29,7 +29,7 @@
"error-invalid-channel": "Недействительный канал.",
"error-invalid-channel-start-with-chars": "Недействительный канал. Начните с @ или #",
"error-invalid-custom-field": "Неверное настраиваемое поле",
"error-invalid-custom-field-name": "Неверное имя настраиваемого поля. Используйте только буквы, цифры, дефисы и символы подчеркивания.",
"error-invalid-custom-field-name": "Неверное имя настраиваемого поля. Используйте только буквы, цифры, дефис и символ подчеркивания.",
"error-invalid-date": "Указана недопустимая дата.",
"error-invalid-description": "Недопустимое описание",
"error-invalid-domain": "Недопустимый домен",
@ -46,9 +46,9 @@
"error-invalid-password": "Неверный пароль",
"error-invalid-redirectUri": "Недопустимый redirectUri",
"error-invalid-role": "Недопустимая роль",
"error-invalid-room": "Недопустимый канал",
"error-invalid-room-name": "{{room_name}} не является допустимым именем канала",
"error-invalid-room-type": "{{type}} не является допустимым типом канала.",
"error-invalid-room": "Недопустимый чат",
"error-invalid-room-name": "{{room_name}} не является допустимым именем чата",
"error-invalid-room-type": "{{type}} не является допустимым типом чата.",
"error-invalid-settings": "Недопустимые параметры",
"error-invalid-subscription": "Недействительная подписка",
"error-invalid-token": "Недопустимый токен",
@ -77,7 +77,7 @@
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "Регистрация пользователей отключена",
"error-user-registration-secret": "Регистрация пользователей разрешена только через секретный URL",
"error-you-are-last-owner": "Вы последний владелец. Пожалуйста, установите нового владельца, прежде чем покинуть комнату.",
"error-you-are-last-owner": "Вы последний владелец. Пожалуйста, назначьте нового владельца, прежде чем покинуть чат.",
"Actions": "Действия",
"activity": "активности",
"Activity": "По активности",
@ -281,6 +281,8 @@
"Invite_Link": "Ссылка Приглашения",
"Invite_users": "Приглашение пользователей",
"Join": "Присоединиться",
"Join_Code": "Код присоединения",
"Insert_Join_Code": "Вставить код присоединения",
"Join_our_open_workspace": "Присоединиться к нашему открытому серверу",
"Join_your_workspace": "Присоединиться к вашему серверу",
"Just_invited_people_can_access_this_channel": "Только приглашенные люди могут получить доступ к этому каналу",
@ -322,7 +324,7 @@
"Mute": "Заглушить",
"muted": "Заглушен",
"My_servers": "Мои серверы",
"N_person_reacted": "{{n}} людей отреагировало",
"N_people_reacted": "отреагировало {{n}} человек",
"N_users": "{{n}} пользователи",
"name": "имя",
"Name": "Имя",
@ -704,5 +706,15 @@
"Enter_workspace_URL": "Введите URL вашего рабочего пространства",
"Workspace_URL_Example": "Например, your-company.rocket.chat",
"This_room_encryption_has_been_enabled_by__username_": "Шифрование для этого чата включено {{username}}",
"This_room_encryption_has_been_disabled_by__username_": "Шифрование для этого чата выключено {{username}}"
"This_room_encryption_has_been_disabled_by__username_": "Шифрование для этого чата выключено {{username}}",
"Teams": "Команды",
"No_team_channels_found": "Каналы не найдены",
"Team_not_found": "Команда не найдена",
"Create_Team": "Создать Команду",
"Team_Name": "Имя Команды",
"Private_Team": "Приватная Команда",
"Read_Only_Team": "Команда только для чтения",
"Broadcast_Team": "Широковещательная Команда",
"creating_team": "создание Команды",
"team-name-already-exists": "Команда с таким названием уже существует"
}

View File

@ -440,7 +440,6 @@
"Room_changed_announcement": "Oda duyurusu, {{userBy}} tarafından {{announcement}} olarak değiştirildi",
"Room_changed_avatar": "Oda profil fotoğrafı {{userBy}} tarafından değiştirildi",
"Room_changed_description": "Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi",
"Room_changed_privacy": "Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi",
"Room_changed_topic": "Oda konusu, {{userBy}} tarafından {{topic}} olarak değiştirildi",
"Room_Files": "Oda Dosyaları",
"Room_Info_Edit": "Oda Bilgilerini Düzenle",
@ -565,7 +564,6 @@
"Username": "Kullanıcı adı",
"Username_or_email": "Kullanıcı adı ya da e-posta",
"Uses_server_configuration": "Sunucu yapılandırmasını kullanır",
"Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture": "Genellikle tartışma, \"Nasıl resim yüklerim?\" gibi bir soruyla başlar.",
"Validating": "Doğrulanıyor",
"Registration_Succeeded": "Kayıt Başarılı!",
"Verify": "Onayla",
@ -600,7 +598,6 @@
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Bir şeyler paylaşmak için Rocket.Chat sunucusuna erişmeniz gerekir.",
"You_need_to_verifiy_your_email_address_to_get_notications": "Bildirim almak için e-posta adresinizi doğrulamanız gerekiyor",
"Your_certificate": "Sertifikanız",
"Your_message": "İletiınız",
"Your_invite_link_will_expire_after__usesLeft__uses": "Davet bağlantınızın geçerliliği {{usesLeft}} kullanımdan sonra sona erecek.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Davet bağlantınızın geçerliliği {{date}} tarihinde veya {{usesLeft}} kullanımdan sonra sona erecek.",
"Your_invite_link_will_expire_on__date__": "Davet bağlantınızın geçerlilik süresi {{date}} tarihinde sona erecek.",
@ -695,12 +692,12 @@
"User_has_been_ignored": "Kullanıcı yok sayıldı.",
"User_has_been_unignored": "Kullanıcı artık yok sayılmıyor.",
"User_has_been_removed_from_s": "Kullanıcı {{s}} alanından kaldırıldı.",
"User__username__is_now_a_leader_of__room_name_": "{{Username}} kullanıcısı artık {{room_name}} lideridir.",
"User__username__is_now_a_moderator_of__room_name_": "{{Username}} kullanıcısı artık bir {{room_name}} moderatörüdür.",
"User__username__is_now_a_owner_of__room_name_": "{{Username}} kullanıcısı artık {{room_name}} adlı odanın sahibidir.",
"User__username__removed_from__room_name__leaders": "{{Username}} adlı kullanıcı, {{room_name}} liderlerinden kaldırıldı.",
"User__username__removed_from__room_name__moderators": "{{Username}} adlı kullanıcı, {{room_name}} moderatörlerinden kaldırıldı.",
"User__username__removed_from__room_name__owners": "{{Username}} adlı kullanıcı, {{room_name}} sahiplerinden kaldırıldı.",
"User__username__is_now_a_leader_of__room_name_": "{{username}} kullanıcısı artık {{room_name}} lideridir.",
"User__username__is_now_a_moderator_of__room_name_": "{{username}} kullanıcısı artık bir {{room_name}} moderatörüdür.",
"User__username__is_now_a_owner_of__room_name_": "{{username}} kullanıcısı artık {{room_name}} adlı odanın sahibidir.",
"User__username__removed_from__room_name__leaders": "{{username}} adlı kullanıcı, {{room_name}} liderlerinden kaldırıldı.",
"User__username__removed_from__room_name__moderators": "{{username}} adlı kullanıcı, {{room_name}} moderatörlerinden kaldırıldı.",
"User__username__removed_from__room_name__owners": "{{username}} adlı kullanıcı, {{room_name}} sahiplerinden kaldırıldı.",
"The_user_will_be_removed_from_s": "Kullanıcı, {{s}} alanından kaldırılacak!",
"Yes_remove_user": "Evet, kullanıcıyı kaldır!",
"Direct_message": "Özel ileti",

View File

@ -33,7 +33,7 @@
"error-invalid-date": "无效的日期",
"error-invalid-description": "无效的描述",
"error-invalid-domain": "无效的域名",
"error-invalid-email": "无效的电子邮件{{emai}}",
"error-invalid-email": "无效的电子邮件{{email}}",
"error-invalid-email-address": "无效的邮件地址",
"error-invalid-file-height": "无效的文件长度",
"error-invalid-file-type": "无效的文件类型",
@ -278,11 +278,11 @@
"is_typing": "正在输入",
"Invalid_or_expired_invite_token": "无效或到期的邀请 token",
"Invalid_server_version": "此 App 版本已不支援您正在连线之服务器版本。当前版本: {{currentVersion}}.\\n\\n最低版本要求: {{minVersion}}",
"Join_your_workspace": "加入您的工作区",
"Invite_Link": "邀请链接",
"Invite_users": "邀请用戶",
"Join": "加入",
"Join_our_open_workspace": "加入开放工作区",
"Join_your_workspace": "加入您的工作区",
"Just_invited_people_can_access_this_channel": "仅有被邀请人能进入这个频道",
"Language": "语言",
"last_message": "最后一条信息",
@ -300,7 +300,7 @@
"Logging_out": "正在登出",
"Logout": "注销",
"Max_number_of_uses": "最大使用次数",
"Max_number_of_users_allowed_is_number": "允许使用者上限数量",
"Max_number_of_users_allowed_is_number": "允许使用者上限数量{{maxUsers}}",
"members": "成员",
"Members": "成员",
"Mentioned_Messages": "被提及的信息",
@ -444,7 +444,7 @@
"Room_Info_Edit": "聊天室信息编辑",
"Room_Info": "聊天室信息",
"Room_Members": "聊天室成员",
"Room_name_changed": "{{userBy}} 将聊天室名称改为:{{{name}}",
"Room_name_changed": "{{userBy}} 将聊天室名称改为:{{name}}",
"SAVE": "保存",
"Save_Changes": "保存更改",
"Save": "保存",

View File

@ -278,11 +278,11 @@
"is_typing": "正在輸入",
"Invalid_or_expired_invite_token": "無效或到期的邀請 token",
"Invalid_server_version": "此 App 版本已不支援您正在連線之伺服器版本。當前版本: {{currentVersion}}.\\n\\n最低版本要求: {{minVersion}}",
"Join_your_workspace": "加入您的工作區",
"Invite_Link": "邀請連結",
"Invite_users": "邀請使用者",
"Join": "加入",
"Join_our_open_workspace": "加入開放工作區",
"Join_your_workspace": "加入您的工作區",
"Just_invited_people_can_access_this_channel": "僅有受邀者能存取此頻道",
"Language": "語言",
"last_message": "最後一則訊息",
@ -300,7 +300,7 @@
"Logging_out": "正在登出",
"Logout": "登出",
"Max_number_of_uses": "最大使用次數",
"Max_number_of_users_allowed_is_number": "允許使用者上限數量",
"Max_number_of_users_allowed_is_number": "允許使用者上限數量 {{maxUsers}}",
"members": "成員",
"Members": "成員",
"Mentioned_Messages": "被提及的訊息",
@ -444,7 +444,7 @@
"Room_Info_Edit": "修改聊天室資訊",
"Room_Info": "聊天室資訊",
"Room_Members": "聊天室成員",
"Room_name_changed": "{{userBy}} 將聊天室名稱改為:{{{name}}",
"Room_name_changed": "{{userBy}} 將聊天室名稱改為:{{name}}",
"SAVE": "儲存",
"Save_Changes": "儲存更改",
"Save": "儲存",

View File

@ -60,6 +60,7 @@ import UserPreferences from './userPreferences';
import { Encryption } from './encryption';
import EventEmitter from '../utils/events';
import { sanitizeLikeString } from './database/utils';
import { TEAM_TYPE } from '../definition/ITeam';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const CURRENT_SERVER = 'currentServer';
@ -196,6 +197,10 @@ const RocketChat = {
clearTimeout(this.connectTimeout);
}
if (this.connectingListener) {
this.connectingListener.then(this.stopListener);
}
if (this.connectedListener) {
this.connectedListener.then(this.stopListener);
}
@ -243,7 +248,7 @@ const RocketChat = {
sdkConnect();
this.connectedListener = this.sdk.onStreamData('connecting', () => {
this.connectingListener = this.sdk.onStreamData('connecting', () => {
reduxStore.dispatch(connectRequest());
});
@ -728,7 +733,24 @@ const RocketChat = {
prid, pmid, t_name, reply, users, encrypted
});
},
createTeam({
name, users, type, readOnly, broadcast, encrypted
}) {
const params = {
name,
users,
type: type ? TEAM_TYPE.PRIVATE : TEAM_TYPE.PUBLIC,
room: {
readOnly,
extraData: {
broadcast,
encrypted
}
}
};
// RC 3.13.0
return this.post('teams.create', params);
},
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0
@ -1136,7 +1158,7 @@ const RocketChat = {
methodCall(...args) {
return new Promise(async(resolve, reject) => {
try {
const result = await this.sdk.methodCall(...args, this.code || '');
const result = await this.sdk?.methodCall(...args, this.code || '');
return resolve(result);
} catch (e) {
if (e.error && (e.error === 'totp-required' || e.error === 'totp-invalid')) {

View File

@ -21,6 +21,10 @@ const createGroupChat = function createGroupChat() {
return RocketChat.createGroupChat();
};
const createTeam = function createTeam(data) {
return RocketChat.createTeam(data);
};
const handleRequest = function* handleRequest({ data }) {
try {
const auth = yield select(state => state.login.isAuthenticated);
@ -29,7 +33,21 @@ const handleRequest = function* handleRequest({ data }) {
}
let sub;
if (data.group) {
if (data.isTeam) {
const {
type,
readOnly,
broadcast,
encrypted
} = data;
logEvent(events.CR_CREATE, {
type,
readOnly,
broadcast,
encrypted
});
sub = yield call(createTeam, data);
} else if (data.group) {
logEvent(events.SELECTED_USERS_CREATE_GROUP);
const result = yield call(createGroupChat);
if (result.success) {
@ -56,7 +74,7 @@ const handleRequest = function* handleRequest({ data }) {
const subCollection = db.get('subscriptions');
yield db.action(async() => {
await subCollection.create((s) => {
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
s._raw = sanitizedRaw({ id: sub.team ? sub.team.roomId : sub.rid }, subCollection.schema);
Object.assign(s, sub);
});
});
@ -64,7 +82,17 @@ const handleRequest = function* handleRequest({ data }) {
// do nothing
}
yield put(createChannelSuccess(sub));
let successParams = {};
if (data.isTeam) {
successParams = {
...sub.team,
rid: sub.team.roomId,
t: sub.team.type ? 'p' : 'c'
};
} else {
successParams = data;
}
yield put(createChannelSuccess(successParams));
} catch (err) {
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']);
yield put(createChannelFailure(err));
@ -81,7 +109,7 @@ const handleSuccess = function* handleSuccess({ data }) {
const handleFailure = function handleFailure({ err }) {
setTimeout(() => {
const msg = err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
const msg = err.data ? I18n.t(err.data.error) : err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
showErrorAlert(msg);
}, 300);
};

View File

@ -51,6 +51,7 @@ export default {
STATUS_F: 'status_f',
STATUS_CUSTOM: 'status_custom',
STATUS_CUSTOM_F: 'status_custom_f',
SET_STATUS_FAIL: 'set_status_fail',
// ROOMS LIST VIEW
RL_TOGGLE_SERVER_DROPDOWN: 'rl_toggle_server_dropdown',
@ -87,6 +88,7 @@ export default {
// NEW MESSAGE VIEW
NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel',
NEW_MSG_CREATE_TEAM: 'new_msg_create_team',
NEW_MSG_CREATE_GROUP_CHAT: 'new_msg_create_group_chat',
NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion',
NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user',

View File

@ -45,3 +45,5 @@ export const getBadgeColor = ({ subscription, messageId, theme }) => {
};
export const makeThreadName = messageRecord => messageRecord.msg || messageRecord?.attachments[0]?.title;
export const isTeamRoom = ({ teamId, joined }) => teamId && joined;

View File

@ -88,8 +88,8 @@ class AuthenticationWebView extends React.PureComponent {
this.dismiss();
}
// eslint-disable-next-line react/sort-comp
debouncedLogin = debounce(params => this.login(params), 3000, true);
// Force 3s delay so the server has time to evaluate the token
debouncedLogin = debounce(params => this.login(params), 3000);
tryLogin = debounce(async() => {
const { Accounts_Iframe_api_url, Accounts_Iframe_api_method } = this.props;

View File

@ -68,12 +68,9 @@ const styles = StyleSheet.create({
});
class CreateChannelView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Create_Channel')
});
static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
baseUrl: PropTypes.string,
create: PropTypes.func.isRequired,
removeUser: PropTypes.func.isRequired,
@ -89,12 +86,19 @@ class CreateChannelView extends React.Component {
theme: PropTypes.string
};
state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false
constructor(props) {
super(props);
const { route } = this.props;
const isTeam = route?.params?.isTeam || false;
this.state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false,
isTeam
};
this.setHeader();
}
shouldComponentUpdate(nextProps, nextState) {
@ -134,6 +138,15 @@ class CreateChannelView extends React.Component {
return false;
}
setHeader = () => {
const { navigation } = this.props;
const { isTeam } = this.state;
navigation.setOptions({
title: isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
});
}
toggleRightButton = (channelName) => {
const { navigation } = this.props;
navigation.setOptions({
@ -152,9 +165,11 @@ class CreateChannelView extends React.Component {
submit = () => {
const {
channelName, type, readOnly, broadcast, encrypted
channelName, type, readOnly, broadcast, encrypted, isTeam
} = this.state;
const { users: usersProps, isFetching, create } = this.props;
const {
users: usersProps, isFetching, create
} = this.props;
if (!channelName.trim() || isFetching) {
return;
@ -163,9 +178,9 @@ class CreateChannelView extends React.Component {
// transform users object into array of usernames
const users = usersProps.map(user => user.name);
// create channel
// create channel or team
create({
name: channelName, users, type, readOnly, broadcast, encrypted
name: channelName, users, type, readOnly, broadcast, encrypted, isTeam
});
Review.pushPositiveEvent();
@ -196,11 +211,12 @@ class CreateChannelView extends React.Component {
}
renderType() {
const { type } = this.state;
const { type, isTeam } = this.state;
return this.renderSwitch({
id: 'type',
value: type,
label: 'Private_Channel',
label: isTeam ? 'Private_Team' : 'Private_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_TYPE);
// If we set the channel as public, encrypted status should be false
@ -210,11 +226,12 @@ class CreateChannelView extends React.Component {
}
renderReadOnly() {
const { readOnly, broadcast } = this.state;
const { readOnly, broadcast, isTeam } = this.state;
return this.renderSwitch({
id: 'readonly',
value: readOnly,
label: 'Read_Only_Channel',
label: isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_READ_ONLY);
this.setState({ readOnly: value });
@ -244,11 +261,12 @@ class CreateChannelView extends React.Component {
}
renderBroadcast() {
const { broadcast, readOnly } = this.state;
const { broadcast, readOnly, isTeam } = this.state;
return this.renderSwitch({
id: 'broadcast',
value: broadcast,
label: 'Broadcast_Channel',
label: isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_BROADCAST);
this.setState({
@ -301,8 +319,10 @@ class CreateChannelView extends React.Component {
}
render() {
const { channelName } = this.state;
const { users, isFetching, theme } = this.props;
const { channelName, isTeam } = this.state;
const {
users, isFetching, theme
} = this.props;
const userCount = users.length;
return (
@ -312,18 +332,18 @@ class CreateChannelView extends React.Component {
keyboardVerticalOffset={128}
>
<StatusBar />
<SafeAreaView testID='create-channel-view'>
<SafeAreaView testID={isTeam ? 'create-team-view' : 'create-channel-view'}>
<ScrollView {...scrollPersistTaps}>
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
<TextInput
autoFocus
style={[styles.input, { backgroundColor: themes[theme].backgroundColor }]}
label={I18n.t('Channel_Name')}
label={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
value={channelName}
onChangeText={this.onChangeText}
placeholder={I18n.t('Channel_Name')}
placeholder={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
returnKeyType='done'
testID='create-channel-name'
testID={isTeam ? 'create-team-name' : 'create-channel-name'}
autoCorrect={false}
autoCapitalize='none'
theme={theme}

View File

@ -116,6 +116,12 @@ class NewMessageView extends React.Component {
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
}
createTeam = () => {
logEvent(events.NEW_MSG_CREATE_TEAM);
const { navigation } = this.props;
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView', { isTeam: true }) });
}
createGroupChat = () => {
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
const { createChannel, maxUsers, navigation } = this.props;
@ -172,6 +178,12 @@ class NewMessageView extends React.Component {
testID: 'new-message-view-create-channel',
first: true
})}
{this.renderButton({
onPress: this.createTeam,
title: I18n.t('Create_Team'),
icon: 'teams',
testID: 'new-message-view-create-team'
})}
{maxUsers > 2 ? this.renderButton({
onPress: this.createGroupChat,
title: I18n.t('Create_Direct_Messages'),
@ -253,7 +265,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
createChannel: params => dispatch(createChannelRequest(params))
create: params => dispatch(createChannelRequest(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));

View File

@ -595,7 +595,7 @@ class RoomActionsView extends React.Component {
return (
<SafeAreaView testID='room-actions-view'>
<StatusBar />
<List.Container>
<List.Container testID='room-actions-scrollview'>
{this.renderRoomInfo()}
{this.renderJitsi()}
{this.renderE2EEncryption()}

View File

@ -7,6 +7,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login';
import { logEvent, events } from '../../utils/log';
import { isTeamRoom } from '../../utils/room';
class RightButtonsContainer extends Component {
static propTypes = {
@ -15,10 +16,11 @@ class RightButtonsContainer extends Component {
rid: PropTypes.string,
t: PropTypes.string,
tmid: PropTypes.string,
teamId: PropTypes.bool,
teamId: PropTypes.string,
navigation: PropTypes.object,
isMasterDetail: PropTypes.bool,
toggleFollowThread: PropTypes.func
toggleFollowThread: PropTypes.func,
joined: PropTypes.bool
};
constructor(props) {
@ -163,7 +165,7 @@ class RightButtonsContainer extends Component {
isFollowingThread, tunread, tunreadUser, tunreadGroup
} = this.state;
const {
t, tmid, threadsEnabled, teamId
t, tmid, threadsEnabled, teamId, joined
} = this.props;
if (t === 'l') {
return null;
@ -181,7 +183,7 @@ class RightButtonsContainer extends Component {
}
return (
<HeaderButton.Container>
{teamId ? (
{isTeamRoom({ teamId, joined }) ? (
<HeaderButton.Item
iconName='channel-public'
onPress={this.goTeamChannels}

View File

@ -38,7 +38,9 @@ import { themes } from '../../constants/colors';
import debounce from '../../utils/debounce';
import ReactionsModal from '../../containers/ReactionsModal';
import { LISTENER } from '../../containers/Toast';
import { getBadgeColor, isBlocked, makeThreadName } from '../../utils/room';
import {
getBadgeColor, isBlocked, makeThreadName, isTeamRoom
} from '../../utils/room';
import { isReadOnly } from '../../utils/isReadOnly';
import { isIOS, isTablet } from '../../utils/deviceInfo';
import { showErrorAlert } from '../../utils/info';
@ -301,7 +303,7 @@ class RoomView extends React.Component {
setHeader = () => {
const {
room, unreadsCount, roomUserId
room, unreadsCount, roomUserId, joined
} = this.state;
const {
navigation, isMasterDetail, theme, baseUrl, user, insets, route
@ -331,7 +333,7 @@ class RoomView extends React.Component {
let numIconsRight = 2;
if (tmid) {
numIconsRight = 1;
} else if (teamId) {
} else if (isTeamRoom({ teamId, joined })) {
numIconsRight = 3;
}
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight });
@ -380,6 +382,8 @@ class RoomView extends React.Component {
rid={rid}
tmid={tmid}
teamId={teamId}
teamMain={teamMain}
joined={joined}
t={t}
navigation={navigation}
toggleFollowThread={this.toggleFollowThread}

View File

@ -17,7 +17,6 @@ import sharedStyles from './Styles';
import * as HeaderButton from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import { animateNextTransition } from '../utils/layoutAnimation';
import { withTheme } from '../theme';
import { getUserSelector } from '../selectors/login';
import {
@ -28,6 +27,9 @@ import {
import { showErrorAlert } from '../utils/info';
import SafeAreaView from '../containers/SafeAreaView';
const ITEM_WIDTH = 250;
const getItemLayout = (_, index) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index });
class SelectedUsersView extends React.Component {
static propTypes = {
baseUrl: PropTypes.string,
@ -50,7 +52,7 @@ class SelectedUsersView extends React.Component {
constructor(props) {
super(props);
this.init();
this.flatlist = React.createRef();
const maxUsers = props.route.params?.maxUsers;
this.state = {
maxUsers,
@ -151,7 +153,6 @@ class SelectedUsersView extends React.Component {
return;
}
animateNextTransition();
if (!this.isChecked(user.name)) {
if (this.isGroupChat() && users.length === maxUsers) {
return showErrorAlert(I18n.t('Max_number_of_users_allowed_is_number', { maxUsers }), I18n.t('Oops'));
@ -184,15 +185,23 @@ class SelectedUsersView extends React.Component {
);
}
setFlatListRef = ref => this.flatlist = ref;
onContentSizeChange = () => this.flatlist.scrollToEnd({ animated: true });
renderSelected = () => {
const { users, theme } = this.props;
if (users.length === 0) {
return null;
}
return (
<FlatList
data={users}
ref={this.setFlatListRef}
onContentSizeChange={this.onContentSizeChange}
getItemLayout={getItemLayout}
keyExtractor={item => item._id}
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
contentContainerStyle={{ marginVertical: 5 }}

View File

@ -91,7 +91,7 @@ class StatusView extends React.Component {
const { statusText } = this.state;
const { user } = this.props;
if (statusText !== user.statusText) {
await this.setCustomStatus();
await this.setCustomStatus(statusText);
}
this.close();
}
@ -101,8 +101,7 @@ class StatusView extends React.Component {
navigation.goBack();
}
setCustomStatus = async() => {
const { statusText } = this.state;
setCustomStatus = async(statusText) => {
const { user, setUser } = this.props;
this.setState({ loading: true });

View File

@ -1,9 +1,9 @@
const random = require('./helpers/random');
const value = random(20);
const data = {
server: 'http://localhost:3000',
adminUser: 'admin',
adminPassword: 'password',
server: 'https://mobile.rocket.chat',
adminUser: 'e2e_admin',
adminPassword: 'p7mFh4yLwCRXSnMvG',
alternateServer: 'https://stable.rocket.chat',
users: {
regular: {
@ -42,6 +42,11 @@ const data = {
name: `detox-private-${ value }`
}
},
teams: {
private: {
name: `detox-team-${ value }`
}
},
registeringUser: {
username: `newuser${ value }`,
password: `password${ value }`,
@ -57,6 +62,11 @@ const data = {
password: `passwordthree${ value }`,
email: `mobile+registeringthree${ value }@rocket.chat`
},
registeringUser4: {
username: `newuserfour${ value }`,
password: `passwordfour${ value }`,
email: `mobile+registeringfour${ value }@rocket.chat`
},
random: value
}
module.exports = data;

View File

@ -7,8 +7,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')).replaceText(server);
await element(by.id('new-server-view-button')).tap();
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();
}
@ -33,7 +32,7 @@ async function login(username, password) {
await element(by.id('login-view-email')).replaceText(username);
await element(by.id('login-view-password')).replaceText(password);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(30000);
}
async function logout() {
@ -52,24 +51,24 @@ async function logout() {
await expect(element(by.id('onboarding-view'))).toBeVisible();
}
async function mockMessage(message) {
await element(by.id('messagebox-input')).atIndex(0).tap();
await element(by.id('messagebox-input')).atIndex(0).typeText(`${ data.random }${ message }`);
await element(by.id('messagebox-send-message')).atIndex(0).tap();
await waitFor(element(by.label(`${ data.random }${ message }`)).atIndex(0)).toExist().withTimeout(60000);
await expect(element(by.label(`${ data.random }${ message }`)).atIndex(0)).toExist();
async function mockMessage(message, isThread = false) {
let 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();
await waitFor(element(by.label(`${ data.random }${ message }`))).toExist().withTimeout(60000);
await expect(element(by.label(`${ data.random }${ message }`))).toExist();
await element(by.label(`${ data.random }${ message }`)).atIndex(0).tap();
};
async function starMessage(message){
const messageLabel = `${ data.random }${ message }`
await waitFor(element(by.label(messageLabel))).toBeVisible().withTimeout(5000);
await element(by.label(messageLabel)).atIndex(0).longPress();
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', 'fast', 0.5);
await element(by.label('Star')).tap();
await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
await waitFor(element(by.id('action-sheet'))).not.toExist().withTimeout(5000);
};
async function pinMessage(message){
@ -80,7 +79,7 @@ async function pinMessage(message){
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.label('Pin')).tap();
await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
await waitFor(element(by.id('action-sheet'))).not.toExist().withTimeout(5000);
}
async function dismissReviewNag(){

View File

@ -1,5 +1,6 @@
const axios = require('axios').default;
const data = require('../data');
const { TEAM_TYPE } = require('../../app/definition/ITeam');
let server = data.server
@ -20,6 +21,7 @@ const login = async (username, password) => {
const authToken = response.data.data.authToken
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) => {
@ -56,6 +58,24 @@ const createChannelIfNotExists = async (channelname) => {
}
}
const createTeamIfNotExists = async (teamname) => {
console.log(`Creating private team ${teamname}`)
try {
await rocketchat.post('teams.create', {
"name": teamname,
"type": TEAM_TYPE.PRIVATE
})
} catch (createError) {
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"
}
}
}
const createGroupIfNotExists = async (groupname) => {
console.log(`Creating private group ${groupname}`)
try {
@ -91,11 +111,11 @@ const changeChannelJoinCode = async (roomId, joinCode) => {
}
}
const sendMessage = async (user, groupname, msg) => {
console.log(`Sending message to ${groupname}`)
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: `#${groupname}`, msg });
await rocketchat.post('chat.postMessage', { channel, msg });
} catch (infoError) {
console.log(JSON.stringify(infoError))
throw "Failed to find or create private group"
@ -132,6 +152,13 @@ const setup = async () => {
}
}
for (var teamKey in data.teams) {
if (data.teams.hasOwnProperty(teamKey)) {
const team = data.teams[teamKey]
await createTeamIfNotExists(team.name)
}
}
return
}
@ -146,5 +173,5 @@ const post = (endpoint, body) => {
}
module.exports = {
setup, sendMessage, get, post
setup, sendMessage, get, post, login
}

View File

@ -166,7 +166,7 @@ describe('E2E Encryption', () => {
await navigateToLogin();
await login(testuser.username, testuser.password);
await navigateToRoom(room);
await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toNotExist().withTimeout(2000);
await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).not.toExist().withTimeout(2000);
await expect(element(by.label('Encrypted message')).atIndex(0)).toExist();
});
@ -178,7 +178,7 @@ describe('E2E Encryption', () => {
await waitFor(element(by.id('e2e-enter-your-password-view'))).toBeVisible().withTimeout(2000);
await element(by.id('e2e-enter-your-password-view-password')).typeText(newPassword);
await element(by.id('e2e-enter-your-password-view-confirm')).tap();
await waitFor(element(by.id('listheader-encryption'))).toNotExist().withTimeout(10000);
await waitFor(element(by.id('listheader-encryption'))).not.toExist().withTimeout(10000);
await navigateToRoom(room);
await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toExist().withTimeout(2000);
});
@ -221,8 +221,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')).replaceText(data.alternateServer);
await element(by.id('new-server-view-button')).tap();
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);
@ -231,7 +230,7 @@ describe('E2E Encryption', () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-password')).typeText(data.registeringUser.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);

View File

@ -75,7 +75,7 @@ describe('Broadcast room', () => {
});
it('should have the message created earlier', async() => {
await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000);
await waitFor(element(by.label(`${ data.random }message`))).toExist().withTimeout(60000);
});
it('should have reply button', async() => {

View File

@ -9,10 +9,10 @@ const profileChangeUser = data.users.profileChanges
const scrollDown = 200;
async function waitForToast() {
// await waitFor(element(by.id('toast'))).toBeVisible().withTimeout(10000);
// await waitFor(element(by.id('toast'))).toBeVisible().withTimeout(1000);
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
// await waitFor(element(by.id('toast'))).not.toBeNotVisible().withTimeout(1000);
// await expect(element(by.id('toast'))).not.toBeVisible();
await sleep(300);
}
@ -76,9 +76,8 @@ describe('Profile screen', () => {
describe('Usage', async() => {
it('should change name and username', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('profile-view-name')).replaceText(`${ profileChangeUser.username }new`);
await element(by.id('profile-view-username')).replaceText(`${ profileChangeUser.username }new`);
await element(by.id('profile-view-username')).typeText(`${ profileChangeUser.username }new`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('profile-view-submit')).tap();
await waitForToast();
@ -88,9 +87,7 @@ describe('Profile screen', () => {
await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${ data.random }@rocket.chat`);
await element(by.id('profile-view-new-password')).replaceText(`${ profileChangeUser.password }new`);
await element(by.id('profile-view-submit')).tap();
await element(by.type('_UIAlertControllerTextField')).replaceText(`${ profileChangeUser.password }`)
// For some reason, replaceText does some type of submit, which submits the alert for us
// await element(by.label('Save').and(by.type('_UIAlertControllerActionView'))).tap();
await element(by.type('_UIAlertControllerTextField')).typeText(`${ profileChangeUser.password }\n`)
await waitForToast();
});

View File

@ -63,31 +63,12 @@ describe('Settings screen', () => {
});
describe('Usage', async() => {
it('should navigate to language view', async() => {
await element(by.id('settings-view-language')).tap();
await waitFor(element(by.id('language-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('language-view-zh-CN'))).toExist();
await expect(element(by.id('language-view-de'))).toExist();
await expect(element(by.id('language-view-en'))).toExist();
await expect(element(by.id('language-view-fr'))).toExist();
await expect(element(by.id('language-view-pt-BR'))).toExist();
await expect(element(by.id('language-view-pt-PT'))).toExist();
await expect(element(by.id('language-view-ru'))).toExist();
await tapBack();
});
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();
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);
// Database was cleared, so the room shouldn't be there anymore while it's fetched again from the server
/**
* FIXME: rooms are fetched to quickly on docker and the test below fails
* We need to think on another way to test database being resetted
*/
// await waitFor(element(by.id(`rooms-list-view-item-${ data.groups.private.name }`))).toNotExist().withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${ data.groups.private.name }`))).toExist().withTimeout(10000);
})
});

View File

@ -63,7 +63,7 @@ describe('Join public room', () => {
describe('Room Actions', async() => {
before(async() => {
await navigateToRoomActions('c');
await navigateToRoomActions();
});
it('should have room actions screen', async() => {
@ -103,7 +103,6 @@ describe('Join public room', () => {
});
it('should have share', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await expect(element(by.id('room-actions-share'))).toBeVisible();
});
@ -142,7 +141,7 @@ describe('Join public room', () => {
});
it('should have disable notifications and leave channel', async() => {
await navigateToRoomActions('c');
await navigateToRoomActions();
await expect(element(by.id('room-actions-view'))).toBeVisible();
await expect(element(by.id('room-actions-info'))).toBeVisible();
// await expect(element(by.id('room-actions-voice'))).toBeVisible();
@ -165,7 +164,6 @@ describe('Join public room', () => {
await expect(element(by.text('Yes, leave it!'))).toBeVisible();
await element(by.text('Yes, leave it!')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await element(by.id('rooms-list-view-search')).typeText('');
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
});
});

View File

@ -41,10 +41,10 @@ describe('Status screen', () => {
});
it('should change status text', async () => {
await element(by.id('status-view-input')).replaceText('status-text-new');
await element(by.id('status-view-input')).typeText('status-text-new');
await element(by.id('status-view-submit')).tap();
await waitForToast();
await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toExist().withTimeout(2000);
});
});
});

View File

@ -24,9 +24,8 @@ 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')).replaceText(data.alternateServer);
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(6000);
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);
});

View File

@ -14,17 +14,12 @@ async function navigateToRoom() {
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
async function navigateToRoomActions() {
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
}
async function openJoinCode() {
await element(by.id('room-view-join-button')).tap();
await waitFor(element(by.id('join-code'))).toBeVisible().withTimeout(5000);
}
describe('Join public room', () => {
describe('Join protected room', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();

View File

@ -22,8 +22,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')).replaceText(data.alternateServer);
await element(by.id('new-server-view-button')).tap();
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);
@ -32,7 +31,7 @@ describe('Delete server', () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser3.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser3.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser3.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser3.password);
await element(by.id('register-view-password')).typeText(data.registeringUser3.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);

View File

@ -2,8 +2,8 @@ const {
device, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, checkServer, navigateToRegister, login } = require('../../helpers/app');
const { post, get } = require('../../helpers/data_setup');
const { tapBack, checkServer, navigateToRegister } = require('../../helpers/app');
const { post, get, login } = require('../../helpers/data_setup');
const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' };
const getDeepLink = (method, server, params) => {
@ -14,35 +14,31 @@ const getDeepLink = (method, server, params) => {
describe('Deep linking', () => {
let userId;
let token;
let authToken;
before(async() => {
const loginResult = await post('login', {
user: data.users.regular.username,
password: data.users.regular.password
})
userId = loginResult.data.data.userId
token = loginResult.data.data.authToken
const loginResult = await login(data.users.regular.username, data.users.regular.password);
({ userId, authToken } = loginResult);
});
describe('Authentication', () => {
it('should run a deep link to an invalid account and raise error', async() => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
delete: true,
url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, 'userId=123&token=abc'),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.text('You\'ve been logged out by the server. Please log in again.'))).toExist().withTimeout(5000); // TODO: we need to improve this message
await waitFor(element(by.text('You\'ve been logged out by the server. Please log in again.'))).toExist().withTimeout(10000); // TODO: we need to improve this message
});
const authAndNavigate = async() => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, `userId=${ userId }&token=${ token }&path=group/${ data.groups.private.name }`),
url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, `userId=${ userId }&token=${ authToken }&path=group/${ data.groups.private.name }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(30000);
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await checkServer(data.server);
@ -56,10 +52,10 @@ describe('Deep linking', () => {
it('should authenticate while logged in another server', async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToRegister(data.alternateServer);
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-name')).replaceText(data.registeringUser4.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser4.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser4.email);
await element(by.id('register-view-password')).typeText(data.registeringUser4.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await authAndNavigate();
@ -86,7 +82,7 @@ describe('Deep linking', () => {
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `rid=${ roomResult.data.group._id }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(15000);
await tapBack();
});
});

View File

@ -0,0 +1,114 @@
const {
device, element, by, waitFor
} = require('detox');
const { navigateToLogin, login, sleep } = require('../../helpers/app');
const { post } = require('../../helpers/data_setup');
const data = require('../../data');
const testuser = data.users.regular
const defaultLaunchArgs = { permissions: { notifications: 'YES' } };
const navToLanguage = async() => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
await element(by.id('settings-view-language')).tap();
await waitFor(element(by.id('language-view'))).toBeVisible().withTimeout(10000);
};
describe('i18n', () => {
describe('OS language', () => {
it('OS set to \'en\' and proper translate to \'en\'', async() => {
await device.launchApp({
...defaultLaunchArgs,
languageAndLocale: {
language: "en",
locale: "en"
},
delete: true
});
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await expect(element(by.id('join-workspace').and(by.label('Join a workspace')))).toBeVisible();
await expect(element(by.id('create-workspace-button').and(by.label('Create a new workspace')))).toBeVisible();
});
it('OS set to unavailable language and fallback to \'en\'', async() => {
await device.launchApp({
...defaultLaunchArgs,
languageAndLocale: {
language: "es-MX",
locale: "es-MX"
}
});
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await expect(element(by.id('join-workspace').and(by.label('Join a workspace')))).toBeVisible();
await expect(element(by.id('create-workspace-button').and(by.label('Create a new workspace')))).toBeVisible();
});
/**
* This test might become outdated as soon as we support the language
* Although this seems to be a bad approach, that's the intention for having fallback enabled
*/
it('OS set to available language and fallback to \'en\' on strings missing translation', async() => {
await device.launchApp({
...defaultLaunchArgs,
languageAndLocale: {
language: "nl",
locale: "nl"
}
});
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await expect(element(by.id('join-workspace').and(by.label('Join a workspace')))).toBeVisible(); // Missing nl translation
await expect(element(by.id('create-workspace-button').and(by.label('Een nieuwe workspace maken')))).toBeVisible();
});
});
describe('Rocket.Chat language', () => {
before(async() => {
await device.launchApp(defaultLaunchArgs);
await navigateToLogin();
await login(testuser.username, testuser.password);
});
it('should select \'en\'', async() => {
await navToLanguage();
await element(by.id('language-view-en')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible();
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profile')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Settings')))).toBeVisible();
await element(by.id('sidebar-close-drawer')).tap();
});
it('should select \'nl\' and fallback to \'en\'', async() => {
await navToLanguage();
await element(by.id('language-view-nl')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible(); // fallback to en
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profiel')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Instellingen')))).toBeVisible();
await element(by.id('sidebar-close-drawer')).tap();
});
it('should set unsupported language and fallback to \'en\'', async() => {
await post('users.setPreferences', { data: { language: 'eo' } }); // Set language to Esperanto
await device.launchApp(defaultLaunchArgs);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
// give the app some time to apply new language
await sleep(3000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible();
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profile')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Settings')))).toBeVisible();
await post('users.setPreferences', { data: { language: 'en' } }); // Set back to english
});
})
});

View File

@ -34,8 +34,7 @@ describe('Onboarding', () => {
});
it('should enter an invalid server and get error', async() => {
await element(by.id('new-server-view-input')).replaceText('invalidtest');
await element(by.id('new-server-view-button')).tap();
await element(by.id('new-server-view-input')).typeText('invalidtest\n');
const errorText = 'Oops!';
await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000);
await element(by.text('OK')).tap();
@ -51,8 +50,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')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap();
await element(by.id('new-server-view-input')).typeText(`${data.server}\n`);
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
});
});

View File

@ -30,6 +30,7 @@ describe('Forgot password screen', () => {
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();
await waitFor(element(by.text('OK'))).toExist().withTimeout(10000);
await element(by.text('OK')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
});

View File

@ -25,7 +25,7 @@ describe('Server history', () => {
it('should tap on a server history and navigate to login', async() => {
await element(by.id(`server-history-${ data.server }`)).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(5000);
await expect(element(by.id('login-view-email'))).toHaveText(data.users.regular.username);
});

View File

@ -94,7 +94,7 @@ describe('Create room screen', () => {
describe('Usage', async() => {
it('should get invalid room', async() => {
await element(by.id('create-channel-name')).replaceText('general');
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();
@ -103,16 +103,17 @@ describe('Create room screen', () => {
it('should create public room', async() => {
const room = `public${ data.random }`;
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-name')).replaceText('');
await element(by.id('create-channel-name')).typeText(room);
await element(by.id('create-channel-type')).tap();
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await waitFor(element(by.id('room-view'))).toExist().withTimeout(6000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ room }`))).toExist().withTimeout(60000);
await waitFor(element(by.id(`room-view-title-${ room }`))).toExist().withTimeout(6000);
await expect(element(by.id(`room-view-title-${ room }`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(6000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toExist();
});
@ -127,7 +128,7 @@ describe('Create room screen', () => {
await waitFor(element(by.id('selected-user-rocket.cat'))).toExist().withTimeout(5000);
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(room);
await element(by.id('create-channel-name')).typeText(room);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
@ -149,7 +150,7 @@ describe('Create room screen', () => {
await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(10000);
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-name')).typeText(room);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();

View File

@ -125,7 +125,6 @@ describe('Room screen', () => {
});
it('should not show user autocomplete on @ in the middle of a string', async() => {
const username = data.users.regular.username
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`email@gmail`);
await waitFor(element(by.id('messagebox-container'))).toNotExist().withTimeout(4000);
@ -149,8 +148,8 @@ describe('Room screen', () => {
await element(by.id('messagebox-input')).clearText();
});
it('should draft message', async () => {
await element(by.id('messagebox-input')).atIndex(0).tap();
await element(by.id('messagebox-input')).atIndex(0).typeText(`${ data.random }draft`);
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`${ data.random }draft`);
await tapBack();
await navigateToRoom(mainRoom);
@ -191,9 +190,9 @@ describe('Room screen', () => {
await element(by.label(`${ data.random }message`)).atIndex(0).longPress();
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', 'fast', 0.5);
await waitFor(element(by.label('Unstar'))).toBeVisible().withTimeout(2000);
await element(by.id('action-sheet-backdrop')).tap();
await element(by.id('action-sheet-handle')).swipe('up', 'slow', 0.5);
await waitFor(element(by.label('Unstar'))).toBeVisible().withTimeout(6000);
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.8);
});
it('should react to message', async() => {
@ -267,14 +266,14 @@ describe('Room screen', () => {
await mockMessage('pin')
await pinMessage('pin')
await waitFor(element(by.label(`${ data.random }pin`)).atIndex(0)).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(`${ data.users.regular.username } Message pinned`)).atIndex(0)).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(`${ data.random }pin`)).atIndex(0)).toExist().withTimeout(5000);
await waitFor(element(by.label(`${ data.users.regular.username } Message pinned`)).atIndex(0)).toExist().withTimeout(5000);
await element(by.label(`${ data.random }pin`)).atIndex(0).longPress();
await waitFor(element(by.id('action-sheet'))).toExist().withTimeout(1000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by.label('Unpin'))).toBeVisible().withTimeout(2000);
await element(by.id('action-sheet-backdrop')).tap();
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.8);
});
it('should delete message', async() => {
@ -285,6 +284,7 @@ describe('Room screen', () => {
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', 'fast', 0.5);
await waitFor(element(by.label('Delete'))).toExist().withTimeout(1000);
await element(by.label('Delete')).tap();
const deleteAlertMessage = 'You will not be able to recover this message!';
@ -294,12 +294,5 @@ describe('Room screen', () => {
await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toNotExist().withTimeout(2000);
});
});
// after(async() => {
// await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
// await tapBack();
// await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
// await expect(element(by.id('rooms-list-view'))).toExist();
// });
});
});

View File

@ -223,10 +223,10 @@ describe('Room actions screen', () => {
//Go to starred messages
await element(by.id('room-actions-starred')).tap();
await waitFor(element(by.id('starred-messages-view'))).toExist().withTimeout(2000);
await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000);
await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toExist().withTimeout(60000);
//Unstar message
await element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view'))).longPress();
await element(by.label(`${ data.random }messageToStar`)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.label('Unstar')).tap();
@ -247,18 +247,18 @@ describe('Room actions screen', () => {
//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.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await element(by.id('room-actions-pinned')).tap();
await waitFor(element(by.id('pinned-messages-view'))).toExist().withTimeout(2000);
await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000);
await element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view'))).longPress();
await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toExist().withTimeout(6000);
await element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view'))).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.label('Unpin')).tap();
await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000);
await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).not.toExist().withTimeout(6000);
await backToActions();
});
@ -283,7 +283,7 @@ describe('Room actions screen', () => {
describe('Notification', async() => {
it('should navigate to notification preference view', async() => {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000);
await element(by.id('room-actions-notifications')).tap();
await waitFor(element(by.id('notification-preference-view'))).toExist().withTimeout(2000);
@ -311,7 +311,7 @@ describe('Room actions screen', () => {
it('should have notification sound option', async() => {
// Ugly hack to scroll on detox
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('notification-preference-view-sound'))).toExist().withTimeout(4000);
});
@ -335,7 +335,7 @@ describe('Room actions screen', () => {
const user = data.users.alternate
it('should tap on leave channel and raise alert', async() => {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-leave-channel'))).toExist().withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.text('Yes, leave it!'))).toExist().withTimeout(2000);
@ -368,7 +368,7 @@ describe('Room actions screen', () => {
await element(by.id('room-actions-members')).tap();
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
await backToActions(1);
await backToActions();
});
describe('Room Members', async() => {
@ -414,7 +414,7 @@ describe('Room actions screen', () => {
}
const closeActionSheet = async() => {
await element(by.id('action-sheet-backdrop')).tap();
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6);
}
it('should set/remove as owner', async() => {
@ -483,7 +483,7 @@ describe('Room actions screen', () => {
it('should ignore user', async() => {
const message = `${ data.random }ignoredmessagecontent`;
const channelName = data.groups.private.name;
const channelName = `#${ data.groups.private.name }`;
await sendMessage(user, channelName, message);
await openActionSheet(user.username);
await element(by.label('Ignore')).tap();

View File

@ -81,7 +81,7 @@ describe('Threads', () => {
it('should navigate to thread from button', async() => {
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id('room-view'))).toExist().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await tapBack();
@ -89,7 +89,7 @@ describe('Threads', () => {
it('should toggle follow thread', async() => {
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id('room-view'))).toExist().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await element(by.id('room-view-header-unfollow')).tap();
@ -102,7 +102,7 @@ describe('Threads', () => {
it('should send message in thread only', async() => {
const messageText = 'threadonly';
await mockMessage(messageText);
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.label(`${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
@ -113,7 +113,7 @@ describe('Threads', () => {
it('should mark send to channel and show on main channel', async() => {
const messageText = 'sendToChannel';
await element(by.id(`message-thread-button-${ thread }`)).tap();
await element(by.id('messagebox-input')).atIndex(0).typeText(messageText);
await element(by.id('messagebox-input-thread')).typeText(messageText);
await element(by.id('messagebox-send-to-channel')).tap();
await element(by.id('messagebox-send-message')).tap();
await tapBack();
@ -128,16 +128,14 @@ describe('Threads', () => {
await mockMessage('dummymessagebetweenthethread');
await dismissReviewNag() //TODO: Create a proper test for this elsewhere.
await element(by.id(`message-thread-button-${ thread }`)).tap();
await element(by.id('messagebox-input')).atIndex(0).typeText(messageText);
await element(by.id('messagebox-input-thread')).typeText(messageText);
await element(by.id('messagebox-send-to-channel')).tap();
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.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.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'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await tapBack();
@ -149,7 +147,6 @@ describe('Threads', () => {
await waitFor(element(by.id('thread-messages-view'))).toExist().withTimeout(5000);
await expect(element(by.id('thread-messages-view'))).toExist();
await element(by.id(`thread-messages-view-${ thread }`)).atIndex(0).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await tapBack();
@ -160,31 +157,20 @@ describe('Threads', () => {
it('should draft thread message', async () => {
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await element(by.id('messagebox-input')).atIndex(0).tap();
await element(by.id('messagebox-input')).atIndex(0).typeText(`${ thread }draft`);
await element(by.id('messagebox-input-thread')).typeText(`${ thread }draft`);
await tapBack();
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id('messagebox-input')).atIndex(0)).toHaveText(`${ thread }draft`);
await element(by.id('messagebox-input')).atIndex(0).clearText();
await expect(element(by.id('messagebox-input-thread'))).toHaveText(`${ thread }draft`);
await element(by.id('messagebox-input-thread')).clearText();
await tapBack();
await element(by.id(`message-thread-button-${ thread }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id('messagebox-input')).atIndex(0)).toHaveText('');
await expect(element(by.id('messagebox-input-thread'))).toHaveText('');
});
});
// after(async() => {
// await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
// await tapBack();
// await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
// await expect(element(by.id('rooms-list-view'))).toExist();
// });
});
});

View File

@ -2,7 +2,8 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { navigateToLogin, login, mockMessage, tapBack, searchRoom, logout } = require('../../helpers/app');
const { navigateToLogin, login, searchRoom } = require('../../helpers/app');
const { sendMessage } = require('../../helpers/data_setup')
async function navigateToRoom(user) {
await searchRoom(`${ user }`);
@ -20,23 +21,21 @@ 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() => {
it('should mark message as unread', async() => {
await mockMessage('message')
await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toExist();
await tapBack();
await logout();
await navigateToLogin();
await login(data.users.alternate.username, data.users.alternate.password);
await navigateToRoom(data.users.regular.username);
await element(by.label(`${ data.random }message`)).atIndex(0).longPress();
const message = `${ data.random }message`;
const channelName = `@${ data.users.regular.username }`;
await sendMessage(data.users.alternate, channelName, message);
await waitFor(element(by.label(message)).atIndex(0)).toExist().withTimeout(30000);
await element(by.label(message)).atIndex(0).longPress();
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', 'fast', 0.5);
await element(by.label('Mark Unread')).tap();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(5000);
await expect(element(by.id(`rooms-list-view-item-${data.users.regular.username}`))).toExist();
await expect(element(by.id(`rooms-list-view-item-${data.users.alternate.username}`))).toExist();
});
});
});

View File

@ -119,7 +119,7 @@ describe('Room info screen', () => {
it('should have type switch', async() => {
// Ugly hack to scroll on detox
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.8);
await expect(element(by.id('room-info-edit-view-t'))).toExist();
});
@ -145,7 +145,7 @@ describe('Room info screen', () => {
after(async() => {
// Ugly hack to scroll on detox
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8);
});
});
@ -164,7 +164,7 @@ describe('Room info screen', () => {
it('should change room name', async() => {
await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }new`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
@ -174,10 +174,10 @@ describe('Room info screen', () => {
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8);
});
it('should reset form', async() => {
@ -186,7 +186,7 @@ describe('Room info screen', () => {
await element(by.id('room-info-edit-view-topic')).replaceText('abc');
await element(by.id('room-info-edit-view-announcement')).replaceText('abc');
await element(by.id('room-info-edit-view-password')).replaceText('abc');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
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-react-when-ro')).tap();
@ -200,12 +200,12 @@ describe('Room info screen', () => {
await expect(element(by.id('room-info-edit-view-t'))).toHaveValue('1');
await expect(element(by.id('room-info-edit-view-ro'))).toHaveValue('0');
await expect(element(by.id('room-info-edit-view-react-when-ro'))).toBeNotVisible();
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.8);
});
it('should change room description', async() => {
await element(by.id('room-info-edit-view-description')).replaceText('new description');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
@ -218,7 +218,7 @@ describe('Room info screen', () => {
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await element(by.id('room-info-edit-view-topic')).replaceText('new topic');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
@ -231,7 +231,7 @@ describe('Room info screen', () => {
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
@ -243,14 +243,14 @@ describe('Room info screen', () => {
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-password')).replaceText('password');
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
});
it('should change room type', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-t')).tap();
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
@ -272,7 +272,7 @@ describe('Room info screen', () => {
// });
it('should archive room', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-archive')).tap();
await waitFor(element(by.text('Yes, archive it!'))).toExist().withTimeout(5000);
await element(by.text('Yes, archive it!')).tap();
@ -288,7 +288,7 @@ describe('Room info screen', () => {
});
it('should delete room', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-delete')).tap();
await waitFor(element(by.text('Yes, delete it!'))).toExist().withTimeout(5000);
await element(by.text('Yes, delete it!')).tap();

View File

@ -0,0 +1,82 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app');
describe('Create team screen', () => {
before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('New Message', async() => {
before(async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
});
describe('Render', async() => {
it('should have team button', async() => {
await waitFor(element(by.id('new-message-view-create-channel'))).toBeVisible().withTimeout(2000);
});
})
describe('Usage', async() => {
it('should navigate to select users', async() => {
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() => {
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);
});
it('should select/unselect user', async() => {
// Spotlight issues
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
await element(by.id('selected-user-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeNotVisible().withTimeout(10000);
// Spotlight issues
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000);
});
it('should 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() => {
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-submit')).tap();
await element(by.text('OK')).tap();
});
it('should create private team', async() => {
const room = `private${ data.random }`;
await element(by.id('create-channel-name')).replaceText('');
await element(by.id('create-channel-name')).typeText(room);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(20000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ room }`))).toExist().withTimeout(6000);
await expect(element(by.id(`room-view-title-${ room }`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(6000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toExist();
});
})
});
});

View File

@ -1450,6 +1450,7 @@
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = S6UPZG7ZR3;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
@ -1569,6 +1570,7 @@
DEVELOPMENT_TEAM = S6UPZG7ZR3;
ENABLE_BITCODE = NO;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/Crashlytics/iOS\"",
@ -1687,11 +1689,12 @@
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = S6UPZG7ZR3;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.16.0;
MARKETING_VERSION = 4.16.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
@ -1728,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.16.0;
MARKETING_VERSION = 4.16.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1756,6 +1759,7 @@
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = S6UPZG7ZR3;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",

View File

@ -23,7 +23,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.16.0</string>
<string>4.16.2</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.16.0</string>
<string>4.16.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>KeychainGroup</key>

View File

@ -1,6 +1,6 @@
{
"name": "rocket-chat-reactnative",
"version": "4.16.0",
"version": "4.16.2",
"private": true,
"scripts": {
"start": "react-native start",
@ -67,6 +67,7 @@
"js-base64": "2.5.2",
"js-sha256": "^0.9.0",
"lodash": "4.17.20",
"mocha": "7.1.2",
"moment": "2.27.0",
"pretty-bytes": "^5.3.0",
"prop-types": "15.7.2",
@ -144,7 +145,7 @@
"babel-runtime": "^6.26.0",
"bugsnag-sourcemaps": "1.3.0",
"codecov": "3.7.1",
"detox": "^18.8.1",
"detox": "^18.10.0",
"emotion-theming": "10.0.27",
"eslint": "6.8.0",
"eslint-plugin-import": "2.22.0",
@ -156,7 +157,6 @@
"jest": "^25.1.0",
"jest-cli": "^23.6.0",
"metro-react-native-babel-preset": "^0.59.0",
"mocha": "7.1.2",
"otp.js": "1.2.0",
"patch-package": "6.2.2",
"react-dom": "16.13.1",
@ -209,8 +209,7 @@
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11 Pro",
"os": "13.7"
"type": "iPhone 11 Pro"
}
},
"ios.sim.release": {
@ -218,8 +217,7 @@
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11 Pro",
"os": "13.7"
"type": "iPhone 11 Pro"
},
"artifacts": {
"plugins": {

View File

@ -1,11 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';
import { StyleSheet } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import { themes } from '../../app/constants/colors';
import Avatar from '../../app/containers/Avatar/Avatar';
import Status from '../../app/containers/Status/Status';
import StoriesSeparator from './StoriesSeparator';
import sharedStyles from '../../app/views/Styles';
const styles = StyleSheet.create({
@ -16,125 +15,152 @@ const styles = StyleSheet.create({
const server = 'https://open.rocket.chat';
const Separator = ({ title, theme }) => <StoriesSeparator title={title} theme={theme} />;
Separator.propTypes = {
title: PropTypes.string,
theme: PropTypes.string
};
const _theme = 'light';
const AvatarStories = ({ theme }) => (
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
<Separator title='Avatar by text' theme={theme} />
<Avatar
text='Avatar'
server={server}
size={56}
const stories = storiesOf('Avatar', module);
stories.add('Avatar by text', () => (
<Avatar
text='Avatar'
server={server}
size={56}
/>
));
stories.add('Avatar by roomId', () => (
<Avatar
type='p'
rid='devWBbYr7inwupPqK'
server={server}
size={56}
/>
));
stories.add('Avatar by url', () => (
<Avatar
avatar='https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg'
server={server}
size={56}
/>
));
stories.add('Avatar by path', () => (
<Avatar
avatar='/avatar/diego.mello'
server={server}
size={56}
/>
));
stories.add('With ETag', () => (
<Avatar
type='d'
text='djorkaeff.alexandre'
avatarETag='5ag8KffJcZj9m5rCv'
server={server}
size={56}
/>
));
stories.add('Without ETag', () => (
<Avatar
type='d'
text='djorkaeff.alexandre'
server={server}
size={56}
/>
));
stories.add('Emoji', () => (
<Avatar
emoji='troll'
getCustomEmoji={() => ({ name: 'troll', extension: 'jpg' })}
server={server}
size={56}
/>
));
stories.add('Direct', () => (
<Avatar
text='diego.mello'
server={server}
type='d'
size={56}
/>
));
stories.add('Channel', () => (
<Avatar
text='general'
server={server}
type='c'
size={56}
/>
));
stories.add('Touchable', () => (
<Avatar
text='Avatar'
server={server}
onPress={() => console.log('Pressed!')}
size={56}
/>
));
stories.add('Static', () => (
<Avatar
avatar='https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg'
server={server}
isStatic
size={56}
/>
));
stories.add('Avatar by roomId', () => (
<Avatar
type='p'
rid='devWBbYr7inwupPqK'
server={server}
size={56}
/>
));
stories.add('Custom borderRadius', () => (
<Avatar
text='Avatar'
server={server}
borderRadius={28}
size={56}
/>
));
stories.add('Children', () => (
<Avatar
text='Avatar'
server={server}
size={56}
>
<Status
size={24}
style={[sharedStyles.status, styles.status]}
theme={_theme}
/>
<Separator title='Avatar by roomId' theme={theme} />
<Avatar
type='p'
rid='devWBbYr7inwupPqK'
server={server}
size={56}
/>
<Separator title='Avatar by url' theme={theme} />
<Avatar
avatar='https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg'
server={server}
size={56}
/>
<Separator title='Avatar by path' theme={theme} />
<Avatar
avatar='/avatar/diego.mello'
server={server}
size={56}
/>
<Separator title='With ETag' theme={theme} />
<Avatar
type='d'
text='djorkaeff.alexandre'
avatarETag='5ag8KffJcZj9m5rCv'
server={server}
size={56}
/>
<Separator title='Without ETag' theme={theme} />
<Avatar
type='d'
text='djorkaeff.alexandre'
server={server}
size={56}
/>
<Separator title='Emoji' theme={theme} />
<Avatar
emoji='troll'
getCustomEmoji={() => ({ name: 'troll', extension: 'jpg' })}
server={server}
size={56}
/>
<Separator title='Direct' theme={theme} />
<Avatar
text='diego.mello'
server={server}
type='d'
size={56}
/>
<Separator title='Channel' theme={theme} />
<Avatar
text='general'
server={server}
type='c'
size={56}
/>
<Separator title='Touchable' theme={theme} />
<Avatar
text='Avatar'
server={server}
onPress={() => console.log('Pressed!')}
size={56}
/>
<Separator title='Static' theme={theme} />
<Avatar
avatar='https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg'
server={server}
isStatic
size={56}
/>
<Separator title='Custom borderRadius' theme={theme} />
<Avatar
text='Avatar'
server={server}
borderRadius={28}
size={56}
/>
<Separator title='Children' theme={theme} />
<Avatar
text='Avatar'
server={server}
size={56}
>
<View style={[sharedStyles.status, { backgroundColor: themes[theme].backgroundColor }]}>
<Status
size={20}
status='online'
/>
</View>
</Avatar>
<Separator title='Wrong server' theme={theme} />
<Avatar
text='Avatar'
server='https://google.com'
size={56}
/>
<Separator title='Custom style' theme={theme} />
<Avatar
text='Avatar'
server={server}
size={56}
style={styles.custom}
/>
</ScrollView>
);
AvatarStories.propTypes = {
theme: PropTypes.string
};
export default AvatarStories;
</Avatar>
));
stories.add('Wrong server', () => (
<Avatar
text='Avatar'
server='https://google.com'
size={56}
/>
));
stories.add('Custom style', () => (
<Avatar
text='Avatar'
server={server}
size={56}
style={styles.custom}
/>
));

View File

@ -1,14 +1,19 @@
/* eslint-disable react/prop-types */
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import Markdown from '../../app/containers/markdown';
import StoriesSeparator from './StoriesSeparator';
import { themes } from '../../app/constants/colors';
const theme = 'light';
const styles = StyleSheet.create({
container: {
marginHorizontal: 15
marginHorizontal: 15,
backgroundColor: themes[theme].backgroundColor,
marginVertical: 50
},
separator: {
marginHorizontal: 10,
@ -37,258 +42,231 @@ const getCustomEmoji = (content) => {
return customEmoji;
};
// eslint-disable-next-line arrow-body-style
export default ({ theme }) => {
return (
<ScrollView
style={{
backgroundColor: themes[theme].backgroundColor,
marginVertical: 50
}}
contentContainerStyle={{
paddingBottom: 50
}}
>
<StoriesSeparator style={styles.separator} title='Short Text' theme={theme} />
<View style={styles.container}>
<Markdown msg='This is Rocket.Chat' theme={theme} />
</View>
const stories = storiesOf('Markdown', module);
<StoriesSeparator style={styles.separator} title='Long Text' theme={theme} />
<View style={styles.container}>
<Markdown
msg={longText}
theme={theme}
/>
</View>
stories.add('Text', () => (
<View style={styles.container}>
<Markdown msg='This is Rocket.Chat' theme={theme} />
<Markdown
msg={longText}
theme={theme}
/>
<Markdown
msg={lineBreakText}
theme={theme}
/>
<Markdown
msg={sequentialEmptySpacesText}
theme={theme}
/>
<Markdown
msg='Strong emphasis, aka bold, with **asterisks** or __underscores__'
theme={theme}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Line Break Text' theme={theme} />
<View style={styles.container}>
<Markdown
msg={lineBreakText}
theme={theme}
/>
</View>
stories.add('Edited', () => (
<View style={styles.container}>
<Markdown
msg='This is edited'
theme={theme}
isEdited
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Sequential empty spaces' theme={theme} />
<View style={styles.container}>
<Markdown
msg={sequentialEmptySpacesText}
theme={theme}
/>
</View>
stories.add('Preview', () => (
<View style={styles.container}>
<Markdown
msg={longText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg={lineBreakText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg={sequentialEmptySpacesText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown #general #unknown'
theme={theme}
numberOfLines={1}
preview
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
channels={[{ _id: '123', name: 'test-channel' }]}
username='rocket.cat'
/>
<Markdown
msg='Testing: 😃 :+1: :marioparty:'
getCustomEmoji={getCustomEmoji}
theme={theme}
numberOfLines={1}
preview
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Edited' theme={theme} />
<View style={styles.container}>
<Markdown
msg='This is edited'
theme={theme}
isEdited
/>
</View>
stories.add('Mentions', () => (
<ScrollView style={styles.container}>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown'
theme={theme}
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
username='rocket.cat'
/>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown'
theme={theme}
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
username='rocket.cat'
useRealName
/>
</ScrollView>
));
<StoriesSeparator style={styles.separator} title='Preview' theme={theme} />
<View style={styles.container}>
<Markdown
msg={longText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg={lineBreakText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg={sequentialEmptySpacesText}
theme={theme}
numberOfLines={1}
preview
/>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown #general #unknown'
theme={theme}
numberOfLines={1}
preview
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
channels={[{ _id: '123', name: 'test-channel' }]}
username='rocket.cat'
/>
<Markdown
msg='Testing: 😃 :+1: :marioparty:'
getCustomEmoji={getCustomEmoji}
theme={theme}
numberOfLines={1}
preview
/>
</View>
stories.add('Hashtag', () => (
<View style={styles.container}>
<Markdown
msg='#test-channel #unknown'
theme={theme}
channels={[{ _id: '123', name: 'test-channel' }]}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Mentions' theme={theme} />
<View style={styles.container}>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown'
theme={theme}
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
username='rocket.cat'
/>
</View>
stories.add('Emoji', () => (
<View style={styles.container}>
<Markdown msg='Unicode: 😃😇👍' theme={theme} />
<Markdown msg='Shortnames: :joy::+1:' theme={theme} />
<Markdown
msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:'
theme={theme}
getCustomEmoji={getCustomEmoji}
baseUrl={baseUrl}
/>
<Markdown
msg='😃 :+1: :marioparty:'
theme={theme}
getCustomEmoji={getCustomEmoji}
baseUrl={baseUrl}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Mentions with Real Name' theme={theme} />
<View style={styles.container}>
<Markdown
msg='@rocket.cat @name1 @all @here @unknown'
theme={theme}
mentions={[
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
{ _id: 'random2', name: 'Name', username: 'name1' },
{ _id: 'here', username: 'here' },
{ _id: 'all', username: 'all' }
]}
username='rocket.cat'
useRealName
/>
</View>
<StoriesSeparator style={styles.separator} title='Hashtag' theme={theme} />
<View style={styles.container}>
<Markdown
msg='#test-channel #unknown'
theme={theme}
channels={[{ _id: '123', name: 'test-channel' }]}
/>
</View>
<StoriesSeparator style={styles.separator} title='Emoji' theme={theme} />
<View style={styles.container}>
<Markdown msg='Unicode: 😃😇👍' theme={theme} />
<Markdown msg='Shortnames: :joy::+1:' theme={theme} />
<Markdown
msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:'
theme={theme}
getCustomEmoji={getCustomEmoji}
baseUrl={baseUrl}
/>
<Markdown
msg='😃 :+1: :marioparty:'
theme={theme}
getCustomEmoji={getCustomEmoji}
baseUrl={baseUrl}
/>
</View>
<StoriesSeparator style={styles.separator} title='Block Quote' theme={theme} />
<View style={styles.container}>
<Markdown
msg={`> This is block quote
stories.add('Block quote', () => (
<View style={styles.container}>
<Markdown
msg={`> This is block quote
this is a normal line`}
theme={theme}
/>
</View>
theme={theme}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Links' theme={theme} />
<View style={styles.container}>
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' theme={theme} />
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' theme={theme} />
</View>
stories.add('Links', () => (
<View style={styles.container}>
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' theme={theme} />
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' theme={theme} />
</View>
));
<StoriesSeparator style={styles.separator} title='Image' theme={theme} />
<View style={styles.container}>
<Markdown msg='![alt text](https://play.google.com/intl/en_us/badges/images/badge_new.png)' theme={theme} />
</View>
<StoriesSeparator style={styles.separator} title='Headers' theme={theme} />
<View style={styles.container}>
<Markdown
msg='# Header 1'
theme={theme}
/>
<Markdown
msg='## Header 2'
theme={theme}
/>
<Markdown
msg='### Header 3'
theme={theme}
/>
<Markdown
msg='#### Header 4'
theme={theme}
/>
<Markdown
msg='##### Header 5'
theme={theme}
/>
<Markdown
msg='###### Header 6'
theme={theme}
/>
</View>
stories.add('Image', () => (
<View style={styles.container}>
<Markdown msg='![alt text](https://play.google.com/intl/en_us/badges/images/badge_new.png)' theme={theme} />
</View>
));
<StoriesSeparator style={styles.separator} title='Inline Code' theme={theme} />
<View style={styles.container}>
<Markdown
msg='This is `inline code`'
theme={theme}
/>
</View>
stories.add('Headers', () => (
<View style={styles.container}>
<Markdown
msg='# Header 1'
theme={theme}
/>
<Markdown
msg='## Header 2'
theme={theme}
/>
<Markdown
msg='### Header 3'
theme={theme}
/>
<Markdown
msg='#### Header 4'
theme={theme}
/>
<Markdown
msg='##### Header 5'
theme={theme}
/>
<Markdown
msg='###### Header 6'
theme={theme}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Code Block' theme={theme} />
<View style={styles.container}>
<Markdown
msg='Inline `code` has `back-ticks around` it.
stories.add('Code', () => (
<View style={styles.container}>
<Markdown
msg='This is `inline code`'
theme={theme}
/>
<Markdown
msg='Inline `code` has `back-ticks around` it.
```
Code block
```'
theme={theme}
/>
</View>
theme={theme}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Lists' theme={theme} />
<View style={styles.container}>
<Markdown
msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'}
theme={theme}
/>
</View>
stories.add('Lists', () => (
<View style={styles.container}>
<Markdown
msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'}
theme={theme}
/>
<Markdown
msg={'1. Open Source\n2. Rocket.Chat'}
theme={theme}
/>
</View>
));
<StoriesSeparator style={styles.separator} title='Numbered Lists' theme={theme} />
<View style={styles.container}>
<Markdown
msg={'1. Open Source\n2. Rocket.Chat'}
theme={theme}
/>
</View>
<StoriesSeparator style={styles.separator} title='Emphasis' theme={theme} />
<View style={styles.container}>
<Markdown
msg='Strong emphasis, aka bold, with **asterisks** or __underscores__'
theme={theme}
/>
</View>
<StoriesSeparator style={styles.separator} title='Table' theme={theme} />
<View style={styles.container}>
<Markdown
msg='First Header | Second Header
stories.add('Table', () => (
<View style={styles.container}>
<Markdown
msg='First Header | Second Header
------------ | -------------
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column'
theme={theme}
/>
</View>
</ScrollView>
);
};
theme={theme}
/>
</View>
));

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,18 @@
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { ScrollView, Dimensions } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import { Provider } from 'react-redux';
// import moment from 'moment';
import { themes } from '../../app/constants/colors';
import RoomItemComponent from '../../app/presentation/RoomItem/RoomItem';
import { longText } from '../utils';
import StoriesSeparator from './StoriesSeparator';
import { store } from './index';
const baseUrl = 'https://open.rocket.chat';
const { width } = Dimensions.get('window');
let _theme = 'light';
const _theme = 'light';
const lastMessage = {
u: {
username: 'diego.mello'
@ -22,7 +25,6 @@ const updatedAt = {
const RoomItem = props => (
<RoomItemComponent
rid='abc'
type='d'
name='rocket.cat'
avatar='rocket.cat'
@ -34,98 +36,109 @@ const RoomItem = props => (
/>
);
// eslint-disable-next-line react/prop-types
const Separator = ({ title }) => <StoriesSeparator title={title} theme={_theme} />;
const stories = storiesOf('Room Item', module)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.addDecorator(story => <ScrollView style={{ backgroundColor: themes[_theme].backgroundColor }}>{story()}</ScrollView>);
// eslint-disable-next-line react/prop-types
export default ({ theme }) => {
_theme = theme;
return (
<ScrollView style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
<Separator title='Basic' />
<RoomItem />
<Separator title='User' />
<RoomItem name='diego.mello' avatar='diego.mello' />
<RoomItem
name={longText}
/>
stories.add('Basic', () => (
<RoomItem />
));
<Separator title='Type' />
<RoomItem type='d' />
<RoomItem type='c' />
<RoomItem type='p' />
<RoomItem type='l' />
<RoomItem type='discussion' />
<RoomItem type='d' isGroupChat />
<RoomItem type='&' />
<Separator title='User status' />
<RoomItem status='online' />
<RoomItem status='away' />
<RoomItem status='busy' />
<RoomItem status='offline' />
<RoomItem status='loading' />
<RoomItem status='wrong' />
stories.add('User', () => (
<>
<RoomItem name='diego.mello' avatar='diego.mello' />
<RoomItem
name={longText}
/>
</>
));
<Separator title='Alerts' />
<RoomItem alert />
<RoomItem alert name='unread' unread={1} />
<RoomItem alert name='unread' unread={1000} />
<RoomItem alert name='user mentions' unread={1} userMentions={1} />
<RoomItem alert name='group mentions' unread={1} groupMentions={1} />
<RoomItem alert name='thread unread' tunread={[1]} />
<RoomItem alert name='thread unread user' tunread={[1]} tunreadUser={[1]} />
<RoomItem alert name='thread unread group' tunread={[1]} tunreadGroup={[1]} />
<RoomItem name='user mentions priority 1' alert unread={1} userMentions={1} groupMentions={1} tunread={[1]} />
<RoomItem name='group mentions priority 2' alert unread={1} groupMentions={1} tunread={[1]} />
<RoomItem name='thread unread priority 3' alert unread={1} tunread={[1]} />
stories.add('Type', () => (
<>
<RoomItem type='d' />
<RoomItem type='c' />
<RoomItem type='p' />
<RoomItem type='l' />
<RoomItem type='discussion' />
<RoomItem type='d' isGroupChat />
<RoomItem type='&' />
</>
));
<Separator title='Last Message' />
<RoomItem
showLastMessage
/>
<RoomItem
showLastMessage
lastMessage={{
u: {
username: 'rocket.chat'
},
msg: '2'
}}
/>
<RoomItem
showLastMessage
lastMessage={{
u: {
username: 'diego.mello'
},
msg: '1'
}}
username='diego.mello'
/>
<RoomItem
showLastMessage
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1000}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
tunread={[1]}
lastMessage={lastMessage}
/>
</ScrollView>
);
};
stories.add('User status', () => (
<>
<RoomItem status='online' />
<RoomItem status='away' />
<RoomItem status='busy' />
<RoomItem status='offline' />
<RoomItem status='loading' />
<RoomItem status='wrong' />
</>
));
stories.add('Alerts', () => (
<>
<RoomItem alert />
<RoomItem alert name='unread' unread={1} />
<RoomItem alert name='unread' unread={1000} />
<RoomItem alert name='user mentions' unread={1} userMentions={1} />
<RoomItem alert name='group mentions' unread={1} groupMentions={1} />
<RoomItem alert name='thread unread' tunread={[1]} />
<RoomItem alert name='thread unread user' tunread={[1]} tunreadUser={[1]} />
<RoomItem alert name='thread unread group' tunread={[1]} tunreadGroup={[1]} />
<RoomItem name='user mentions priority 1' alert unread={1} userMentions={1} groupMentions={1} tunread={[1]} />
<RoomItem name='group mentions priority 2' alert unread={1} groupMentions={1} tunread={[1]} />
<RoomItem name='thread unread priority 3' alert unread={1} tunread={[1]} />
</>
));
stories.add('Last Message', () => (
<>
<RoomItem
showLastMessage
/>
<RoomItem
showLastMessage
lastMessage={{
u: {
username: 'rocket.chat'
},
msg: '2'
}}
/>
<RoomItem
showLastMessage
lastMessage={{
u: {
username: 'diego.mello'
},
msg: '1'
}}
username='diego.mello'
/>
<RoomItem
showLastMessage
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1000}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
tunread={[1]}
lastMessage={lastMessage}
/>
</>
));

View File

@ -1,36 +0,0 @@
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../app/constants/colors';
const styles = StyleSheet.create({
separator: {
marginVertical: 30,
marginLeft: 10,
fontSize: 20,
fontWeight: '300'
}
});
const Separator = ({ title, style, theme }) => (
<Text
style={[
styles.separator,
{
color: themes[theme].titleText
},
style
]}
>
{title}
</Text>
);
Separator.propTypes = {
title: PropTypes.string.isRequired,
theme: PropTypes.string,
style: PropTypes.object
};
export default Separator;

View File

@ -1,11 +1,11 @@
/* eslint-disable import/no-extraneous-dependencies */
import React from 'react';
import { ScrollView, StyleSheet, SafeAreaView } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import MessageContext from '../../app/containers/message/Context';
import { UiKitMessage } from '../../app/containers/UIKit';
import StoriesSeparator from './StoriesSeparator';
// eslint-disable-next-line react/prop-types
const Separator = ({ title }) => <StoriesSeparator title={title} theme='light' />;
import { themes } from '../../app/constants/colors';
const styles = StyleSheet.create({
container: {
@ -17,426 +17,435 @@ const styles = StyleSheet.create({
}
});
export default () => (
<SafeAreaView style={styles.container}>
<ScrollView style={[styles.container, styles.padding]} keyboardShouldPersistTaps='always'>
<Separator title='Section' />
{
UiKitMessage([{
type: 'section',
const user = {
id: 'y8bd77ptZswPj3EW8',
username: 'diego.mello',
token: '79q6lH40W4ZRGLOshDiDiVlQaCc4f_lU9HNdHLAzuHz'
};
const baseUrl = 'https://open.rocket.chat';
const messageDecorator = story => (
<MessageContext.Provider
value={{
user,
baseUrl,
onPress: () => {},
onLongPress: () => {},
reactionInit: () => {},
onErrorPress: () => {},
replyBroadcast: () => {},
onReactionPress: () => {},
onDiscussionPress: () => {},
onReactionLongPress: () => {},
threadBadgeColor: themes.light.tunreadColor
}}
>
{story()}
</MessageContext.Provider>
);
const stories = storiesOf('UiKitMessage', module)
.addDecorator(story => <SafeAreaView style={styles.container}>{story()}</SafeAreaView>)
.addDecorator(story => <ScrollView style={[styles.container, styles.padding]} keyboardShouldPersistTaps='always'>{story()}</ScrollView>)
.addDecorator(messageDecorator);
const Section = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section'
}
}]);
stories.add('Section', () => <Section />);
const SectionMarkdownList = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: '*List*:\n1. Item'
}
}]);
stories.add('Section + Markdown List', () => <SectionMarkdownList />);
const SectionOverflow = () => UiKitMessage([
{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + Overflow'
},
accessory: {
type: 'overflow',
options: [
{
text: {
type: 'mrkdwn',
text: 'Section'
}
}])
}
<Separator title='Section + Markdown List' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: '*List*:\n1. Item'
}
}])
}
<Separator title='Section + Overflow' />
{
UiKitMessage([
{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + Overflow'
},
accessory: {
type: 'overflow',
options: [
{
text: {
type: 'plain_text',
text: 'Option 1',
emoji: true
},
value: 'value-0'
},
{
text: {
type: 'plain_text',
text: 'Option 2',
emoji: true
},
value: 'value-1'
},
{
text: {
type: 'plain_text',
text: 'Option 3',
emoji: true
},
value: 'value-2'
},
{
text: {
type: 'plain_text',
text: 'Option 4',
emoji: true
},
value: 'value-3'
}
]
}
}
])
}
<Separator title='Section + image' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + Image'
},
accessory: {
type: 'image',
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'plants'
}
}])
}
<Separator title='Section + button' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + button'
},
accessory: {
type: 'button',
text: {
type: 'plain_text',
text: 'button'
}
}
}])
}
<Separator title='Section + Select' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + select'
},
accessory: {
type: 'static_select',
options: [
{
value: 1,
text: {
type: 'plain_text',
text: 'button'
}
}, {
value: 2,
text: {
type: 'plain_text',
text: 'second button'
}
}]
}
}])
}
<Separator title='Section + DatePicker' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + DatePicker'
},
accessory: {
type: 'datepicker',
initial_date: '1990-04-28',
placeholder: {
type: 'plain_text',
text: 'Select a date',
emoji: true
}
}
}])
}
<Separator title='Section + Multi Select' />
{
UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + select'
},
accessory: {
type: 'multi_static_select',
options: [{
text: {
type: 'plain_text',
text: 'button'
},
value: 1
}, {
text: {
type: 'plain_text',
text: 'opt 1'
},
value: 2
}, {
text: {
type: 'plain_text',
text: 'opt 2'
},
value: 3
}, {
text: {
type: 'plain_text',
text: 'opt 3'
},
value: 4
}]
}
}])
}
<Separator title='Image' />
{
UiKitMessage([{
type: 'image',
title: {
type: 'plain_text',
text: 'Example Image',
text: 'Option 1',
emoji: true
},
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'Example Image'
}])
}
<Separator title='Context' />
{
UiKitMessage([{
type: 'context',
elements: [{
type: 'image',
title: {
type: 'plain_text',
text: 'Example Image',
emoji: true
},
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'Example Image'
value: 'value-0'
},
{
text: {
type: 'plain_text',
text: 'Option 2',
emoji: true
},
{
type: 'mrkdwn',
text: 'context'
}
]
}])
}
value: 'value-1'
},
{
text: {
type: 'plain_text',
text: 'Option 3',
emoji: true
},
value: 'value-2'
},
{
text: {
type: 'plain_text',
text: 'Option 4',
emoji: true
},
value: 'value-3'
}
]
}
}
]);
stories.add('Section + Overflow', () => <SectionOverflow />);
<Separator title='Action - Buttons' />
{
UiKitMessage([{
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Approve'
},
style: 'primary',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
}
]
}])
}
const SectionImage = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + Image'
},
accessory: {
type: 'image',
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'plants'
}
}]);
stories.add('Section + image', () => <SectionImage />);
<Separator title='Fields' />
{
UiKitMessage([
{
type: 'section',
fields: [
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
}
]
}])
}
const SectionButton = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + button'
},
accessory: {
type: 'button',
text: {
type: 'plain_text',
text: 'button'
}
}
}]);
stories.add('Section + button', () => <SectionButton />);
<Separator title='Action - Select' />
const SectionSelect = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + select'
},
accessory: {
type: 'static_select',
options: [
{
UiKitMessage([{
type: 'actions',
elements: [
{
type: 'conversations_select',
placeholder: {
type: 'plain_text',
text: 'Select a conversation',
emoji: true
}
},
{
type: 'channels_select',
placeholder: {
type: 'plain_text',
text: 'Select a channel',
emoji: true
}
},
{
type: 'users_select',
placeholder: {
type: 'plain_text',
text: 'Select a user',
emoji: true
}
},
{
type: 'static_select',
placeholder: {
type: 'plain_text',
text: 'Select an item',
emoji: true
},
options: [
{
text: {
type: 'plain_text',
text: 'Excellent item 1',
emoji: true
},
value: 'value-0'
},
{
text: {
type: 'plain_text',
text: 'Fantastic item 2',
emoji: true
},
value: 'value-1'
},
{
text: {
type: 'plain_text',
text: 'Nifty item 3',
emoji: true
},
value: 'value-2'
},
{
text: {
type: 'plain_text',
text: 'Pretty good item 4',
emoji: true
},
value: 'value-3'
}
]
}
]
}])
value: 1,
text: {
type: 'plain_text',
text: 'button'
}
}, {
value: 2,
text: {
type: 'plain_text',
text: 'second button'
}
}]
}
}]);
stories.add('Section + Select', () => <SectionSelect />);
const SectionDatePicker = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + DatePicker'
},
accessory: {
type: 'datepicker',
initial_date: '1990-04-28',
placeholder: {
type: 'plain_text',
text: 'Select a date',
emoji: true
}
}
}]);
stories.add('Section + DatePicker', () => <SectionDatePicker />);
const SectionMultiSelect = () => UiKitMessage([{
type: 'section',
text: {
type: 'mrkdwn',
text: 'Section + select'
},
accessory: {
type: 'multi_static_select',
options: [{
text: {
type: 'plain_text',
text: 'button'
},
value: 1
}, {
text: {
type: 'plain_text',
text: 'opt 1'
},
value: 2
}, {
text: {
type: 'plain_text',
text: 'opt 2'
},
value: 3
}, {
text: {
type: 'plain_text',
text: 'opt 3'
},
value: 4
}]
}
}]);
stories.add('Section + Multi Select', () => <SectionMultiSelect />);
const Image = () => UiKitMessage([{
type: 'image',
title: {
type: 'plain_text',
text: 'Example Image',
emoji: true
},
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'Example Image'
}]);
stories.add('Image', () => <Image />);
const Context = () => UiKitMessage([{
type: 'context',
elements: [{
type: 'image',
title: {
type: 'plain_text',
text: 'Example Image',
emoji: true
},
imageUrl: 'https://raw.githubusercontent.com/RocketChat/Rocket.Chat.Artwork/master/Logos/icon-circle-256.png',
altText: 'Example Image'
},
{
type: 'mrkdwn',
text: 'context'
}
]
}]);
stories.add('Context', () => <Context />);
const ActionButton = () => UiKitMessage([{
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Approve'
},
style: 'primary',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
},
{
type: 'button',
text: {
type: 'plain_text',
emoji: true,
text: 'Deny'
},
style: 'danger',
value: 'click_me_123'
}
]
}]);
stories.add('Action - Buttons', () => <ActionButton />);
const Fields = () => UiKitMessage([
{
type: 'section',
fields: [
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
},
{
type: 'plain_text',
text: '*this is plain_text text*',
emoji: true
}
</ScrollView>
</SafeAreaView>
);
]
}]);
stories.add('Fields', () => <Fields />);
const ActionSelect = () => UiKitMessage([{
type: 'actions',
elements: [
{
type: 'conversations_select',
placeholder: {
type: 'plain_text',
text: 'Select a conversation',
emoji: true
}
},
{
type: 'channels_select',
placeholder: {
type: 'plain_text',
text: 'Select a channel',
emoji: true
}
},
{
type: 'users_select',
placeholder: {
type: 'plain_text',
text: 'Select a user',
emoji: true
}
},
{
type: 'static_select',
placeholder: {
type: 'plain_text',
text: 'Select an item',
emoji: true
},
options: [
{
text: {
type: 'plain_text',
text: 'Excellent item 1',
emoji: true
},
value: 'value-0'
},
{
text: {
type: 'plain_text',
text: 'Fantastic item 2',
emoji: true
},
value: 'value-1'
},
{
text: {
type: 'plain_text',
text: 'Nifty item 3',
emoji: true
},
value: 'value-2'
},
{
text: {
type: 'plain_text',
text: 'Pretty good item 4',
emoji: true
},
value: 'value-3'
}
]
}
]
}]);
stories.add('Action - Select', () => <ActionSelect />);
// stories.add('Section', () => UiKitMessage([{
// type: 'section',
// text: {
// type: 'mrkdwn',
// text: 'Section'
// }
// }]));

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,22 @@
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import React from 'react';
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import { storiesOf } from '@storybook/react-native';
import RoomItem from './RoomItem';
import './RoomItem';
import './List';
import './ServerItem';
import Message from './Message';
import UiKitMessage from './UiKitMessage';
import UiKitModal from './UiKitModal';
import Markdown from './Markdown';
import './Message';
import './UiKitMessage';
import './UiKitModal';
import './Markdown';
import './HeaderButtons';
import './UnreadBadge';
import '../../app/views/ThreadMessagesView/Item.stories.js';
import './Avatar';
import '../../app/containers/BackgroundContainer/index.stories.js';
import '../../app/containers/RoomHeader/RoomHeader.stories.js';
import Avatar from './Avatar';
// import RoomViewHeader from './RoomViewHeader';
import MessageContext from '../../app/containers/message/Context';
import { themes } from '../../app/constants/colors';
// MessageProvider
const baseUrl = 'https://open.rocket.chat';
const user = {
id: '',
username: 'diego.mello',
token: ''
};
// Change here to see themed storybook
const theme = 'light';
export const theme = 'light';
const reducers = combineReducers({
settings: () => ({}),
@ -52,47 +37,4 @@ const reducers = combineReducers({
meteor: () => ({ connected: true }),
activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } })
});
const store = createStore(reducers);
const messageDecorator = story => (
<MessageContext.Provider
value={{
user,
baseUrl,
onPress: () => {},
onLongPress: () => {},
reactionInit: () => {},
onErrorPress: () => {},
replyBroadcast: () => {},
onReactionPress: () => {},
onDiscussionPress: () => {},
onReactionLongPress: () => {},
threadBadgeColor: themes.light.tunreadColor
}}
>
{story()}
</MessageContext.Provider>
);
storiesOf('RoomItem', module)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.add('list roomitem', () => <RoomItem theme={theme} />);
storiesOf('Message', module)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.addDecorator(messageDecorator)
.add('list message', () => <Message theme={theme} />);
storiesOf('UiKitMessage', module)
.addDecorator(messageDecorator)
.add('list uikitmessage', () => <UiKitMessage theme={theme} />);
storiesOf('UiKitModal', module)
.addDecorator(messageDecorator)
.add('list UiKitModal', () => <UiKitModal theme={theme} />);
storiesOf('Markdown', module)
.add('list Markdown', () => <Markdown theme={theme} />);
storiesOf('Avatar', module)
.add('list Avatar', () => <Avatar theme={theme} />);
// FIXME: I couldn't make these pass on jest :(
// storiesOf('RoomViewHeader', module)
// .add('list', () => <RoomViewHeader theme='black' />);
export const store = createStore(reducers);

View File

@ -4610,12 +4610,12 @@ bunyan-debug-stream@^1.1.0:
exception-formatter "^1.0.4"
bunyan@^1.8.12:
version "1.8.12"
resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797"
integrity sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=
version "1.8.15"
resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46"
integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==
optionalDependencies:
dtrace-provider "~0.8"
moment "^2.10.6"
moment "^2.19.3"
mv "~2"
safe-json-stringify "~1"
@ -5923,10 +5923,10 @@ detect-port@^1.3.0:
address "^1.0.1"
debug "^2.6.0"
detox@^18.8.1:
version "18.8.1"
resolved "https://registry.yarnpkg.com/detox/-/detox-18.8.1.tgz#49893bd8fd139826f78fc5c6773624c467bbda66"
integrity sha512-dxtyM5f/IyzkL5wVsmWyg2an3Ls0rNRSOOJ82pk26alBY7TKGm+PqtK9vnFuS84vN1uxdsBez+4W15ojmAEZZw==
detox@^18.10.0:
version "18.10.0"
resolved "https://registry.yarnpkg.com/detox/-/detox-18.10.0.tgz#8b8d6b6f2bf9775f09d92b63f98dc1b5f4c9334e"
integrity sha512-okqMongBq0hKuJN8hxVHoBjM3Ms0XbaaWq5PyZGWuog3SXTX18ux8YjSmCU2J8ESA8muXyuOpl9KGgT8bWJTHA==
dependencies:
bunyan "^1.8.12"
bunyan-debug-stream "^1.1.0"
@ -5942,13 +5942,14 @@ detox@^18.8.1:
proper-lockfile "^3.0.2"
resolve-from "^5.0.0"
sanitize-filename "^1.6.1"
serialize-error "^8.0.1"
shell-quote "^1.7.2"
signal-exit "^3.0.3"
tail "^2.0.0"
telnet-client "1.2.8"
tempfile "^2.0.0"
which "^1.3.1"
ws "^3.3.1"
ws "^7.4.3"
yargs "^16.0.3"
yargs-unparser "^2.0.0"
@ -7306,9 +7307,9 @@ flat-cache@^2.0.1:
write "1.0.3"
flat@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2"
integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==
version "4.1.1"
resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b"
integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==
dependencies:
is-buffer "~2.0.3"
@ -8324,7 +8325,12 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
ini@^1.3.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
ini@^1.3.5, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
@ -8495,9 +8501,9 @@ is-buffer@^1.1.5:
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-buffer@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
is-callable@^1.1.4, is-callable@^1.1.5:
version "1.1.5"
@ -10325,11 +10331,16 @@ lodash@4.17.20, lodash@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0:
lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.5:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
@ -11156,12 +11167,12 @@ moment@2.27.0:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
moment@2.x.x, moment@^2.10.6:
moment@2.x.x:
version "2.26.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
moment@^2.24.0:
moment@^2.19.3, moment@^2.24.0:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
@ -11212,11 +11223,16 @@ mv@~2:
ncp "~2.0.0"
rimraf "~2.4.0"
nan@^2.12.1, nan@^2.14.0:
nan@^2.12.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
nan@^2.14.0:
version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nanoid@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.10.tgz#69a8a52b77892de0d11cede96bc9762852145bc4"
@ -14057,6 +14073,13 @@ serialize-error@^2.1.0:
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=
serialize-error@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.0.1.tgz#7a67f8ecbbf28973b5a954a2852ff9f4eef52d99"
integrity sha512-r5o60rWFS+8/b49DNAbB+GXZA0SpDpuWE758JxDKgRTga05r3U5lwyksE91dYKDhXSmnu36RALj615E6Aj5pSg==
dependencies:
type-fest "^0.20.2"
serialize-javascript@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
@ -14918,9 +14941,9 @@ table@^5.2.3:
string-width "^3.0.0"
tail@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/tail/-/tail-2.0.3.tgz#37567adc4624a70b35f1d146c3376fa3d6ef7c04"
integrity sha512-s9NOGkLqqiDEtBttQZI7acLS8ycYK5sTlDwNjGnpXG9c8AWj0cfAtwEIzo/hVRMMiC5EYz+bXaJWC1u1u0GPpQ==
version "2.2.1"
resolved "https://registry.yarnpkg.com/tail/-/tail-2.2.1.tgz#3369a786dde3d7b1a5baa3a0accea09348bc5a83"
integrity sha512-pqtI8HB6pbltcaDxkTq12meYxMeLNtZg7+h+c2WlXofaOh4bUeLFQ3eU8S23niqb8We4/UFc+QNlky9nCRnrSQ==
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
@ -15316,6 +15339,11 @@ type-fest@^0.11.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
@ -16128,7 +16156,7 @@ ws@^1.1.0, ws@^1.1.5:
options ">=0.0.5"
ultron "1.0.x"
ws@^3.3.1, ws@^3.3.3:
ws@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
@ -16154,6 +16182,11 @@ ws@^7.0.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
ws@^7.4.3:
version "7.4.4"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59"
integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==
xcode@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.1.0.tgz#bab64a7e954bb50ca8d19da7e09531c65a43ecfe"
@ -16262,9 +16295,9 @@ y18n@^4.0.0:
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
y18n@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
version "5.0.7"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.7.tgz#0c514aba53fc40e2db911aeb8b51566a3374efe7"
integrity sha512-oOhslryvNcA1lB9WYr+M6TMyLkLg81Dgmyb48ZDU0lvR+5bmNDTMz7iobM1QXooaLhbbrcHrlNaABhI6Vo6StQ==
yallist@^2.1.2:
version "2.1.2"