diff --git a/app/definitions/rest/v1/index.ts b/app/definitions/rest/v1/index.ts
index 385da8927..9fbf3ffc9 100644
--- a/app/definitions/rest/v1/index.ts
+++ b/app/definitions/rest/v1/index.ts
@@ -20,6 +20,7 @@ import { CommandsEndpoints } from './commands';
import { PushTokenEndpoints } from './pushToken';
import { DirectoryEndpoint } from './directory';
import { AutoTranslateEndpoints } from './autotranslate';
+import { ModerationEndpoints } from './moderation';
export type Endpoints = ChannelsEndpoints &
ChatEndpoints &
@@ -42,4 +43,5 @@ export type Endpoints = ChannelsEndpoints &
CommandsEndpoints &
PushTokenEndpoints &
DirectoryEndpoint &
- AutoTranslateEndpoints;
+ AutoTranslateEndpoints &
+ ModerationEndpoints;
diff --git a/app/definitions/rest/v1/moderation.ts b/app/definitions/rest/v1/moderation.ts
new file mode 100644
index 000000000..b0fe15d62
--- /dev/null
+++ b/app/definitions/rest/v1/moderation.ts
@@ -0,0 +1,5 @@
+export type ModerationEndpoints = {
+ 'moderation.reportUser': {
+ POST: (params: { userId: string; description: string }) => void;
+ };
+};
diff --git a/app/i18n/locales/ar.json b/app/i18n/locales/ar.json
index cb5df1b35..e7c9f4938 100644
--- a/app/i18n/locales/ar.json
+++ b/app/i18n/locales/ar.json
@@ -44,7 +44,6 @@
"Avatar_Url": "عنوان ويب الصورة الرمزية",
"Away": "غير متواجد",
"Black": "أسود",
- "Block_user": "حظر المستخدم",
"Browser": "المتصفح",
"Busy": "مشغول",
"Cancel_editing": "إلغاء التعديل",
@@ -372,7 +371,6 @@
"Two_Factor_Authentication": "المصادقة الثنائية",
"unarchive": "إلغاء الأرشفة",
"UNARCHIVE": "إلغاء الأرشفة",
- "Unblock_user": "إلغاء حظر عن مستخدم",
"Unfollowed_thread": "موضوع غير متابع",
"Unmute": "إلغاء كتم",
"unmuted": "إلغاء كتم",
diff --git a/app/i18n/locales/bn-IN.json b/app/i18n/locales/bn-IN.json
index 4e0f5b751..848e6b19c 100644
--- a/app/i18n/locales/bn-IN.json
+++ b/app/i18n/locales/bn-IN.json
@@ -51,7 +51,6 @@
"Avatar_Url": "অবতার URL",
"Away": "দূরে",
"Black": "কালো",
- "Block_user": "ব্যবহারকারী ব্লক করুন",
"Browser": "ব্রাউজার",
"Busy": "ব্যস্ত",
"Cancel_editing": "সম্পাদনা বাতিল করুন",
@@ -405,7 +404,6 @@
"Two_Factor_Authentication": "দুটি ধারণামূলক প্রমাণীকরণ",
"unarchive": "আনআরকাইভ",
"UNARCHIVE": "আনআরকাইভ",
- "Unblock_user": "ব্যবহারকারী আনব্লক করুন",
"Unfollowed_thread": "থ্রেড অনফলোয়েড",
"Unmute": "আনমিউট",
"unmuted": "আনমিউটেড",
diff --git a/app/i18n/locales/de.json b/app/i18n/locales/de.json
index cac081cef..1cdc1d1c6 100644
--- a/app/i18n/locales/de.json
+++ b/app/i18n/locales/de.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatar-URL",
"Away": "Abwesend",
"Black": "Schwarz",
- "Block_user": "Benutzer blockieren",
"Browser": "Browser",
"Busy": "Beschäftigt",
"Cancel_editing": "Bearbeitung abbrechen",
@@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Zwei-Faktor-Authentifizierung",
"unarchive": "wiederherstellen",
"UNARCHIVE": "WIEDERHERSTELLEN",
- "Unblock_user": "Benutzer entsperren",
"Unfollowed_thread": "Thread nicht mehr folgen",
"Unmute": "Stummschaltung aufheben",
"unmuted": "Stummschaltung aufgehoben",
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index 73f311964..23db513e0 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL",
"Away": "Away",
"Black": "Black",
- "Block_user": "Block user",
"Browser": "Browser",
"Busy": "Busy",
"Cancel_editing": "Cancel editing",
@@ -407,7 +406,6 @@
"Two_Factor_Authentication": "Two-factor authentication",
"unarchive": "unarchive",
"UNARCHIVE": "UNARCHIVE",
- "Unblock_user": "Unblock user",
"Unfollowed_thread": "Unfollowed thread",
"Unmute": "Unmute",
"unmuted": "unmuted",
@@ -762,6 +760,11 @@
"Enable_writing_in_room": "Enable writing in room",
"Disable_writing_in_room": "Disable writing in room",
"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 don’t have permission to perform this action. Check with a workspace administrator.",
"Jump_to_message": "Jump to message",
"Missed_call": "Missed call"
diff --git a/app/i18n/locales/es.json b/app/i18n/locales/es.json
index 3edf67cf3..ad479fe47 100644
--- a/app/i18n/locales/es.json
+++ b/app/i18n/locales/es.json
@@ -38,7 +38,6 @@
"Avatar_Url": "URL del Avatar",
"Away": "Ausente",
"Black": "Negro",
- "Block_user": "Bloquear usuario",
"Busy": "Ocupado",
"Cancel_editing": "Cancelar edición",
"Cancel_recording": "Cancelar grabación",
@@ -239,7 +238,6 @@
"Two_Factor_Authentication": "Autenticación de doble factor",
"unarchive": "desarchivar",
"UNARCHIVE": "DESARCHIVAR",
- "Unblock_user": "Desbloquear usuario",
"Unfollowed_thread": "Dejar de seguir el hilo",
"Unmute": "Desmutear",
"unmuted": "Desmuteado",
diff --git a/app/i18n/locales/fi.json b/app/i18n/locales/fi.json
index 9daaa4169..878d9b1be 100644
--- a/app/i18n/locales/fi.json
+++ b/app/i18n/locales/fi.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatarin URL-osoite",
"Away": "Poissa",
"Black": "Musta",
- "Block_user": "Estä käyttäjä",
"Browser": "Selain",
"Busy": "Varattu",
"Cancel_editing": "Peruuta muokkaus",
@@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Kaksivaiheinen tunnistautuminen",
"unarchive": "palauta arkistosta",
"UNARCHIVE": "PALAUTA ARKISTOSTA",
- "Unblock_user": "Poista käyttäjän esto",
"Unfollowed_thread": "Lopetettiin ketjun seuraaminen",
"Unmute": "Mykistys poistettu",
"unmuted": "poisti mykistyksen",
diff --git a/app/i18n/locales/fr.json b/app/i18n/locales/fr.json
index dd229bb2d..2f7476092 100644
--- a/app/i18n/locales/fr.json
+++ b/app/i18n/locales/fr.json
@@ -45,7 +45,6 @@
"Avatar_Url": "URL de l'avatar",
"Away": "Absent",
"Black": "Noir",
- "Block_user": "Bloquer l'utilisateur",
"Browser": "Navigateur",
"Busy": "Occupé",
"Cancel_editing": "Annuler la modification",
@@ -381,7 +380,6 @@
"Two_Factor_Authentication": "Authentification à deux facteurs",
"unarchive": "désarchiver",
"UNARCHIVE": "DÉSARCHIVER",
- "Unblock_user": "Débloquer l'utilisateur",
"Unfollowed_thread": "Ne plus suivre ce fil",
"Unmute": "Rendre la parole",
"unmuted": "rendu la parole",
diff --git a/app/i18n/locales/hi-IN.json b/app/i18n/locales/hi-IN.json
index e7f394d22..f0ce8bac4 100644
--- a/app/i18n/locales/hi-IN.json
+++ b/app/i18n/locales/hi-IN.json
@@ -51,7 +51,6 @@
"Avatar_Url": "अवतार URL",
"Away": "दूर",
"Black": "काला",
- "Block_user": "उपयोगकर्ता को ब्लॉक करें",
"Browser": "ब्राउज़र",
"Busy": "व्यस्त",
"Cancel_editing": "संपादन रद्द करें",
@@ -405,7 +404,6 @@
"Two_Factor_Authentication": "दो-क्रमिक प्रमाणीकरण",
"unarchive": "अनारकाइव",
"UNARCHIVE": "अनारकाइव करें",
- "Unblock_user": "उपयोगकर्ता को अनब्लॉक करें",
"Unfollowed_thread": "थ्रेड का अनुसरण नहीं किया गया",
"Unmute": "आवाज़ हटाएं",
"unmuted": "आवाज़ हटाई गई",
diff --git a/app/i18n/locales/hu.json b/app/i18n/locales/hu.json
index a138d5962..64a639050 100644
--- a/app/i18n/locales/hu.json
+++ b/app/i18n/locales/hu.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL",
"Away": "Távol",
"Black": "Fekete",
- "Block_user": "Felhasználó tiltása",
"Browser": "Böngésző",
"Busy": "Elfoglalt",
"Cancel_editing": "Szerkesztés megszakítás",
@@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Kétfaktoros hitelesíté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",
"Unmute": "Némítás megszüntetése",
"unmuted": "Némítás megszüntetve",
diff --git a/app/i18n/locales/it.json b/app/i18n/locales/it.json
index 62873f639..e5bb866c7 100644
--- a/app/i18n/locales/it.json
+++ b/app/i18n/locales/it.json
@@ -51,7 +51,6 @@
"Avatar_Url": "URL avatar",
"Away": "Assente",
"Black": "Nero",
- "Block_user": "Blocca utente",
"Browser": "Browser",
"Busy": "Occupato",
"Cancel_editing": "Annulla modifica",
@@ -386,7 +385,6 @@
"Two_Factor_Authentication": "Autenticazione a due fattori",
"unarchive": "rimuovi dall'archivio",
"UNARCHIVE": "RIMUOVI DALL'ARCHIVIO",
- "Unblock_user": "Sblocca utente",
"Unfollowed_thread": "Non segui più il thread",
"Unmute": "Attiva notifiche",
"unmuted": "notifiche attivate",
diff --git a/app/i18n/locales/ja.json b/app/i18n/locales/ja.json
index e8cef1f39..5d85aabc4 100644
--- a/app/i18n/locales/ja.json
+++ b/app/i18n/locales/ja.json
@@ -45,7 +45,6 @@
"Avatar_Url": "アバターURL",
"Away": "退出中",
"Black": "ブラック",
- "Block_user": "ブロックしたユーザー",
"Browser": "ブラウザ",
"Busy": "取り込み中",
"Cancel_editing": "編集をキャンセル",
@@ -334,7 +333,6 @@
"Two_Factor_Authentication": "2段階認証",
"unarchive": "アーカイブ解除",
"UNARCHIVE": "アーカイブ解除",
- "Unblock_user": "ブロックを解除",
"Unfollowed_thread": "スレッド更新時に通知しない",
"Unmute": "ミュート解除",
"unmuted": "ミュート解除しました",
diff --git a/app/i18n/locales/nl.json b/app/i18n/locales/nl.json
index 8e4e37391..1ca17a219 100644
--- a/app/i18n/locales/nl.json
+++ b/app/i18n/locales/nl.json
@@ -45,7 +45,6 @@
"Avatar_Url": "Avatar-URL",
"Away": "Afwezig",
"Black": "Zwart",
- "Block_user": "Blokkeer gebruiker",
"Browser": "Browser",
"Busy": "Bezig",
"Cancel_editing": "Bewerken annuleren",
@@ -381,7 +380,6 @@
"Two_Factor_Authentication": "Twee-factor authenticatie",
"unarchive": "dearchiveren",
"UNARCHIVE": "DEARCHIVEREN",
- "Unblock_user": "Deblokkeer gebruiker",
"Unfollowed_thread": "Draad ontvolgd",
"Unmute": "Dempen opheffen",
"unmuted": "ongedempt",
diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json
index ea9a1d4e1..4a54fc597 100644
--- a/app/i18n/locales/pt-BR.json
+++ b/app/i18n/locales/pt-BR.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL",
"Away": "Ausente",
"Black": "Preto",
- "Block_user": "Bloquear usuário",
"Browser": "Navegador",
"Busy": "Ocupado",
"Cancel_editing": "Cancelar edição",
@@ -407,7 +406,6 @@
"Two_Factor_Authentication": "Autenticação de dois fatores",
"unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR",
- "Unblock_user": "Desbloquear usuário",
"Unfollowed_thread": "Parou de seguir tópico",
"Unmute": "Permitir 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}}",
"Enable_writing_in_room": "Permitir 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:",
"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",
diff --git a/app/i18n/locales/pt-PT.json b/app/i18n/locales/pt-PT.json
index a1a004d0c..98d14d4fb 100644
--- a/app/i18n/locales/pt-PT.json
+++ b/app/i18n/locales/pt-PT.json
@@ -44,7 +44,6 @@
"Avatar_Url": "URL do Avatar",
"Away": "Ausente",
"Black": "Preto",
- "Block_user": "Bloquear utilizador",
"Browser": "Navegador",
"Busy": "Ocupado",
"Cancel_editing": "Cancelar edição",
@@ -323,7 +322,6 @@
"Two_Factor_Authentication": "Autenticação 2FA",
"unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR",
- "Unblock_user": "Desbloquear utilizador",
"Unmute": "Retirar silêncio",
"unmuted": "silêncio removido",
"Unpin": "Desafixar",
diff --git a/app/i18n/locales/ru.json b/app/i18n/locales/ru.json
index 85c9b0ee3..bcdbc1728 100644
--- a/app/i18n/locales/ru.json
+++ b/app/i18n/locales/ru.json
@@ -51,7 +51,6 @@
"Avatar_Url": "URL аватара",
"Away": "Отошел",
"Black": "Черный",
- "Block_user": "Блокировать пользователя",
"Browser": "Браузер",
"Busy": "Занят",
"Cancel_editing": "Отменить правку",
@@ -392,7 +391,6 @@
"Two_Factor_Authentication": "Двухфакторная аутентификация",
"unarchive": "разархивировать",
"UNARCHIVE": "РАЗАРХИВИРОВАТЬ",
- "Unblock_user": "Разблокировать пользователя",
"Unfollowed_thread": "Не следить",
"Unmute": "Отменить заглушивание",
"unmuted": "Заглушивание отменено",
diff --git a/app/i18n/locales/sl-SI.json b/app/i18n/locales/sl-SI.json
index 345ebe343..37033fb0f 100644
--- a/app/i18n/locales/sl-SI.json
+++ b/app/i18n/locales/sl-SI.json
@@ -51,7 +51,6 @@
"Avatar_Url": "Avatar URL",
"Away": "Odsoten",
"Black": "Črn",
- "Block_user": "Blokiraj uporabnika",
"Browser": "Brskalnik",
"Busy": "Zaseden",
"Cancel_editing": "Prekliči urejanje",
@@ -389,7 +388,6 @@
"Two_Factor_Authentication": "Dvofaktorska overjanje",
"unarchive": "Odpakirati",
"UNARCHIVE": "ODPAKIRATI",
- "Unblock_user": "Odblokirati uporabnika",
"Unfollowed_thread": "Nit, ki ji nihče ne sledi",
"Unmute": "Preklicati utišanje",
"unmuted": "preklicati utišanje",
diff --git a/app/i18n/locales/sv.json b/app/i18n/locales/sv.json
index 3949e02da..13f50884d 100644
--- a/app/i18n/locales/sv.json
+++ b/app/i18n/locales/sv.json
@@ -51,7 +51,6 @@
"Avatar_Url": "URL till avatar",
"Away": "Borta",
"Black": "Svart",
- "Block_user": "Blockera användare",
"Browser": "Webbläsare",
"Busy": "Upptagen",
"Cancel_editing": "Avbryt redigering",
@@ -406,7 +405,6 @@
"Two_Factor_Authentication": "Tvåfaktorsautentisering",
"unarchive": "avarkivera",
"UNARCHIVE": "AVARKIVERA",
- "Unblock_user": "Avblockera användare",
"Unfollowed_thread": "Sluta följa tråd",
"Unmute": "Slå på ljudet",
"unmuted": "slog på ljudet",
diff --git a/app/i18n/locales/ta-IN.json b/app/i18n/locales/ta-IN.json
index 29c83c600..fdb3d563b 100644
--- a/app/i18n/locales/ta-IN.json
+++ b/app/i18n/locales/ta-IN.json
@@ -51,7 +51,6 @@
"Avatar_Url": "அவதார் URL",
"Away": "விலகியிருக்கிறேன்",
"Black": "கருப்பு",
- "Block_user": "பயனாளரை தடுக்கவும்",
"Browser": "உலாவி",
"Busy": "தொலைநேரம்",
"Cancel_editing": "திருத்தலை ரத்து செய்க",
@@ -405,7 +404,6 @@
"Two_Factor_Authentication": "இரண்டு பரிமாறு அங்கீகாரம்",
"unarchive": "அசைவு செய்",
"UNARCHIVE": "அசைவு செய்",
- "Unblock_user": "பயனரை விடுவிக்கு",
"Unfollowed_thread": "பின்னாளிக்குப் பின்னாளிக்கப்பட்டுள்ளது",
"Unmute": "மூடாது",
"unmuted": "மூடப்படவில்லை",
diff --git a/app/i18n/locales/te-IN.json b/app/i18n/locales/te-IN.json
index f0f61a193..82006d235 100644
--- a/app/i18n/locales/te-IN.json
+++ b/app/i18n/locales/te-IN.json
@@ -51,7 +51,6 @@
"Avatar_Url": "అవతార్ URL",
"Away": "దూరంగా",
"Black": "నలుపు",
- "Block_user": "వాడిని నిలిపించండి",
"Browser": "బ్రౌజర్",
"Busy": "వ్యస్తంగా",
"Cancel_editing": "సవరించడానికి రద్దు చేయి",
@@ -405,7 +404,6 @@
"Two_Factor_Authentication": "రెండు అంశ ప్రామాణీకరణ",
"unarchive": "అనార్కైవ్",
"UNARCHIVE": "అనార్కైవ్",
- "Unblock_user": "వాడుకరిని నిషేధించకోండి",
"Unfollowed_thread": "అన్ఫాలో థ్రెడ్",
"Unmute": "ఆధ్వర్యపరచించండి",
"unmuted": "ఆధ్వర్యపరచించబడింది",
diff --git a/app/i18n/locales/tr.json b/app/i18n/locales/tr.json
index c789deea2..91ef9fadb 100644
--- a/app/i18n/locales/tr.json
+++ b/app/i18n/locales/tr.json
@@ -43,7 +43,6 @@
"Avatar_Url": "Profil fotoğrafı URL'si",
"Away": "Uzakta",
"Black": "Koyu",
- "Block_user": "Kullanıcıyı engelle",
"Browser": "Tarayıcı",
"Busy": "Meşgul",
"Cancel_editing": "Düzenlemeyi iptal et",
@@ -369,7 +368,6 @@
"Two_Factor_Authentication": "İki faktörlü Kimlik Doğrulama",
"unarchive": "arşivden çıkar",
"UNARCHIVE": "ARŞİVDEN ÇIKAR",
- "Unblock_user": "Kullanıcının engelini kaldır",
"Unfollowed_thread": "Takip edilmeyen başlık",
"Unmute": "Sesi Aç",
"unmuted": "Sesi Açıldı",
diff --git a/app/i18n/locales/zh-CN.json b/app/i18n/locales/zh-CN.json
index 30a133fe7..e3c0b852e 100644
--- a/app/i18n/locales/zh-CN.json
+++ b/app/i18n/locales/zh-CN.json
@@ -43,7 +43,6 @@
"Avatar_Url": "头像地址",
"Away": "离开",
"Black": "黑色",
- "Block_user": "屏蔽此用户",
"Browser": "浏览器",
"Busy": "忙碌",
"Cancel_editing": "取消编辑",
@@ -365,7 +364,6 @@
"Two_Factor_Authentication": "双重认证",
"unarchive": "取消封存",
"UNARCHIVE": "取消封存",
- "Unblock_user": "解除屏蔽",
"Unfollowed_thread": "取消追踪讨论",
"Unmute": "取消静音",
"unmuted": "静音状态",
diff --git a/app/i18n/locales/zh-TW.json b/app/i18n/locales/zh-TW.json
index b42d838f0..bc265143d 100644
--- a/app/i18n/locales/zh-TW.json
+++ b/app/i18n/locales/zh-TW.json
@@ -44,7 +44,6 @@
"Avatar_Url": "大頭貼地址",
"Away": "離開",
"Black": "黑色",
- "Block_user": "封鎖此用戶",
"Browser": "瀏覽器",
"Busy": "忙碌",
"Cancel_editing": "取消編輯",
@@ -371,7 +370,6 @@
"Two_Factor_Authentication": "雙重認證",
"unarchive": "取消封存",
"UNARCHIVE": "取消封存",
- "Unblock_user": "解除封鎖",
"Unfollowed_thread": "取消追蹤討論",
"Unmute": "取消靜音",
"unmuted": "靜音狀態",
diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts
index 1a324ba00..424566357 100644
--- a/app/lib/services/restApi.ts
+++ b/app/lib/services/restApi.ts
@@ -296,6 +296,10 @@ export const togglePinMessage = (messageId: string, pinned?: boolean) => {
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) =>
// RC 0.64.0
sdk.post('chat.reportMessage', { messageId, description: 'Message reported by user' });
diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx
index 56374a36e..bcf1e071d 100644
--- a/app/stacks/InsideStack.tsx
+++ b/app/stacks/InsideStack.tsx
@@ -11,6 +11,7 @@ import RoomView from '../views/RoomView';
import RoomsListView from '../views/RoomsListView';
import RoomActionsView from '../views/RoomActionsView';
import RoomInfoView from '../views/RoomInfoView';
+import ReportUserView from '../views/ReportUserView';
import RoomInfoEditView from '../views/RoomInfoEditView';
import RoomMembersView from '../views/RoomMembersView';
import SearchMessagesView from '../views/SearchMessagesView';
@@ -99,6 +100,7 @@ const ChatsStackNavigator = () => {
+
{/* @ts-ignore */}
diff --git a/app/stacks/MasterDetailStack/index.tsx b/app/stacks/MasterDetailStack/index.tsx
index 1ba89c82a..ffdd2f15b 100644
--- a/app/stacks/MasterDetailStack/index.tsx
+++ b/app/stacks/MasterDetailStack/index.tsx
@@ -15,6 +15,7 @@ import RoomView from '../../views/RoomView';
import RoomsListView from '../../views/RoomsListView';
import RoomActionsView from '../../views/RoomActionsView';
import RoomInfoView from '../../views/RoomInfoView';
+import ReportUserView from '../../views/ReportUserView';
import RoomInfoEditView from '../../views/RoomInfoEditView';
import ChangeAvatarView from '../../views/ChangeAvatarView';
import RoomMembersView from '../../views/RoomMembersView';
@@ -118,6 +119,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
{/* @ts-ignore */}
+
{/* @ts-ignore */}
diff --git a/app/stacks/MasterDetailStack/types.ts b/app/stacks/MasterDetailStack/types.ts
index f12c006a3..816e4b250 100644
--- a/app/stacks/MasterDetailStack/types.ts
+++ b/app/stacks/MasterDetailStack/types.ts
@@ -199,6 +199,11 @@ export type ModalStackParamList = {
SupportedVersionsWarning: {
showCloseButton?: boolean;
};
+ ReportUserView: {
+ username: string;
+ userId: string;
+ name: string;
+ };
};
export type MasterDetailInsideStackParamList = {
diff --git a/app/stacks/types.ts b/app/stacks/types.ts
index 145bfc1c8..e3509a320 100644
--- a/app/stacks/types.ts
+++ b/app/stacks/types.ts
@@ -172,6 +172,11 @@ export type ChatsStackParamList = {
room?: ISubscription;
t?: SubscriptionType;
};
+ ReportUserView: {
+ username: string;
+ userId: string;
+ name: string;
+ };
};
export type ProfileStackParamList = {
diff --git a/app/views/ReportUserView/UserInfo.tsx b/app/views/ReportUserView/UserInfo.tsx
new file mode 100644
index 000000000..c303b87b3
--- /dev/null
+++ b/app/views/ReportUserView/UserInfo.tsx
@@ -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 (
+
+
+
+ {name || username}
+
+
+ );
+};
+
+export default UserInfo;
diff --git a/app/views/ReportUserView/index.tsx b/app/views/ReportUserView/index.tsx
new file mode 100644
index 000000000..4febc2abf
--- /dev/null
+++ b/app/views/ReportUserView/index.tsx
@@ -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,
+ StackNavigationProp
+>;
+
+type TReportUserViewRouteProp = RouteProp;
+
+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();
+ const { isMasterDetail } = useAppSelector(state => ({ isMasterDetail: state.app.isMasterDetail }));
+
+ const {
+ params: { username, userId, name }
+ } = useRoute();
+
+ const {
+ control,
+ handleSubmit,
+ formState: { isValid }
+ } = useForm({ 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 (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ReportUserView;
diff --git a/app/views/ReportUserView/styles.ts b/app/views/ReportUserView/styles.ts
new file mode 100644
index 000000000..02f040721
--- /dev/null
+++ b/app/views/ReportUserView/styles.ts
@@ -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
+ }
+});
diff --git a/app/views/RoomActionsView/index.tsx b/app/views/RoomActionsView/index.tsx
index c405e4cc0..bb5a64bd1 100644
--- a/app/views/RoomActionsView/index.tsx
+++ b/app/views/RoomActionsView/index.tsx
@@ -464,6 +464,20 @@ class RoomActionsView extends React.Component {
+ 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 () => {
logEvent(events.RA_TOGGLE_ENCRYPTED);
const { room } = this.state;
@@ -863,22 +877,39 @@ class RoomActionsView extends React.Component
-
-
- this.onPressTouchable({
- event: this.toggleBlockUser
- })
- }
- testID='room-actions-block-user'
- left={() => }
- showActionIndicator
- color={themes[theme].dangerColor}
- />
-
-
+ <>
+
+
+
+ this.onPressTouchable({
+ event: this.toggleBlockUser
+ })
+ }
+ testID='room-actions-block-user'
+ left={() => }
+ showActionIndicator
+ />
+
+
+
+
+
+ this.onPressTouchable({
+ event: this.handleReportUser
+ })
+ }
+ testID='room-actions-block-user'
+ left={() => }
+ showActionIndicator
+ color={themes[theme].dangerColor}
+ />
+
+
+ >
);
}
diff --git a/app/views/RoomInfoView/components/RoomInfoButtons.tsx b/app/views/RoomInfoView/components/RoomInfoButtons.tsx
index d82466dcf..8f7c36d20 100644
--- a/app/views/RoomInfoView/components/RoomInfoButtons.tsx
+++ b/app/views/RoomInfoView/components/RoomInfoButtons.tsx
@@ -8,6 +8,7 @@ import i18n from '../../../i18n';
import { useVideoConf } from '../../../lib/hooks/useVideoConf';
import { useTheme } from '../../../theme';
import styles from '../styles';
+import { compareServerVersion } from '../../../lib/methods/helpers';
function BaseButton({
danger,
@@ -31,7 +32,9 @@ function BaseButton({
return (
- {label}
+
+ {label}
+
);
return null;
@@ -59,7 +62,10 @@ interface IRoomInfoButtons {
handleCreateDirectMessage: () => void;
handleIgnoreUser: () => void;
handleBlockUser: () => void;
+ handleReportUser: () => void;
roomFromRid: ISubscription | undefined;
+ serverVersion: string | null;
+ itsMe?: boolean;
}
export const RoomInfoButtons = ({
@@ -71,7 +77,10 @@ export const RoomInfoButtons = ({
handleCreateDirectMessage,
handleIgnoreUser,
handleBlockUser,
- roomFromRid
+ handleReportUser,
+ roomFromRid,
+ serverVersion,
+ itsMe
}: IRoomInfoButtons): React.ReactElement => {
const room = roomFromRid || roomFromProps;
// 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 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 (
@@ -93,13 +104,18 @@ export const RoomInfoButtons = ({
label={i18n.t(isIgnored ? 'Unignore' : 'Ignore')}
iconName='ignore'
showIcon={!!renderIgnoreUser}
- danger
/>
+
diff --git a/app/views/RoomInfoView/index.tsx b/app/views/RoomInfoView/index.tsx
index f53e79df4..8aded3c76 100644
--- a/app/views/RoomInfoView/index.tsx
+++ b/app/views/RoomInfoView/index.tsx
@@ -31,8 +31,8 @@ import RoomInfoViewTitle from './components/RoomInfoViewTitle';
import styles from './styles';
type TRoomInfoViewNavigationProp = CompositeNavigationProp<
- StackNavigationProp,
- StackNavigationProp
+StackNavigationProp,
+StackNavigationProp
>;
type TRoomInfoViewRouteProp = RouteProp;
@@ -59,6 +59,7 @@ const RoomInfoView = (): React.ReactElement => {
subscribedRoom,
usersRoles,
roles,
+ serverVersion,
// permissions
editRoomPermission,
editOmnichannelContact,
@@ -68,6 +69,7 @@ const RoomInfoView = (): React.ReactElement => {
isMasterDetail: state.app.isMasterDetail,
roles: state.roles,
usersRoles: state.usersRoles,
+ serverVersion: state.server.version,
// permissions
editRoomPermission: state.permissions['edit-room'],
editOmnichannelContact: state.permissions['edit-omnichannel-contact'],
@@ -275,6 +277,14 @@ const RoomInfoView = (): React.ReactElement => {
if (r?.rid) handleIgnore(roomUser._id, !isIgnored, r?.rid);
};
+ const handleReportUser = () => {
+ navigate('ReportUserView', {
+ name: roomUser?.name,
+ userId: roomUser?._id,
+ username: roomUser.username
+ });
+ };
+
return (
@@ -301,10 +311,13 @@ const RoomInfoView = (): React.ReactElement => {
handleBlockUser={handleBlockUser}
handleCreateDirectMessage={handleCreateDirectMessage}
handleIgnoreUser={handleIgnoreUser}
+ handleReportUser={handleReportUser}
isDirect={isDirect}
room={room || roomUser}
roomUserId={roomUser?._id}
roomFromRid={roomFromRid}
+ serverVersion={serverVersion}
+ itsMe={itsMe}
/>
diff --git a/app/views/RoomInfoView/styles.ts b/app/views/RoomInfoView/styles.ts
index 133a162e5..7711e6fcb 100644
--- a/app/views/RoomInfoView/styles.ts
+++ b/app/views/RoomInfoView/styles.ts
@@ -79,11 +79,12 @@ export default StyleSheet.create({
},
roomButton: {
alignItems: 'center',
- paddingHorizontal: 20,
- justifyContent: 'space-between'
+ marginHorizontal: 4,
+ justifyContent: 'space-between',
+ width: 80
},
roomButtonText: {
- marginTop: 5
+ marginTop: 4
},
roomInfoViewTitleContainer: {
paddingTop: 16,
diff --git a/e2e/tests/room/10-ignoreuser.spec.ts b/e2e/tests/room/10-ignoreuser.spec.ts
index d4743a07f..421162609 100644
--- a/e2e/tests/room/10-ignoreuser.spec.ts
+++ b/e2e/tests/room/10-ignoreuser.spec.ts
@@ -47,11 +47,12 @@ describe('Ignore/Block User', () => {
await navigateToInfoView();
});
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()
.withTimeout(2000);
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()
.withTimeout(2000);
await tapBack();
@@ -67,7 +68,7 @@ describe('Ignore/Block User', () => {
await sleep(300); // wait for navigation animation
await tapAndWaitFor(
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
);
await tapBack();
@@ -124,5 +125,59 @@ describe('Ignore/Block User', () => {
.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);
+ });
+ });
});
});