chore: Merge 4.45.0 into master (#5482)

This commit is contained in:
Diego Mello 2024-01-15 18:02:17 -03:00 committed by GitHub
commit 233b858070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 429 additions and 121 deletions

View File

@ -147,7 +147,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer versionCode VERSIONCODE as Integer
versionName "4.44.2" versionName "4.45.0"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
if (!isFoss) { if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
@ -380,6 +380,7 @@ dependencies {
androidTestImplementation('com.wix:detox:+') androidTestImplementation('com.wix:detox:+')
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.facebook.soloader:soloader:0.10.4' implementation 'com.facebook.soloader:soloader:0.10.4'
implementation 'com.facebook.fresco:animated-gif:2.5.0'
} }
if (isNewArchitectureEnabled()) { if (isNewArchitectureEnabled()) {

View File

@ -36,6 +36,7 @@ const IncomingCallHeader = React.memo(
({ uid, callId, avatar, roomName }: { callId: string; avatar: string; uid: string; roomName: string }) => { ({ uid, callId, avatar, roomName }: { callId: string; avatar: string; uid: string; roomName: string }) => {
const [mic, setMic] = useState(true); const [mic, setMic] = useState(true);
const [cam, setCam] = useState(false); const [cam, setCam] = useState(false);
const [audio, setAudio] = useState(true);
const dispatch = useDispatch(); const dispatch = useDispatch();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const styles = useStyle(); const styles = useStyle();
@ -64,12 +65,20 @@ const IncomingCallHeader = React.memo(
direct={true} direct={true}
/> />
<View style={styles.row}> <View style={styles.row}>
<Touchable hitSlop={BUTTON_HIT_SLOP} onPress={hideNotification} style={styles.closeButton}> <Touchable
hitSlop={BUTTON_HIT_SLOP}
onPress={() => {
setAudio(!audio);
hideNotification();
}}
style={styles.closeButton}
>
<CustomIcon name='close' size={20} color={colors.gray300} /> <CustomIcon name='close' size={20} color={colors.gray300} />
</Touchable> </Touchable>
<Touchable <Touchable
hitSlop={BUTTON_HIT_SLOP} hitSlop={BUTTON_HIT_SLOP}
onPress={() => { onPress={() => {
setAudio(!audio);
hideNotification(); hideNotification();
dispatch(cancelCall({ callId })); dispatch(cancelCall({ callId }));
}} }}
@ -80,6 +89,7 @@ const IncomingCallHeader = React.memo(
<Touchable <Touchable
hitSlop={BUTTON_HIT_SLOP} hitSlop={BUTTON_HIT_SLOP}
onPress={() => { onPress={() => {
setAudio(!audio);
hideNotification(); hideNotification();
dispatch(acceptCall({ callId })); dispatch(acceptCall({ callId }));
}} }}
@ -88,7 +98,7 @@ const IncomingCallHeader = React.memo(
<Text style={styles.buttonText}>{i18n.t('accept')}</Text> <Text style={styles.buttonText}>{i18n.t('accept')}</Text>
</Touchable> </Touchable>
</View> </View>
<Ringer ringer={ERingerSounds.RINGTONE} /> {audio ? <Ringer ringer={ERingerSounds.RINGTONE} /> : null}
</View> </View>
); );
} }

View File

@ -1,6 +1,5 @@
import { Audio } from 'expo-av'; import { Audio } from 'expo-av';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { View } from 'react-native';
export enum ERingerSounds { export enum ERingerSounds {
DIALTONE = 'dialtone', DIALTONE = 'dialtone',
@ -8,34 +7,28 @@ export enum ERingerSounds {
} }
const Ringer = React.memo(({ ringer }: { ringer: ERingerSounds }) => { const Ringer = React.memo(({ ringer }: { ringer: ERingerSounds }) => {
const sound = useRef<Audio.Sound | null>(null); const sound = useRef(new Audio.Sound());
useEffect(() => { useEffect(() => {
(async () => { const loadAndPlay = async () => {
let expo = null; try {
switch (ringer) { const soundFile = ringer === ERingerSounds.DIALTONE ? require(`./dialtone.mp3`) : require(`./ringtone.mp3`);
case ERingerSounds.DIALTONE: await sound.current.loadAsync(soundFile);
expo = await Audio.Sound.createAsync(require(`./dialtone.mp3`)); await sound.current.playAsync();
break; await sound.current.setIsLoopingAsync(true);
case ERingerSounds.RINGTONE: } catch (error) {
expo = await Audio.Sound.createAsync(require(`./ringtone.mp3`)); console.error('Error loading sound:', error);
break;
default:
expo = await Audio.Sound.createAsync(require(`./dialtone.mp3`));
break;
} }
sound.current = expo.sound; };
await sound.current.playAsync();
await sound.current.setIsLoopingAsync(true); loadAndPlay();
})();
return () => {
sound.current?.unloadAsync();
};
}, []); }, []);
useEffect(() => () => stopSound(), []); return null;
const stopSound = () => {
sound?.current?.unloadAsync();
};
return <View />;
}); });
export default Ringer; export default Ringer;

View File

@ -20,6 +20,7 @@ import { CommandsEndpoints } from './commands';
import { PushTokenEndpoints } from './pushToken'; import { PushTokenEndpoints } from './pushToken';
import { DirectoryEndpoint } from './directory'; import { DirectoryEndpoint } from './directory';
import { AutoTranslateEndpoints } from './autotranslate'; import { AutoTranslateEndpoints } from './autotranslate';
import { ModerationEndpoints } from './moderation';
export type Endpoints = ChannelsEndpoints & export type Endpoints = ChannelsEndpoints &
ChatEndpoints & ChatEndpoints &
@ -42,4 +43,5 @@ export type Endpoints = ChannelsEndpoints &
CommandsEndpoints & CommandsEndpoints &
PushTokenEndpoints & PushTokenEndpoints &
DirectoryEndpoint & DirectoryEndpoint &
AutoTranslateEndpoints; AutoTranslateEndpoints &
ModerationEndpoints;

View File

@ -0,0 +1,5 @@
export type ModerationEndpoints = {
'moderation.reportUser': {
POST: (params: { userId: string; description: string }) => void;
};
};

View File

@ -44,7 +44,6 @@
"Avatar_Url": "عنوان ويب الصورة الرمزية", "Avatar_Url": "عنوان ويب الصورة الرمزية",
"Away": "غير متواجد", "Away": "غير متواجد",
"Black": "أسود", "Black": "أسود",
"Block_user": "حظر المستخدم",
"Browser": "المتصفح", "Browser": "المتصفح",
"Busy": "مشغول", "Busy": "مشغول",
"Cancel_editing": "إلغاء التعديل", "Cancel_editing": "إلغاء التعديل",
@ -372,7 +371,6 @@
"Two_Factor_Authentication": "المصادقة الثنائية", "Two_Factor_Authentication": "المصادقة الثنائية",
"unarchive": "إلغاء الأرشفة", "unarchive": "إلغاء الأرشفة",
"UNARCHIVE": "إلغاء الأرشفة", "UNARCHIVE": "إلغاء الأرشفة",
"Unblock_user": "إلغاء حظر عن مستخدم",
"Unfollowed_thread": "موضوع غير متابع", "Unfollowed_thread": "موضوع غير متابع",
"Unmute": "إلغاء كتم", "Unmute": "إلغاء كتم",
"unmuted": "إلغاء كتم", "unmuted": "إلغاء كتم",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "অবতার URL", "Avatar_Url": "অবতার URL",
"Away": "দূরে", "Away": "দূরে",
"Black": "কালো", "Black": "কালো",
"Block_user": "ব্যবহারকারী ব্লক করুন",
"Browser": "ব্রাউজার", "Browser": "ব্রাউজার",
"Busy": "ব্যস্ত", "Busy": "ব্যস্ত",
"Cancel_editing": "সম্পাদনা বাতিল করুন", "Cancel_editing": "সম্পাদনা বাতিল করুন",
@ -405,7 +404,6 @@
"Two_Factor_Authentication": "দুটি ধারণামূলক প্রমাণীকরণ", "Two_Factor_Authentication": "দুটি ধারণামূলক প্রমাণীকরণ",
"unarchive": "আনআরকাইভ", "unarchive": "আনআরকাইভ",
"UNARCHIVE": "আনআরকাইভ", "UNARCHIVE": "আনআরকাইভ",
"Unblock_user": "ব্যবহারকারী আনব্লক করুন",
"Unfollowed_thread": "থ্রেড অনফলোয়েড", "Unfollowed_thread": "থ্রেড অনফলোয়েড",
"Unmute": "আনমিউট", "Unmute": "আনমিউট",
"unmuted": "আনমিউটেড", "unmuted": "আনমিউটেড",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatar-URL", "Avatar_Url": "Avatar-URL",
"Away": "Abwesend", "Away": "Abwesend",
"Black": "Schwarz", "Black": "Schwarz",
"Block_user": "Benutzer blockieren",
"Browser": "Browser", "Browser": "Browser",
"Busy": "Beschäftigt", "Busy": "Beschäftigt",
"Cancel_editing": "Bearbeitung abbrechen", "Cancel_editing": "Bearbeitung abbrechen",
@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Zwei-Faktor-Authentifizierung", "Two_Factor_Authentication": "Zwei-Faktor-Authentifizierung",
"unarchive": "wiederherstellen", "unarchive": "wiederherstellen",
"UNARCHIVE": "WIEDERHERSTELLEN", "UNARCHIVE": "WIEDERHERSTELLEN",
"Unblock_user": "Benutzer entsperren",
"Unfollowed_thread": "Thread nicht mehr folgen", "Unfollowed_thread": "Thread nicht mehr folgen",
"Unmute": "Stummschaltung aufheben", "Unmute": "Stummschaltung aufheben",
"unmuted": "Stummschaltung aufgehoben", "unmuted": "Stummschaltung aufgehoben",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL", "Avatar_Url": "Avatar URL",
"Away": "Away", "Away": "Away",
"Black": "Black", "Black": "Black",
"Block_user": "Block user",
"Browser": "Browser", "Browser": "Browser",
"Busy": "Busy", "Busy": "Busy",
"Cancel_editing": "Cancel editing", "Cancel_editing": "Cancel editing",
@ -407,7 +406,6 @@
"Two_Factor_Authentication": "Two-factor authentication", "Two_Factor_Authentication": "Two-factor authentication",
"unarchive": "unarchive", "unarchive": "unarchive",
"UNARCHIVE": "UNARCHIVE", "UNARCHIVE": "UNARCHIVE",
"Unblock_user": "Unblock user",
"Unfollowed_thread": "Unfollowed thread", "Unfollowed_thread": "Unfollowed thread",
"Unmute": "Unmute", "Unmute": "Unmute",
"unmuted": "unmuted", "unmuted": "unmuted",
@ -762,6 +760,11 @@
"Enable_writing_in_room": "Enable writing in room", "Enable_writing_in_room": "Enable writing in room",
"Disable_writing_in_room": "Disable writing in room", "Disable_writing_in_room": "Disable writing in room",
"Pinned_a_message": "Pinned a message:", "Pinned_a_message": "Pinned a message:",
"Unblock": "Unblock",
"Block": "Block",
"Report_user": "Report user",
"Report_sent_successfully": "Report sent successfully",
"Why_do_you_want_to_report": "Why do you want to report?",
"You_dont_have_permission_to_perform_this_action": "You dont have permission to perform this action. Check with a workspace administrator.", "You_dont_have_permission_to_perform_this_action": "You dont have permission to perform this action. Check with a workspace administrator.",
"Jump_to_message": "Jump to message", "Jump_to_message": "Jump to message",
"Missed_call": "Missed call" "Missed_call": "Missed call"

View File

@ -38,7 +38,6 @@
"Avatar_Url": "URL del Avatar", "Avatar_Url": "URL del Avatar",
"Away": "Ausente", "Away": "Ausente",
"Black": "Negro", "Black": "Negro",
"Block_user": "Bloquear usuario",
"Busy": "Ocupado", "Busy": "Ocupado",
"Cancel_editing": "Cancelar edición", "Cancel_editing": "Cancelar edición",
"Cancel_recording": "Cancelar grabación", "Cancel_recording": "Cancelar grabación",
@ -239,7 +238,6 @@
"Two_Factor_Authentication": "Autenticación de doble factor", "Two_Factor_Authentication": "Autenticación de doble factor",
"unarchive": "desarchivar", "unarchive": "desarchivar",
"UNARCHIVE": "DESARCHIVAR", "UNARCHIVE": "DESARCHIVAR",
"Unblock_user": "Desbloquear usuario",
"Unfollowed_thread": "Dejar de seguir el hilo", "Unfollowed_thread": "Dejar de seguir el hilo",
"Unmute": "Desmutear", "Unmute": "Desmutear",
"unmuted": "Desmuteado", "unmuted": "Desmuteado",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatarin URL-osoite", "Avatar_Url": "Avatarin URL-osoite",
"Away": "Poissa", "Away": "Poissa",
"Black": "Musta", "Black": "Musta",
"Block_user": "Estä käyttäjä",
"Browser": "Selain", "Browser": "Selain",
"Busy": "Varattu", "Busy": "Varattu",
"Cancel_editing": "Peruuta muokkaus", "Cancel_editing": "Peruuta muokkaus",
@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Kaksivaiheinen tunnistautuminen", "Two_Factor_Authentication": "Kaksivaiheinen tunnistautuminen",
"unarchive": "palauta arkistosta", "unarchive": "palauta arkistosta",
"UNARCHIVE": "PALAUTA ARKISTOSTA", "UNARCHIVE": "PALAUTA ARKISTOSTA",
"Unblock_user": "Poista käyttäjän esto",
"Unfollowed_thread": "Lopetettiin ketjun seuraaminen", "Unfollowed_thread": "Lopetettiin ketjun seuraaminen",
"Unmute": "Mykistys poistettu", "Unmute": "Mykistys poistettu",
"unmuted": "poisti mykistyksen", "unmuted": "poisti mykistyksen",

View File

@ -45,7 +45,6 @@
"Avatar_Url": "URL de l'avatar", "Avatar_Url": "URL de l'avatar",
"Away": "Absent", "Away": "Absent",
"Black": "Noir", "Black": "Noir",
"Block_user": "Bloquer l'utilisateur",
"Browser": "Navigateur", "Browser": "Navigateur",
"Busy": "Occupé", "Busy": "Occupé",
"Cancel_editing": "Annuler la modification", "Cancel_editing": "Annuler la modification",
@ -381,7 +380,6 @@
"Two_Factor_Authentication": "Authentification à deux facteurs", "Two_Factor_Authentication": "Authentification à deux facteurs",
"unarchive": "désarchiver", "unarchive": "désarchiver",
"UNARCHIVE": "DÉSARCHIVER", "UNARCHIVE": "DÉSARCHIVER",
"Unblock_user": "Débloquer l'utilisateur",
"Unfollowed_thread": "Ne plus suivre ce fil", "Unfollowed_thread": "Ne plus suivre ce fil",
"Unmute": "Rendre la parole", "Unmute": "Rendre la parole",
"unmuted": "rendu la parole", "unmuted": "rendu la parole",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "अवतार URL", "Avatar_Url": "अवतार URL",
"Away": "दूर", "Away": "दूर",
"Black": "काला", "Black": "काला",
"Block_user": "उपयोगकर्ता को ब्लॉक करें",
"Browser": "ब्राउज़र", "Browser": "ब्राउज़र",
"Busy": "व्यस्त", "Busy": "व्यस्त",
"Cancel_editing": "संपादन रद्द करें", "Cancel_editing": "संपादन रद्द करें",
@ -405,7 +404,6 @@
"Two_Factor_Authentication": "दो-क्रमिक प्रमाणीकरण", "Two_Factor_Authentication": "दो-क्रमिक प्रमाणीकरण",
"unarchive": "अनारकाइव", "unarchive": "अनारकाइव",
"UNARCHIVE": "अनारकाइव करें", "UNARCHIVE": "अनारकाइव करें",
"Unblock_user": "उपयोगकर्ता को अनब्लॉक करें",
"Unfollowed_thread": "थ्रेड का अनुसरण नहीं किया गया", "Unfollowed_thread": "थ्रेड का अनुसरण नहीं किया गया",
"Unmute": "आवाज़ हटाएं", "Unmute": "आवाज़ हटाएं",
"unmuted": "आवाज़ हटाई गई", "unmuted": "आवाज़ हटाई गई",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL", "Avatar_Url": "Avatar URL",
"Away": "Távol", "Away": "Távol",
"Black": "Fekete", "Black": "Fekete",
"Block_user": "Felhasználó tiltása",
"Browser": "Böngésző", "Browser": "Böngésző",
"Busy": "Elfoglalt", "Busy": "Elfoglalt",
"Cancel_editing": "Szerkesztés megszakítás", "Cancel_editing": "Szerkesztés megszakítás",
@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Kétfaktoros hitelesítés", "Two_Factor_Authentication": "Kétfaktoros hitelesítés",
"unarchive": "Archiválás megszüntetés", "unarchive": "Archiválás megszüntetés",
"UNARCHIVE": "ARCHIVÁLÁS MEGSZÜNTETÉS", "UNARCHIVE": "ARCHIVÁLÁS MEGSZÜNTETÉS",
"Unblock_user": "Felhasználó tiltás feloldása",
"Unfollowed_thread": "Nem követett szál", "Unfollowed_thread": "Nem követett szál",
"Unmute": "Némítás megszüntetése", "Unmute": "Némítás megszüntetése",
"unmuted": "Némítás megszüntetve", "unmuted": "Némítás megszüntetve",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "URL avatar", "Avatar_Url": "URL avatar",
"Away": "Assente", "Away": "Assente",
"Black": "Nero", "Black": "Nero",
"Block_user": "Blocca utente",
"Browser": "Browser", "Browser": "Browser",
"Busy": "Occupato", "Busy": "Occupato",
"Cancel_editing": "Annulla modifica", "Cancel_editing": "Annulla modifica",
@ -386,7 +385,6 @@
"Two_Factor_Authentication": "Autenticazione a due fattori", "Two_Factor_Authentication": "Autenticazione a due fattori",
"unarchive": "rimuovi dall'archivio", "unarchive": "rimuovi dall'archivio",
"UNARCHIVE": "RIMUOVI DALL'ARCHIVIO", "UNARCHIVE": "RIMUOVI DALL'ARCHIVIO",
"Unblock_user": "Sblocca utente",
"Unfollowed_thread": "Non segui più il thread", "Unfollowed_thread": "Non segui più il thread",
"Unmute": "Attiva notifiche", "Unmute": "Attiva notifiche",
"unmuted": "notifiche attivate", "unmuted": "notifiche attivate",

View File

@ -45,7 +45,6 @@
"Avatar_Url": "アバターURL", "Avatar_Url": "アバターURL",
"Away": "退出中", "Away": "退出中",
"Black": "ブラック", "Black": "ブラック",
"Block_user": "ブロックしたユーザー",
"Browser": "ブラウザ", "Browser": "ブラウザ",
"Busy": "取り込み中", "Busy": "取り込み中",
"Cancel_editing": "編集をキャンセル", "Cancel_editing": "編集をキャンセル",
@ -334,7 +333,6 @@
"Two_Factor_Authentication": "2段階認証", "Two_Factor_Authentication": "2段階認証",
"unarchive": "アーカイブ解除", "unarchive": "アーカイブ解除",
"UNARCHIVE": "アーカイブ解除", "UNARCHIVE": "アーカイブ解除",
"Unblock_user": "ブロックを解除",
"Unfollowed_thread": "スレッド更新時に通知しない", "Unfollowed_thread": "スレッド更新時に通知しない",
"Unmute": "ミュート解除", "Unmute": "ミュート解除",
"unmuted": "ミュート解除しました", "unmuted": "ミュート解除しました",

View File

@ -45,7 +45,6 @@
"Avatar_Url": "Avatar-URL", "Avatar_Url": "Avatar-URL",
"Away": "Afwezig", "Away": "Afwezig",
"Black": "Zwart", "Black": "Zwart",
"Block_user": "Blokkeer gebruiker",
"Browser": "Browser", "Browser": "Browser",
"Busy": "Bezig", "Busy": "Bezig",
"Cancel_editing": "Bewerken annuleren", "Cancel_editing": "Bewerken annuleren",
@ -381,7 +380,6 @@
"Two_Factor_Authentication": "Twee-factor authenticatie", "Two_Factor_Authentication": "Twee-factor authenticatie",
"unarchive": "dearchiveren", "unarchive": "dearchiveren",
"UNARCHIVE": "DEARCHIVEREN", "UNARCHIVE": "DEARCHIVEREN",
"Unblock_user": "Deblokkeer gebruiker",
"Unfollowed_thread": "Draad ontvolgd", "Unfollowed_thread": "Draad ontvolgd",
"Unmute": "Dempen opheffen", "Unmute": "Dempen opheffen",
"unmuted": "ongedempt", "unmuted": "ongedempt",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL", "Avatar_Url": "Avatar URL",
"Away": "Ausente", "Away": "Ausente",
"Black": "Preto", "Black": "Preto",
"Block_user": "Bloquear usuário",
"Browser": "Navegador", "Browser": "Navegador",
"Busy": "Ocupado", "Busy": "Ocupado",
"Cancel_editing": "Cancelar edição", "Cancel_editing": "Cancelar edição",
@ -407,7 +406,6 @@
"Two_Factor_Authentication": "Autenticação de dois fatores", "Two_Factor_Authentication": "Autenticação de dois fatores",
"unarchive": "desarquivar", "unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR", "UNARCHIVE": "DESARQUIVAR",
"Unblock_user": "Desbloquear usuário",
"Unfollowed_thread": "Parou de seguir tópico", "Unfollowed_thread": "Parou de seguir tópico",
"Unmute": "Permitir que o usuário fale", "Unmute": "Permitir que o usuário fale",
"unmuted": "permitiu que o usuário fale", "unmuted": "permitiu que o usuário fale",
@ -761,6 +759,11 @@
"The_user_will_be_able_to_type_in_roomName": "O usuário poderá digitar em {{roomName}}", "The_user_will_be_able_to_type_in_roomName": "O usuário poderá digitar em {{roomName}}",
"Enable_writing_in_room": "Permitir escrita na sala", "Enable_writing_in_room": "Permitir escrita na sala",
"Disable_writing_in_room": "Desabilitar escrita na sala", "Disable_writing_in_room": "Desabilitar escrita na sala",
"Unblock": "Desbloquear",
"Block": "Bloquear",
"Report_user": "Reportar usuário",
"Report_sent_successfully": "Reporte enviado com sucesso",
"Why_do_you_want_to_report": "Por que você deseja reportar?",
"Pinned_a_message": "Fixou uma mensagem:", "Pinned_a_message": "Fixou uma mensagem:",
"You_dont_have_permission_to_perform_this_action": "Você não tem permissão para realizar esta ação. Verifique com um administrador do espaço de trabalho.", "You_dont_have_permission_to_perform_this_action": "Você não tem permissão para realizar esta ação. Verifique com um administrador do espaço de trabalho.",
"Jump_to_message": "Ir para mensagem", "Jump_to_message": "Ir para mensagem",

View File

@ -44,7 +44,6 @@
"Avatar_Url": "URL do Avatar", "Avatar_Url": "URL do Avatar",
"Away": "Ausente", "Away": "Ausente",
"Black": "Preto", "Black": "Preto",
"Block_user": "Bloquear utilizador",
"Browser": "Navegador", "Browser": "Navegador",
"Busy": "Ocupado", "Busy": "Ocupado",
"Cancel_editing": "Cancelar edição", "Cancel_editing": "Cancelar edição",
@ -323,7 +322,6 @@
"Two_Factor_Authentication": "Autenticação 2FA", "Two_Factor_Authentication": "Autenticação 2FA",
"unarchive": "desarquivar", "unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR", "UNARCHIVE": "DESARQUIVAR",
"Unblock_user": "Desbloquear utilizador",
"Unmute": "Retirar silêncio", "Unmute": "Retirar silêncio",
"unmuted": "silêncio removido", "unmuted": "silêncio removido",
"Unpin": "Desafixar", "Unpin": "Desafixar",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "URL аватара", "Avatar_Url": "URL аватара",
"Away": "Отошел", "Away": "Отошел",
"Black": "Черный", "Black": "Черный",
"Block_user": "Блокировать пользователя",
"Browser": "Браузер", "Browser": "Браузер",
"Busy": "Занят", "Busy": "Занят",
"Cancel_editing": "Отменить правку", "Cancel_editing": "Отменить правку",
@ -392,7 +391,6 @@
"Two_Factor_Authentication": "Двухфакторная аутентификация", "Two_Factor_Authentication": "Двухфакторная аутентификация",
"unarchive": "разархивировать", "unarchive": "разархивировать",
"UNARCHIVE": "РАЗАРХИВИРОВАТЬ", "UNARCHIVE": "РАЗАРХИВИРОВАТЬ",
"Unblock_user": "Разблокировать пользователя",
"Unfollowed_thread": "Не следить", "Unfollowed_thread": "Не следить",
"Unmute": "Отменить заглушивание", "Unmute": "Отменить заглушивание",
"unmuted": "Заглушивание отменено", "unmuted": "Заглушивание отменено",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL", "Avatar_Url": "Avatar URL",
"Away": "Odsoten", "Away": "Odsoten",
"Black": "Črn", "Black": "Črn",
"Block_user": "Blokiraj uporabnika",
"Browser": "Brskalnik", "Browser": "Brskalnik",
"Busy": "Zaseden", "Busy": "Zaseden",
"Cancel_editing": "Prekliči urejanje", "Cancel_editing": "Prekliči urejanje",
@ -389,7 +388,6 @@
"Two_Factor_Authentication": "Dvofaktorska overjanje", "Two_Factor_Authentication": "Dvofaktorska overjanje",
"unarchive": "Odpakirati", "unarchive": "Odpakirati",
"UNARCHIVE": "ODPAKIRATI", "UNARCHIVE": "ODPAKIRATI",
"Unblock_user": "Odblokirati uporabnika",
"Unfollowed_thread": "Nit, ki ji nihče ne sledi", "Unfollowed_thread": "Nit, ki ji nihče ne sledi",
"Unmute": "Preklicati utišanje", "Unmute": "Preklicati utišanje",
"unmuted": "preklicati utišanje", "unmuted": "preklicati utišanje",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "URL till avatar", "Avatar_Url": "URL till avatar",
"Away": "Borta", "Away": "Borta",
"Black": "Svart", "Black": "Svart",
"Block_user": "Blockera användare",
"Browser": "Webbläsare", "Browser": "Webbläsare",
"Busy": "Upptagen", "Busy": "Upptagen",
"Cancel_editing": "Avbryt redigering", "Cancel_editing": "Avbryt redigering",
@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Tvåfaktorsautentisering", "Two_Factor_Authentication": "Tvåfaktorsautentisering",
"unarchive": "avarkivera", "unarchive": "avarkivera",
"UNARCHIVE": "AVARKIVERA", "UNARCHIVE": "AVARKIVERA",
"Unblock_user": "Avblockera användare",
"Unfollowed_thread": "Sluta följa tråd", "Unfollowed_thread": "Sluta följa tråd",
"Unmute": "Slå på ljudet", "Unmute": "Slå på ljudet",
"unmuted": "slog på ljudet", "unmuted": "slog på ljudet",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "அவதார் URL", "Avatar_Url": "அவதார் URL",
"Away": "விலகியிருக்கிறேன்", "Away": "விலகியிருக்கிறேன்",
"Black": "கருப்பு", "Black": "கருப்பு",
"Block_user": "பயனாளரை தடுக்கவும்",
"Browser": "உலாவி", "Browser": "உலாவி",
"Busy": "தொலைநேரம்", "Busy": "தொலைநேரம்",
"Cancel_editing": "திருத்தலை ரத்து செய்க", "Cancel_editing": "திருத்தலை ரத்து செய்க",
@ -405,7 +404,6 @@
"Two_Factor_Authentication": "இரண்டு பரிமாறு அங்கீகாரம்", "Two_Factor_Authentication": "இரண்டு பரிமாறு அங்கீகாரம்",
"unarchive": "அசைவு செய்", "unarchive": "அசைவு செய்",
"UNARCHIVE": "அசைவு செய்", "UNARCHIVE": "அசைவு செய்",
"Unblock_user": "பயனரை விடுவிக்கு",
"Unfollowed_thread": "பின்னாளிக்குப் பின்னாளிக்கப்பட்டுள்ளது", "Unfollowed_thread": "பின்னாளிக்குப் பின்னாளிக்கப்பட்டுள்ளது",
"Unmute": "மூடாது", "Unmute": "மூடாது",
"unmuted": "மூடப்படவில்லை", "unmuted": "மூடப்படவில்லை",

View File

@ -51,7 +51,6 @@
"Avatar_Url": "అవతార్ URL", "Avatar_Url": "అవతార్ URL",
"Away": "దూరంగా", "Away": "దూరంగా",
"Black": "నలుపు", "Black": "నలుపు",
"Block_user": "వాడిని నిలిపించండి",
"Browser": "బ్రౌజర్", "Browser": "బ్రౌజర్",
"Busy": "వ్యస్తంగా", "Busy": "వ్యస్తంగా",
"Cancel_editing": "సవరించడానికి రద్దు చేయి", "Cancel_editing": "సవరించడానికి రద్దు చేయి",
@ -405,7 +404,6 @@
"Two_Factor_Authentication": "రెండు అంశ ప్రామాణీకరణ", "Two_Factor_Authentication": "రెండు అంశ ప్రామాణీకరణ",
"unarchive": "అనార్కైవ్", "unarchive": "అనార్కైవ్",
"UNARCHIVE": "అనార్కైవ్", "UNARCHIVE": "అనార్కైవ్",
"Unblock_user": "వాడుకరిని నిషేధించకోండి",
"Unfollowed_thread": "అన్‌ఫాలో థ్రెడ్", "Unfollowed_thread": "అన్‌ఫాలో థ్రెడ్",
"Unmute": "ఆధ్వర్యపరచించండి", "Unmute": "ఆధ్వర్యపరచించండి",
"unmuted": "ఆధ్వర్యపరచించబడింది", "unmuted": "ఆధ్వర్యపరచించబడింది",

View File

@ -43,7 +43,6 @@
"Avatar_Url": "Profil fotoğrafı URL'si", "Avatar_Url": "Profil fotoğrafı URL'si",
"Away": "Uzakta", "Away": "Uzakta",
"Black": "Koyu", "Black": "Koyu",
"Block_user": "Kullanıcıyı engelle",
"Browser": "Tarayıcı", "Browser": "Tarayıcı",
"Busy": "Meşgul", "Busy": "Meşgul",
"Cancel_editing": "Düzenlemeyi iptal et", "Cancel_editing": "Düzenlemeyi iptal et",
@ -369,7 +368,6 @@
"Two_Factor_Authentication": "İki faktörlü Kimlik Doğrulama", "Two_Factor_Authentication": "İki faktörlü Kimlik Doğrulama",
"unarchive": "arşivden çıkar", "unarchive": "arşivden çıkar",
"UNARCHIVE": "ARŞİVDEN ÇIKAR", "UNARCHIVE": "ARŞİVDEN ÇIKAR",
"Unblock_user": "Kullanıcının engelini kaldır",
"Unfollowed_thread": "Takip edilmeyen başlık", "Unfollowed_thread": "Takip edilmeyen başlık",
"Unmute": "Sesi Aç", "Unmute": "Sesi Aç",
"unmuted": "Sesi Açıldı", "unmuted": "Sesi Açıldı",

View File

@ -43,7 +43,6 @@
"Avatar_Url": "头像地址", "Avatar_Url": "头像地址",
"Away": "离开", "Away": "离开",
"Black": "黑色", "Black": "黑色",
"Block_user": "屏蔽此用户",
"Browser": "浏览器", "Browser": "浏览器",
"Busy": "忙碌", "Busy": "忙碌",
"Cancel_editing": "取消编辑", "Cancel_editing": "取消编辑",
@ -365,7 +364,6 @@
"Two_Factor_Authentication": "双重认证", "Two_Factor_Authentication": "双重认证",
"unarchive": "取消封存", "unarchive": "取消封存",
"UNARCHIVE": "取消封存", "UNARCHIVE": "取消封存",
"Unblock_user": "解除屏蔽",
"Unfollowed_thread": "取消追踪讨论", "Unfollowed_thread": "取消追踪讨论",
"Unmute": "取消静音", "Unmute": "取消静音",
"unmuted": "静音状态", "unmuted": "静音状态",

View File

@ -44,7 +44,6 @@
"Avatar_Url": "大頭貼地址", "Avatar_Url": "大頭貼地址",
"Away": "離開", "Away": "離開",
"Black": "黑色", "Black": "黑色",
"Block_user": "封鎖此用戶",
"Browser": "瀏覽器", "Browser": "瀏覽器",
"Busy": "忙碌", "Busy": "忙碌",
"Cancel_editing": "取消編輯", "Cancel_editing": "取消編輯",
@ -371,7 +370,6 @@
"Two_Factor_Authentication": "雙重認證", "Two_Factor_Authentication": "雙重認證",
"unarchive": "取消封存", "unarchive": "取消封存",
"UNARCHIVE": "取消封存", "UNARCHIVE": "取消封存",
"Unblock_user": "解除封鎖",
"Unfollowed_thread": "取消追蹤討論", "Unfollowed_thread": "取消追蹤討論",
"Unmute": "取消靜音", "Unmute": "取消靜音",
"unmuted": "靜音狀態", "unmuted": "靜音狀態",

View File

@ -55,4 +55,15 @@ describe('Test the getFilename', () => {
const filename = getFilename({ type: 'image', mimeType: image_type, title, url: image_url }); const filename = getFilename({ type: 'image', mimeType: image_type, title, url: image_url });
expect(filename).toBe('help-image-url.png'); expect(filename).toBe('help-image-url.png');
}); });
it('returns the filename with the gif extension from a gif sent by tenor/giphy', () => {
const { image_type, image_url, title } = {
title: undefined,
image_url: 'https://media4.giphy.com/media/bGtO3RlAPHkeQ/giphy.gif',
image_type: undefined
};
const filename = getFilename({ type: 'image', mimeType: image_type, title, url: image_url });
expect(filename).toBe('giphy.gif');
});
}); });

View File

@ -68,6 +68,10 @@ export const getFilename = ({
}; };
const getExtension = (type: MediaTypes, mimeType?: string, url?: string) => { const getExtension = (type: MediaTypes, mimeType?: string, url?: string) => {
// support url with gif extension and mimetype undefined, ex.: using the app tenor and giphy.
if (url?.split('.').pop() === 'gif') {
return 'gif';
}
if (!mimeType) { if (!mimeType) {
return defaultType[type]; return defaultType[type];
} }

View File

@ -6,6 +6,7 @@ import { themes } from '../../constants';
import { TSupportedThemes } from '../../../theme'; import { TSupportedThemes } from '../../../theme';
import UserPreferences from '../userPreferences'; import UserPreferences from '../userPreferences';
import ensureSecureProtocol from './ensureSecureProtocol'; import ensureSecureProtocol from './ensureSecureProtocol';
import log from './log';
export const DEFAULT_BROWSER_KEY = 'DEFAULT_BROWSER_KEY'; export const DEFAULT_BROWSER_KEY = 'DEFAULT_BROWSER_KEY';
@ -38,6 +39,16 @@ const appSchemeURL = (url: string, browser: string): string => {
}; };
const openLink = async (url: string, theme: TSupportedThemes = 'light'): Promise<void> => { const openLink = async (url: string, theme: TSupportedThemes = 'light'): Promise<void> => {
const telRegExp = new RegExp(/^(tel:)/);
if (telRegExp.test(url)) {
try {
await Linking.openURL(url);
return;
} catch (e) {
log(e);
}
}
url = ensureSecureProtocol(url); url = ensureSecureProtocol(url);
try { try {
const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY); const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY);

View File

@ -296,6 +296,10 @@ export const togglePinMessage = (messageId: string, pinned?: boolean) => {
return sdk.post('chat.pinMessage', { messageId }); return sdk.post('chat.pinMessage', { messageId });
}; };
export const reportUser = (userId: string, description: string) =>
// RC 6.4.0
sdk.post('moderation.reportUser', { userId, description });
export const reportMessage = (messageId: string) => export const reportMessage = (messageId: string) =>
// RC 0.64.0 // RC 0.64.0
sdk.post('chat.reportMessage', { messageId, description: 'Message reported by user' }); sdk.post('chat.reportMessage', { messageId, description: 'Message reported by user' });

View File

@ -11,6 +11,7 @@ import RoomView from '../views/RoomView';
import RoomsListView from '../views/RoomsListView'; import RoomsListView from '../views/RoomsListView';
import RoomActionsView from '../views/RoomActionsView'; import RoomActionsView from '../views/RoomActionsView';
import RoomInfoView from '../views/RoomInfoView'; import RoomInfoView from '../views/RoomInfoView';
import ReportUserView from '../views/ReportUserView';
import RoomInfoEditView from '../views/RoomInfoEditView'; import RoomInfoEditView from '../views/RoomInfoEditView';
import RoomMembersView from '../views/RoomMembersView'; import RoomMembersView from '../views/RoomMembersView';
import SearchMessagesView from '../views/SearchMessagesView'; import SearchMessagesView from '../views/SearchMessagesView';
@ -99,6 +100,7 @@ const ChatsStackNavigator = () => {
<ChatsStack.Screen name='RoomActionsView' component={RoomActionsView} options={RoomActionsView.navigationOptions} /> <ChatsStack.Screen name='RoomActionsView' component={RoomActionsView} options={RoomActionsView.navigationOptions} />
<ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} /> <ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} />
<ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} /> <ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} />
<ChatsStack.Screen name='ReportUserView' component={ReportUserView} />
{/* @ts-ignore */} {/* @ts-ignore */}
<ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} /> <ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
<ChatsStack.Screen name='ChangeAvatarView' component={ChangeAvatarView} /> <ChatsStack.Screen name='ChangeAvatarView' component={ChangeAvatarView} />

View File

@ -15,6 +15,7 @@ import RoomView from '../../views/RoomView';
import RoomsListView from '../../views/RoomsListView'; import RoomsListView from '../../views/RoomsListView';
import RoomActionsView from '../../views/RoomActionsView'; import RoomActionsView from '../../views/RoomActionsView';
import RoomInfoView from '../../views/RoomInfoView'; import RoomInfoView from '../../views/RoomInfoView';
import ReportUserView from '../../views/ReportUserView';
import RoomInfoEditView from '../../views/RoomInfoEditView'; import RoomInfoEditView from '../../views/RoomInfoEditView';
import ChangeAvatarView from '../../views/ChangeAvatarView'; import ChangeAvatarView from '../../views/ChangeAvatarView';
import RoomMembersView from '../../views/RoomMembersView'; import RoomMembersView from '../../views/RoomMembersView';
@ -118,6 +119,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
<ModalStack.Screen name='RoomActionsView' component={RoomActionsView} /> <ModalStack.Screen name='RoomActionsView' component={RoomActionsView} />
{/* @ts-ignore */} {/* @ts-ignore */}
<ModalStack.Screen name='RoomInfoView' component={RoomInfoView} /> <ModalStack.Screen name='RoomInfoView' component={RoomInfoView} />
<ModalStack.Screen name='ReportUserView' component={ReportUserView} />
{/* @ts-ignore */} {/* @ts-ignore */}
<ModalStack.Screen name='SelectListView' component={SelectListView} /> <ModalStack.Screen name='SelectListView' component={SelectListView} />
<ModalStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} /> <ModalStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />

View File

@ -199,6 +199,11 @@ export type ModalStackParamList = {
SupportedVersionsWarning: { SupportedVersionsWarning: {
showCloseButton?: boolean; showCloseButton?: boolean;
}; };
ReportUserView: {
username: string;
userId: string;
name: string;
};
}; };
export type MasterDetailInsideStackParamList = { export type MasterDetailInsideStackParamList = {

View File

@ -172,6 +172,11 @@ export type ChatsStackParamList = {
room?: ISubscription; room?: ISubscription;
t?: SubscriptionType; t?: SubscriptionType;
}; };
ReportUserView: {
username: string;
userId: string;
name: string;
};
}; };
export type ProfileStackParamList = { export type ProfileStackParamList = {

View File

@ -139,6 +139,7 @@ class DirectoryView extends React.Component<IDirectoryViewProps, IDirectoryViewS
} else if (type === 'teams') { } else if (type === 'teams') {
logEvent(events.DIRECTORY_SEARCH_TEAMS); logEvent(events.DIRECTORY_SEARCH_TEAMS);
} }
this.toggleDropdown()
}; };
toggleWorkspace = () => { toggleWorkspace = () => {

View File

@ -0,0 +1,21 @@
import React from 'react';
import { Text, View } from 'react-native';
import Avatar from '../../containers/Avatar';
import styles from './styles';
import { useTheme } from '../../theme';
const UserInfo = ({ username, name }: { username: string; name: string }) => {
const { colors } = useTheme();
return (
<View style={styles.containerAvatarAndName}>
<Avatar text={username} size={32} />
<Text style={[styles.nameText, { color: colors.fontDefault }]} numberOfLines={1}>
{name || username}
</Text>
</View>
);
};
export default UserInfo;

View File

@ -0,0 +1,113 @@
import React, { useLayoutEffect, useState } from 'react';
import { ScrollView, StatusBar } from 'react-native';
import { CompositeNavigationProp, RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import log from '../../lib/methods/helpers/log';
import SafeAreaView from '../../containers/SafeAreaView';
import { useTheme } from '../../theme';
import { ChatsStackParamList } from '../../stacks/types';
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
import I18n from '../../i18n';
import UserInfo from './UserInfo';
import styles from './styles';
import { ControlledFormTextInput } from '../../containers/TextInput';
import Button from '../../containers/Button';
import { useAppSelector } from '../../lib/hooks';
import EventEmitter from '../../lib/methods/helpers/events';
import { LISTENER } from '../../containers/Toast';
import { Services } from '../../lib/services';
import KeyboardView from '../../containers/KeyboardView';
type TReportUserViewNavigationProp = CompositeNavigationProp<
StackNavigationProp<ChatsStackParamList, 'ReportUserView'>,
StackNavigationProp<MasterDetailInsideStackParamList>
>;
type TReportUserViewRouteProp = RouteProp<ChatsStackParamList, 'ReportUserView'>;
interface ISubmit {
description: string;
}
const schema = yup.object().shape({
description: yup.string().trim().required()
});
const ReportUserView = () => {
const [loading, setLoading] = useState(false);
const { colors } = useTheme();
const navigation = useNavigation<TReportUserViewNavigationProp>();
const { isMasterDetail } = useAppSelector(state => ({ isMasterDetail: state.app.isMasterDetail }));
const {
params: { username, userId, name }
} = useRoute<TReportUserViewRouteProp>();
const {
control,
handleSubmit,
formState: { isValid }
} = useForm<ISubmit>({ mode: 'onChange', resolver: yupResolver(schema), defaultValues: { description: '' } });
useLayoutEffect(() => {
navigation?.setOptions({
title: I18n.t('Report_user')
});
}, [navigation]);
const submit = async ({ description }: ISubmit) => {
try {
setLoading(true);
await Services.reportUser(userId, description);
EventEmitter.emit(LISTENER, { message: I18n.t('Report_sent_successfully') });
setLoading(false);
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
return;
}
navigation.navigate('RoomView');
} catch (e) {
log(e);
setLoading(false);
}
};
return (
<KeyboardView
style={{ backgroundColor: colors.auxiliaryBackground }}
contentContainerStyle={styles.container}
keyboardVerticalOffset={128}
>
<SafeAreaView style={[styles.containerView, { backgroundColor: colors.auxiliaryBackground }]} testID='report-user-view'>
<ScrollView contentContainerStyle={[styles.scroll, { backgroundColor: colors.auxiliaryBackground }]}>
<StatusBar />
<UserInfo username={username} name={name} />
<ControlledFormTextInput
name='description'
control={control}
label={I18n.t('Why_do_you_want_to_report')}
multiline
inputStyle={styles.textInput}
testID='report-user-view-input'
containerStyle={styles.containerTextInput}
/>
<Button
title={I18n.t('Report')}
type='primary'
backgroundColor={colors.dangerColor}
disabled={!isValid}
onPress={handleSubmit(submit)}
testID='report-user-view-submit'
loading={loading}
/>
</ScrollView>
</SafeAreaView>
</KeyboardView>
);
};
export default ReportUserView;

View File

@ -0,0 +1,37 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
export default StyleSheet.create({
container: {
...sharedStyles.container
},
scroll: {
flex: 1
},
containerView: {
padding: 16
},
containerAvatarAndName: {
flexDirection: 'row',
marginBottom: 24,
alignItems: 'center'
},
nameText: {
marginLeft: 8,
fontSize: 16,
...sharedStyles.textMedium
},
containerTextInput: {
marginBottom: 24
},
textInput: {
minHeight: 100,
maxHeight: 480,
height: undefined,
textAlignVertical: 'top',
padding: 16,
paddingTop: 16,
paddingBottom: 16
}
});

View File

@ -464,6 +464,20 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
} }
}; };
handleReportUser = () => {
const { navigation } = this.props;
const { member } = this.state;
const { name, _id: userId, username } = member;
if (!name || !userId || !username) {
return;
}
navigation.navigate('ReportUserView', {
name,
userId,
username
});
};
toggleEncrypted = async () => { toggleEncrypted = async () => {
logEvent(events.RA_TOGGLE_ENCRYPTED); logEvent(events.RA_TOGGLE_ENCRYPTED);
const { room } = this.state; const { room } = this.state;
@ -863,22 +877,39 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
if (t === 'd' && !isGroupChat(room)) { if (t === 'd' && !isGroupChat(room)) {
return ( return (
<List.Section> <>
<List.Separator /> <List.Section>
<List.Item <List.Separator />
title={`${blocker ? 'Unblock' : 'Block'}_user`} <List.Item
onPress={() => title={`${blocker ? 'Unblock' : 'Block'}`}
this.onPressTouchable({ onPress={() =>
event: this.toggleBlockUser this.onPressTouchable({
}) event: this.toggleBlockUser
} })
testID='room-actions-block-user' }
left={() => <List.Icon name='ignore' color={themes[theme].dangerColor} />} testID='room-actions-block-user'
showActionIndicator left={() => <List.Icon name='ignore' />}
color={themes[theme].dangerColor} showActionIndicator
/> />
<List.Separator /> <List.Separator />
</List.Section> </List.Section>
<List.Section>
<List.Separator />
<List.Item
title={'Report'}
onPress={() =>
this.onPressTouchable({
event: this.handleReportUser
})
}
testID='room-actions-block-user'
left={() => <List.Icon name='warning' color={themes[theme].dangerColor} />}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
</List.Section>
</>
); );
} }

View File

@ -8,6 +8,7 @@ import i18n from '../../../i18n';
import { useVideoConf } from '../../../lib/hooks/useVideoConf'; import { useVideoConf } from '../../../lib/hooks/useVideoConf';
import { useTheme } from '../../../theme'; import { useTheme } from '../../../theme';
import styles from '../styles'; import styles from '../styles';
import { compareServerVersion } from '../../../lib/methods/helpers';
function BaseButton({ function BaseButton({
danger, danger,
@ -31,7 +32,9 @@ function BaseButton({
return ( return (
<BorderlessButton enabled={enabled} testID={`room-info-view-${iconName}`} onPress={onPress} style={styles.roomButton}> <BorderlessButton enabled={enabled} testID={`room-info-view-${iconName}`} onPress={onPress} style={styles.roomButton}>
<CustomIcon name={iconName} size={30} color={color} /> <CustomIcon name={iconName} size={30} color={color} />
<Text style={[styles.roomButtonText, { color }]}>{label}</Text> <Text numberOfLines={1} style={[styles.roomButtonText, { color }]}>
{label}
</Text>
</BorderlessButton> </BorderlessButton>
); );
return null; return null;
@ -59,7 +62,10 @@ interface IRoomInfoButtons {
handleCreateDirectMessage: () => void; handleCreateDirectMessage: () => void;
handleIgnoreUser: () => void; handleIgnoreUser: () => void;
handleBlockUser: () => void; handleBlockUser: () => void;
handleReportUser: () => void;
roomFromRid: ISubscription | undefined; roomFromRid: ISubscription | undefined;
serverVersion: string | null;
itsMe?: boolean;
} }
export const RoomInfoButtons = ({ export const RoomInfoButtons = ({
@ -71,7 +77,10 @@ export const RoomInfoButtons = ({
handleCreateDirectMessage, handleCreateDirectMessage,
handleIgnoreUser, handleIgnoreUser,
handleBlockUser, handleBlockUser,
roomFromRid handleReportUser,
roomFromRid,
serverVersion,
itsMe
}: IRoomInfoButtons): React.ReactElement => { }: IRoomInfoButtons): React.ReactElement => {
const room = roomFromRid || roomFromProps; const room = roomFromRid || roomFromProps;
// Following the web behavior, when is a DM with myself, shouldn't appear block or ignore option // Following the web behavior, when is a DM with myself, shouldn't appear block or ignore option
@ -82,7 +91,9 @@ export const RoomInfoButtons = ({
const isBlocked = room?.blocker; const isBlocked = room?.blocker;
const renderIgnoreUser = isDirectFromSaved && !isFromDm && !isDmWithMyself; const renderIgnoreUser = isDirectFromSaved && !isFromDm && !isDmWithMyself;
const renderBlockUser = isDirectFromSaved && isFromDm && !isDmWithMyself; const renderBlockUser = !itsMe && isDirectFromSaved && isFromDm && !isDmWithMyself;
const renderReportUser =
!itsMe && isDirectFromSaved && !isDmWithMyself && compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.4.0');
return ( return (
<View style={styles.roomButtonsContainer}> <View style={styles.roomButtonsContainer}>
@ -93,13 +104,18 @@ export const RoomInfoButtons = ({
label={i18n.t(isIgnored ? 'Unignore' : 'Ignore')} label={i18n.t(isIgnored ? 'Unignore' : 'Ignore')}
iconName='ignore' iconName='ignore'
showIcon={!!renderIgnoreUser} showIcon={!!renderIgnoreUser}
danger
/> />
<BaseButton <BaseButton
onPress={handleBlockUser} onPress={handleBlockUser}
label={i18n.t(`${isBlocked ? 'Unblock' : 'Block'}_user`)} label={i18n.t(`${isBlocked ? 'Unblock' : 'Block'}`)}
iconName='ignore' iconName='ignore'
showIcon={!!renderBlockUser} showIcon={!!renderBlockUser}
/>
<BaseButton
onPress={handleReportUser}
label={i18n.t('Report')}
iconName='warning'
showIcon={!!renderReportUser}
danger danger
/> />
</View> </View>

View File

@ -31,8 +31,8 @@ import RoomInfoViewTitle from './components/RoomInfoViewTitle';
import styles from './styles'; import styles from './styles';
type TRoomInfoViewNavigationProp = CompositeNavigationProp< type TRoomInfoViewNavigationProp = CompositeNavigationProp<
StackNavigationProp<ChatsStackParamList, 'RoomInfoView'>, StackNavigationProp<ChatsStackParamList, 'RoomInfoView'>,
StackNavigationProp<MasterDetailInsideStackParamList> StackNavigationProp<MasterDetailInsideStackParamList>
>; >;
type TRoomInfoViewRouteProp = RouteProp<ChatsStackParamList, 'RoomInfoView'>; type TRoomInfoViewRouteProp = RouteProp<ChatsStackParamList, 'RoomInfoView'>;
@ -59,6 +59,7 @@ const RoomInfoView = (): React.ReactElement => {
subscribedRoom, subscribedRoom,
usersRoles, usersRoles,
roles, roles,
serverVersion,
// permissions // permissions
editRoomPermission, editRoomPermission,
editOmnichannelContact, editOmnichannelContact,
@ -68,6 +69,7 @@ const RoomInfoView = (): React.ReactElement => {
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
roles: state.roles, roles: state.roles,
usersRoles: state.usersRoles, usersRoles: state.usersRoles,
serverVersion: state.server.version,
// permissions // permissions
editRoomPermission: state.permissions['edit-room'], editRoomPermission: state.permissions['edit-room'],
editOmnichannelContact: state.permissions['edit-omnichannel-contact'], editOmnichannelContact: state.permissions['edit-omnichannel-contact'],
@ -275,6 +277,14 @@ const RoomInfoView = (): React.ReactElement => {
if (r?.rid) handleIgnore(roomUser._id, !isIgnored, r?.rid); if (r?.rid) handleIgnore(roomUser._id, !isIgnored, r?.rid);
}; };
const handleReportUser = () => {
navigate('ReportUserView', {
name: roomUser?.name,
userId: roomUser?._id,
username: roomUser.username
});
};
return ( return (
<ScrollView style={[styles.scroll, { backgroundColor: colors.backgroundColor }]}> <ScrollView style={[styles.scroll, { backgroundColor: colors.backgroundColor }]}>
<StatusBar /> <StatusBar />
@ -301,10 +311,13 @@ const RoomInfoView = (): React.ReactElement => {
handleBlockUser={handleBlockUser} handleBlockUser={handleBlockUser}
handleCreateDirectMessage={handleCreateDirectMessage} handleCreateDirectMessage={handleCreateDirectMessage}
handleIgnoreUser={handleIgnoreUser} handleIgnoreUser={handleIgnoreUser}
handleReportUser={handleReportUser}
isDirect={isDirect} isDirect={isDirect}
room={room || roomUser} room={room || roomUser}
roomUserId={roomUser?._id} roomUserId={roomUser?._id}
roomFromRid={roomFromRid} roomFromRid={roomFromRid}
serverVersion={serverVersion}
itsMe={itsMe}
/> />
</View> </View>
<RoomInfoViewBody isDirect={isDirect} room={room} roomUser={roomUser} /> <RoomInfoViewBody isDirect={isDirect} room={room} roomUser={roomUser} />

View File

@ -79,11 +79,12 @@ export default StyleSheet.create({
}, },
roomButton: { roomButton: {
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 20, marginHorizontal: 4,
justifyContent: 'space-between' justifyContent: 'space-between',
width: 80
}, },
roomButtonText: { roomButtonText: {
marginTop: 5 marginTop: 4
}, },
roomInfoViewTitleContainer: { roomInfoViewTitleContainer: {
paddingTop: 16, paddingTop: 16,

View File

@ -1,7 +1,7 @@
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { LISTENER } from '../../containers/Toast'; import { LISTENER } from '../../containers/Toast';
import { IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions'; import { IGetRoomRoles, IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { getRoomTitle, showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers'; import { getRoomTitle, showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
import EventEmitter from '../../lib/methods/helpers/events'; import EventEmitter from '../../lib/methods/helpers/events';
@ -18,9 +18,9 @@ const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
goRoom({ item, isMasterDetail, popToRoot: true }); goRoom({ item, isMasterDetail, popToRoot: true });
}; };
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => { export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles?: IGetRoomRoles[]): boolean => {
const userRoleResult = roomRoles.find((r: any) => r.u._id === selectedUser._id); const userRoleResult = roomRoles?.find((r: any) => r.u._id === selectedUser._id);
return userRoleResult?.roles.includes(role); return !!userRoleResult?.roles.includes(role);
}; };
export const fetchRoomMembersRoles = async (roomType: TRoomType, rid: string, updateState: any): Promise<void> => { export const fetchRoomMembersRoles = async (roomType: TRoomType, rid: string, updateState: any): Promise<void> => {

View File

@ -13,7 +13,7 @@ import SafeAreaView from '../../containers/SafeAreaView';
import SearchBox from '../../containers/SearchBox'; import SearchBox from '../../containers/SearchBox';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import UserItem from '../../containers/UserItem'; import UserItem from '../../containers/UserItem';
import { TSubscriptionModel, TUserModel } from '../../definitions'; import { IGetRoomRoles, TSubscriptionModel, TUserModel } from '../../definitions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { useAppSelector, usePermissions } from '../../lib/hooks'; import { useAppSelector, usePermissions } from '../../lib/hooks';
import { compareServerVersion, getRoomTitle, isGroupChat } from '../../lib/methods/helpers'; import { compareServerVersion, getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
@ -50,7 +50,7 @@ interface IRoomMembersViewState {
members: TUserModel[]; members: TUserModel[];
room: TSubscriptionModel; room: TSubscriptionModel;
end: boolean; end: boolean;
roomRoles: any; roomRoles?: IGetRoomRoles[];
filter: string; filter: string;
page: number; page: number;
} }
@ -93,7 +93,7 @@ const RoomMembersView = (): React.ReactElement => {
members: [], members: [],
room: params.room || ({} as TSubscriptionModel), room: params.room || ({} as TSubscriptionModel),
end: false, end: false,
roomRoles: null, roomRoles: undefined,
filter: '', filter: '',
page: 0 page: 0
} }

View File

@ -82,13 +82,13 @@ const Header = React.memo(({ room, thread }: IHeader) => {
<View style={styles.inner}> <View style={styles.inner}>
<Text numberOfLines={1} style={styles.text}> <Text numberOfLines={1} style={styles.text}>
<Text style={[styles.text, { color: textColor }]} numberOfLines={1}> <Text style={[styles.text, { color: textColor }]} numberOfLines={1}>
{I18n.t('Sending_to')}{' '} {I18n.t('Sending_to')}
</Text>
<CustomIcon name={icon} size={16} color={textColor} />
<Text style={[styles.name, { color: textColor }]} numberOfLines={1}>
{title}
</Text> </Text>
</Text> </Text>
<CustomIcon name={icon} size={16} color={textColor} />
<Text style={[styles.name, { color: textColor }]} numberOfLines={1}>
{title}
</Text>
</View> </View>
</View> </View>
); );

View File

@ -76,7 +76,6 @@ describe('Join room from directory', () => {
.withTimeout(2000); .withTimeout(2000);
await element(by.id('directory-view-dropdown')).tap(); await element(by.id('directory-view-dropdown')).tap();
await element(by.label('Users')).atIndex(0).tap(); await element(by.label('Users')).atIndex(0).tap();
await element(by.label('Search by')).atIndex(0).tap();
await navigateToRoom(otherUser.username); await navigateToRoom(otherUser.username);
}); });
@ -88,7 +87,6 @@ describe('Join room from directory', () => {
.withTimeout(2000); .withTimeout(2000);
await element(by.id('directory-view-dropdown')).tap(); await element(by.id('directory-view-dropdown')).tap();
await element(by.label('Teams')).atIndex(0).tap(); await element(by.label('Teams')).atIndex(0).tap();
await element(by.label('Search by')).atIndex(0).tap();
await navigateToRoom(team); await navigateToRoom(team);
}); });
}); });

View File

@ -47,11 +47,12 @@ describe('Ignore/Block User', () => {
await navigateToInfoView(); await navigateToInfoView();
}); });
it('should block user', async () => { it('should block user', async () => {
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user')))) await sleep(300);
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block'))))
.toBeVisible() .toBeVisible()
.withTimeout(2000); .withTimeout(2000);
await element(by.id('room-info-view-ignore')).tap(); await element(by.id('room-info-view-ignore')).tap();
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unblock user')))) await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unblock'))))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
await tapBack(); await tapBack();
@ -67,7 +68,7 @@ describe('Ignore/Block User', () => {
await sleep(300); // wait for navigation animation await sleep(300); // wait for navigation animation
await tapAndWaitFor( await tapAndWaitFor(
element(by.id('room-info-view-ignore')), element(by.id('room-info-view-ignore')),
element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user'))), element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block'))),
2000 2000
); );
await tapBack(); await tapBack();
@ -124,5 +125,59 @@ describe('Ignore/Block User', () => {
.withTimeout(2000); .withTimeout(2000);
}); });
}); });
describe('Report user', () => {
it('should go to user info view from a DM', async () => {
await tapBack();
await sleep(300);
await navigateToRoom(otherUser.username);
await navigateToInfoView();
});
it('should report a user from a DM', async () => {
await waitFor(element(by.id('room-info-view-warning').withDescendant(by[textMatcher]('Report'))))
.toBeVisible()
.withTimeout(2000);
await element(by.id('room-info-view-warning')).tap();
await sleep(300);
await waitFor(element(by.id('report-user-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('report-user-view-input')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('report-user-view-input')).replaceText('e2e test');
await element(by.id('report-user-view-submit')).tap();
await sleep(500);
await checkRoomTitle(otherUser.username);
});
it('should go to user info view from a channel', async () => {
await tapBack();
await sleep(300);
await navigateToRoom(room);
await waitFor(element(by[textMatcher](otherUser.username)).atIndex(0))
.toExist()
.withTimeout(30000);
await element(by[textMatcher](otherUser.username)).atIndex(0).tap();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
});
it('should report a user from a channel', async () => {
await waitFor(element(by.id('room-info-view-warning').withDescendant(by[textMatcher]('Report'))))
.toBeVisible()
.withTimeout(2000);
await element(by.id('room-info-view-warning')).tap();
await sleep(300);
await waitFor(element(by.id('report-user-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('report-user-view-input')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('report-user-view-input')).replaceText('e2e test');
await element(by.id('report-user-view-submit')).tap();
await sleep(500);
await checkRoomTitle(room);
});
});
}); });
}); });

View File

@ -1766,7 +1766,7 @@
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.44.2; MARKETING_VERSION = 4.45.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
@ -1805,7 +1805,7 @@
INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 4.44.2; MARKETING_VERSION = 4.45.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;

View File

@ -26,7 +26,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>4.44.2</string> <string>4.45.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@ -54,6 +54,7 @@
<string>googlechromes</string> <string>googlechromes</string>
<string>firefox</string> <string>firefox</string>
<string>brave</string> <string>brave</string>
<string>tel</string>
</array> </array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>

View File

@ -26,7 +26,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>4.44.2</string> <string>4.45.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>KeychainGroup</key> <key>KeychainGroup</key>

View File

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