From 3cbd1e88cc4c13753e0bb414113dcd6a7c651b76 Mon Sep 17 00:00:00 2001 From: Zachary McCoy <191707+zdmc23@users.noreply.github.com> Date: Wed, 27 May 2020 10:44:25 -0400 Subject: [PATCH 01/14] Bump to 1.5 --- app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 3e87d57f..0d90e756 100644 --- a/app.json +++ b/app.json @@ -7,7 +7,7 @@ "ios", "android" ], - "version": "1.4.0", + "version": "1.5.0", "orientation": "portrait", "icon": "./assets/images/icon.png", "splash": { @@ -28,7 +28,7 @@ "android": { "package": "tools.disciple.app", "googleServicesFile": "./google-services.json", - "versionCode": 140 + "versionCode": 150 }, "description": "" } From 63df04474658a1fe41bda794fd4eaf18a0ef6b4e Mon Sep 17 00:00:00 2001 From: zdmc23 <191707+zdmc23@users.noreply.github.com> Date: Mon, 1 Jun 2020 00:48:35 -0400 Subject: [PATCH 02/14] Resolves #330 --- languages/ar.json | 43 ++++++++++++++++++++++--------------- languages/bn.json | 42 ++++++++++++++++++++++-------------- languages/en.json | 41 +++++++++++++++++++++-------------- languages/es.json | 41 +++++++++++++++++++++-------------- languages/fa.json | 41 +++++++++++++++++++++-------------- languages/fr.json | 41 +++++++++++++++++++++-------------- languages/id.json | 42 ++++++++++++++++++++++-------------- languages/nl.json | 42 ++++++++++++++++++++++-------------- languages/pt.json | 43 ++++++++++++++++++++++--------------- languages/ru.json | 45 +++++++++++++++++++++++---------------- languages/sw.json | 42 ++++++++++++++++++++++-------------- languages/tr.json | 43 ++++++++++++++++++++++--------------- languages/zhCn.json | 41 +++++++++++++++++++++-------------- languages/zhTw.json | 41 +++++++++++++++++++++-------------- screens/LoginScreen.js | 8 ++++--- screens/SettingsScreen.js | 20 ++++++++++------- 16 files changed, 376 insertions(+), 240 deletions(-) diff --git a/languages/ar.json b/languages/ar.json index 47b65cb9..b7053959 100644 --- a/languages/ar.json +++ b/languages/ar.json @@ -24,12 +24,13 @@ "search": "بحث", "membersActivity": "أفراد", "moreFields": "المزيد من الحقول", - "nameRequired": "الاسم مطلوب" + "nameRequired": "الاسم مطلوب", + "close": "قريب" }, "loginScreen": { "domain": { "label": "اسم نطاق أدوات التلمذة", - "placeholder": "مثلاً: myteam.disciple.tools", + "placeholder": "مثلاً: myteam.mydomain.com", "error": "اسم النطاق مطلوب", "errorForgotPass": "الرجاء إدخال عنوان URL للبدء في استعادة كلمة المرور الخاصة بك" }, @@ -47,9 +48,9 @@ "invalidUsername": "اسم مستخدم غير معروف. تحقق مرة أخرى أو حاول عنوان بريدك الإلكتروني.", "incorrectPassword": "كلمة المرور التي أدخلتها لاسم المستخدم غير صحيحة" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "حسنا", + "appRestart": "سيتم إعادة تشغيل التطبيق لتطبيق التكوين التالي:", + "textDirection": "اتجاه النص" }, "contactsScreen": { "contacts": "المتواصلون", @@ -61,12 +62,20 @@ "settings": "الإعدادات", "networkUnavailable": "الشبكة غير متاحة الآن في وضع عدم الاتصال بالانترنت", "networkAvailable": "تم العثور علي شبكة. العودة لوضع الاتصال بالانترنت", - "storybook": "القصص القصيرة", + "storybook": "قصة", "logout": "تسجيل الخروج", "helpSupport": "ساعد لدعم", "rememberPassword": "تذكر كلمة المرور", "rememberPasswordActive": "تمكين تسجيل الدخول باستمرار", - "rememberPasswordInactive": "حافظ على تسجيل الدخول معطلة" + "rememberPasswordInactive": "حافظ على تسجيل الدخول معطلة", + "pinCode": { + "remove": "إزالة رمز PIN", + "set": "قم بتعيين رمز PIN", + "enter": "أدخل رمز PIN", + "incorrect": "رمز PIN غير صحيح", + "saved": "تم حفظ رمز PIN بنجاح!", + "removed": "تمت إزالة رمز PIN بنجاح!" + } }, "contactDetailScreen": { "addNewContact": "إضافة جهة اتصال جديدة", @@ -81,16 +90,16 @@ "addBaptized": "إضافة مُعمّد", "addCoachedBy": "إضافة المدربين من قبل", "addCoaching": "إضافة التدريب", - "fullName": { - "label": "الاسم الكامل", - "error": "مطلوب اسم" - }, "phoneNumber": "رقم الهاتف", "initialComment": "تعليق أولي", "socialMedia": "وسائل التواصل الاجتماعي", "noContactCommentPlacheHolder": "لا توجد أي تعليقات اتصال متاحة حاليا.", + "fullName": { + "label": "الاسم الكامل", + "error": "مطلوب اسم" + }, "noContactCommentPlacheHolder1": "قد تكون هناك مشكلة في الحصول عليها من موقعك. اسحب لأسفل لمحاولة التحديث وحاول مرة أخرى.", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "أنت الآن في وضع عدم الاتصال. تأكد من أنك متصل بشبكة wifi أو بيانات الجوال لتحديث والحصول على تعليق الاتصال الخاص بك." }, "groupDetailScreen": { "addNewGroup": "إضافة مجموعة جديدة", @@ -99,16 +108,16 @@ "searchPeerGroups": "بحث مجموعات الأقران", "childGroup": "ربط المجموعة الإبن", "searchChildGroups": "بحث المجموعة الإبن", - "groupName": { - "label": "أسم المجموعة", - "error": "مطلوب اسم" - }, "addMember": "إضافة عضو", "noGroupCommentPlacheHolder": "لا توجد أي تعليقات جماعية متاحة حاليًا.", "noGroupCommentPlacheHolder1": "ربما كانت هناك مشكلة في الحصول عليها من موقعك. اسحب لأسفل لمحاولة التحديث والمحاولة مرة أخرى.", "noGroupCommentPlacheHolderOffline": "أنت غير متصل حاليًا. تأكد من اتصالك بشبكة wifi أو بيانات الجوال من أجل التحديث والحصول على تعليق مجموعتك.", "selectLocations": "اختر المواقع", - "noMembersMessage": "لا يوجد أعضاء. انقر هنا أو على \"تعديل\" لإضافة بعض الأعضاء إلى هذه المجموعة." + "noMembersMessage": "لا يوجد أعضاء. انقر هنا أو على \"تعديل\" لإضافة بعض الأعضاء إلى هذه المجموعة.", + "groupName": { + "label": "أسم المجموعة", + "error": "مطلوب اسم" + } }, "notificationsScreen": { "notifications": "إخطارات", diff --git a/languages/bn.json b/languages/bn.json index 216fba36..95683c39 100644 --- a/languages/bn.json +++ b/languages/bn.json @@ -24,12 +24,13 @@ "search": "অনুসন্ধান করুন", "membersActivity": "সদস্য", "moreFields": "আরও ক্ষেত্র", - "nameRequired": "নাম (প্রয়োজন" + "nameRequired": "নাম (প্রয়োজন", + "close": "ঘনিষ্ঠ" }, "loginScreen": { "domain": { "label": "URL টি", - "placeholder": "উদাহরণস্বরূপ myteam.disciple.tools", + "placeholder": "উদাহরণস্বরূপ myteam.mydomain.com", "error": "ডোমেন প্রয়োজনীয়", "errorForgotPass": "আপনার পাসওয়ার্ড পুনরুদ্ধার করতে URL টি প্রবেশ করুন" }, @@ -47,9 +48,9 @@ "invalidUsername": "অজানা ব্যবহারকারীর নাম। আবার চেক করুন বা আপনার ইমেল ঠিকানা চেষ্টা করুন।", "incorrectPassword": "আপনি ব্যবহারকারীর জন্য প্রবেশ করা পাসওয়ার্ডটি ভুল" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "ঠিক আছে", + "appRestart": "নিম্নলিখিত কনফিগারেশন প্রয়োগ করার জন্য অ্যাপ্লিকেশনটি আবার শুরু হবে:", + "textDirection": "লেখার দিকবিন্যাস" }, "contactsScreen": { "contacts": "যোগাযোগ", @@ -66,7 +67,15 @@ "helpSupport": "সাহায্য সহযোগীতা", "rememberPassword": "পাসওয়ার্ড মনে", "rememberPasswordActive": "সক্ষম থাকা লগ ইন করুন", - "rememberPasswordInactive": "লগ ইন অক্ষম রাখুন" + "rememberPasswordInactive": "লগ ইন অক্ষম রাখুন", + "pinCode": { + "remove": "পিন কোড সরান", + "set": "পিন কোড সেট করুন", + "enter": "পিন কোড প্রবেশ করান", + "incorrect": "ভুল পিন কোড", + "saved": "পিন কোড সফলভাবে সংরক্ষণ করা হয়েছে!", + "removed": "পিন কোড সফলভাবে সরানো হয়েছে!" + } }, "contactDetailScreen": { "addNewContact": "নতুন পরিচিতি যোগ করুন", @@ -81,15 +90,16 @@ "addBaptized": "বাপ্তিস্ম যুক্ত করুন", "addCoachedBy": "দ্বারা প্রশিক্ষিত যোগ করুন", "addCoaching": "কোচিং যোগ করুন", - "fullName": { - "label": "পুরো নাম", - "error": "নাম প্রয়োজন" - }, "phoneNumber": "ফোন নম্বর", "initialComment": "প্রাথমিক মন্তব্য", "socialMedia": "সামাজিক মাধ্যম", "noContactCommentPlacheHolder": "বর্তমানে কোনও যোগাযোগের মন্তব্য উপলব্ধ নেই।", - "noContactCommentPlacheHolder1": "আপনার সাইট থেকে এগুলি পেতে কোনও সমস্যা হতে পারে। রিফ্রেশ করার চেষ্টা করতে নীচে টানুন এবং আবার চেষ্টা করুন।" + "fullName": { + "label": "পুরো নাম", + "error": "নাম প্রয়োজন" + }, + "noContactCommentPlacheHolder1": "আপনার সাইট থেকে এগুলি পেতে কোনও সমস্যা হতে পারে। রিফ্রেশ করার চেষ্টা করতে নীচে টানুন এবং আবার চেষ্টা করুন।", + "noContactCommentPlacheHolderOffline": "আপনি বর্তমানে অফলাইন আপনার যোগাযোগের মন্তব্যটি রিফ্রেশ করার জন্য এবং আপনি ওয়াইফাই বা মোবাইল ডেটার সাথে সংযুক্ত আছেন তা নিশ্চিত করুন।" }, "groupDetailScreen": { "addNewGroup": "নতুন গ্রুপ যুক্ত করুন", @@ -98,16 +108,16 @@ "searchPeerGroups": "পিয়ার গ্রুপগুলি অনুসন্ধান করুন", "childGroup": "শিশু দল", "searchChildGroups": "শিশুদের দল অনুসন্ধান করুন Search", - "groupName": { - "label": "দলের নাম", - "error": "নাম প্রয়োজন" - }, "addMember": "সদস্য যোগ করুন", "noGroupCommentPlacheHolder": "বর্তমানে কোনও গ্রুপ মন্তব্য উপলব্ধ নেই।", "noGroupCommentPlacheHolder1": "আপনার সাইট থেকে এগুলি পেতে কোনও সমস্যা হতে পারে। রিফ্রেশ করার চেষ্টা করতে নীচে টানুন এবং আবার চেষ্টা করুন।", "noGroupCommentPlacheHolderOffline": "আপনি বর্তমানে অফলাইন আপনার গ্রুপ মন্তব্যটি রিফ্রেশ করার জন্য আপনি ওয়াইফাই বা মোবাইল ডেটাতে সংযুক্ত আছেন তা নিশ্চিত করুন।", "selectLocations": "অবস্থান নির্বাচন করুন", - "noMembersMessage": "noMembersMessage" + "noMembersMessage": "noMembersMessage", + "groupName": { + "label": "দলের নাম", + "error": "নাম প্রয়োজন" + } }, "notificationsScreen": { "notifications": "বিজ্ঞপ্তিগুলি", diff --git a/languages/en.json b/languages/en.json index 6d6fd184..dbd97a3f 100644 --- a/languages/en.json +++ b/languages/en.json @@ -19,17 +19,18 @@ "cancel": "Cancel", "save": "Save", "online": "Online", + "offline": "offline", "language": "Language", "search": "Search", "membersActivity": "Members", - "offline": "offline", "moreFields": "More Fields", - "nameRequired": "Name required" + "nameRequired": "Name required", + "close": "Close" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "E.g. myteam.disciple.tools", + "placeholder": "E.g. myteam.mydomain.com", "error": "Domain is required", "errorForgotPass": "Please enter URL to start to retrieve your password" }, @@ -47,7 +48,7 @@ "invalidUsername": "Unknown username. Check again or try your email address.", "incorrectPassword": "The password you entered for username is incorrect" }, - "ok": "Ok", + "ok": "OK", "appRestart": "The application will be restarted to apply the following configuration:", "textDirection": "Text direction" }, @@ -64,15 +65,23 @@ "storybook": "Storybook", "logout": "Sign Out", "helpSupport": "Help / Support", - "rememberPassword": "Keep logged in", + "rememberPassword": "Remember Password", "rememberPasswordActive": "Keep logged in enabled", - "rememberPasswordInactive": "Keep logged in disabled" + "rememberPasswordInactive": "Keep logged in disabled", + "pinCode": { + "remove": "Remove PIN code", + "set": "Set PIN code", + "enter": "Enter PIN code", + "incorrect": "Incorrect PIN code", + "saved": "PIN code saved succesfully!", + "removed": "PIN code removed succesfully!" + } }, "contactDetailScreen": { "addNewContact": "Add New Contact", "subAssignThisContact": "Sub-assign this contact", "mobile": "Phone", - "selectLocations": "Select locations", + "selectLocations": "Select Locations", "selectSources": "Select sources", "connections": "Connections", "addGroup": "Add Group", @@ -81,14 +90,14 @@ "addBaptized": "Add baptized", "addCoachedBy": "Add coached by", "addCoaching": "Add coaching", - "fullName": { - "label": "Full Name", - "error": "Name is required" - }, "phoneNumber": "Phone Number", "initialComment": "Initial Comment", "socialMedia": "Social Media", "noContactCommentPlacheHolder": "There are not any contact comments currently available.", + "fullName": { + "label": "Full Name", + "error": "Name is required" + }, "noContactCommentPlacheHolder1": "There may have been a problem getting them from your site. Drag down to try to refresh and try again.", "noContactCommentPlacheHolderOffline": "You are currently offline. Ensure you are connected to wifi or mobile data in order to refresh and get your contact comment." }, @@ -99,16 +108,16 @@ "searchPeerGroups": "Search peer groups", "childGroup": "Child Group", "searchChildGroups": "Search child groups", - "groupName": { - "label": "Group Name", - "error": "Name is required" - }, "addMember": "Add Member", "noGroupCommentPlacheHolder": "There are not any group comments currently available.", "noGroupCommentPlacheHolder1": "There may have been a problem getting them from your site. Drag down to try to refresh and try again.", "noGroupCommentPlacheHolderOffline": "You are currently offline. Ensure you are connected to wifi or mobile data in order to refresh and get your group comment.", "selectLocations": "Select Locations", - "noMembersMessage": "No members. Tap here or on Edit to add some members to this group." + "noMembersMessage": "No members. Tap here or on Edit to add some members to this group.", + "groupName": { + "label": "Group Name", + "error": "Name is required" + } }, "notificationsScreen": { "notifications": "Notifications", diff --git a/languages/es.json b/languages/es.json index 9c22713d..fe6b1d0b 100644 --- a/languages/es.json +++ b/languages/es.json @@ -19,17 +19,18 @@ "cancel": "Cancelar", "save": "Guardar", "online": "En línea", + "offline": "Desconectado", "language": "Idioma", "search": "Buscar", "membersActivity": "Miembros", - "offline": "Desconectado", "moreFields": "Más Campos", - "nameRequired": "Nombre requerido" + "nameRequired": "Nombre requerido", + "close": "Cerrar" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "P.ej. myteam.disciple.tools", + "placeholder": "P.ej. myteam.mydomain.com", "error": "El dominio es requerido", "errorForgotPass": "Ingrese la URL para comenzar a recuperar su contraseña" }, @@ -47,8 +48,8 @@ "invalidUsername": "Nombre de usuario desconocido. Vuelve a consultar o prueba tu dirección de correo electrónico.", "incorrectPassword": "La contraseña que ingresó para el usuario es incorrecta" }, - "ok": "Muy bien", - "appRestart": "La aplicación sera reiniciada para aplicar la siguiente configuración:", + "ok": "Okay", + "appRestart": "La aplicación se reiniciará para aplicar la siguiente configuración:", "textDirection": "Dirección del texto" }, "contactsScreen": { @@ -66,7 +67,15 @@ "helpSupport": "Ayuda / Soporte", "rememberPassword": "Mantener iniciada la sesión", "rememberPasswordActive": "Mantener iniciada la sesión activo", - "rememberPasswordInactive": "Mantener iniciada la sesión inactivo" + "rememberPasswordInactive": "Mantener iniciada la sesión inactivo", + "pinCode": { + "remove": "Eliminar código PIN", + "set": "Establecer código PIN", + "enter": "Ingrese el código PIN", + "incorrect": "Código PIN incorrecto", + "saved": "¡Código PIN guardado con éxito!", + "removed": "¡Código PIN eliminado con éxito!" + } }, "contactDetailScreen": { "addNewContact": "Crear contacto", @@ -81,16 +90,16 @@ "addBaptized": "Buscar multiplicadores y contactos", "addCoachedBy": "Buscar multiplicadores y contactos", "addCoaching": "Buscar multiplicadores y contactos", - "fullName": { - "label": "Nombre", - "error": "El nombre es requerido" - }, "phoneNumber": "Telefono", "initialComment": "Primer Comentario", "socialMedia": "Redes Sociales", "noContactCommentPlacheHolder": "No hay comentarios de contacto disponibles actualmente.", + "fullName": { + "label": "Nombre", + "error": "El nombre es requerido" + }, "noContactCommentPlacheHolder1": "Es posible que haya habido un problema para obtenerlos de su sitio. Arrastre hacia abajo para intentar actualizar e intente nuevamente.", - "noContactCommentPlacheHolderOffline": "Actualmente estás desconectado. Asegúrese de estar conectado a wifi o datos móviles para actualizar y obtener los comentarios del contacto." + "noContactCommentPlacheHolderOffline": "Actualmente estás desconectado. Asegúrese de estar conectado a wifi o datos móviles para actualizar y obtener su comentario de contacto." }, "groupDetailScreen": { "addNewGroup": "Crear grupo", @@ -99,16 +108,16 @@ "searchPeerGroups": "Buscar grupos paritarios", "childGroup": "Grupos Subsiguientes", "searchChildGroups": "Buscar grupos subsiguientes", - "groupName": { - "label": "Nombre del Grupo", - "error": "El nombre es requerido" - }, "addMember": "Añadir miembro", "noGroupCommentPlacheHolder": "No hay comentarios del grupo disponibles actualmente.", "noGroupCommentPlacheHolder1": "Es posible que haya habido un problema para obtenerlos de su sitio. Arrastre hacia abajo para intentar actualizar e intente nuevamente.", "noGroupCommentPlacheHolderOffline": "Actualmente estás desconectado. Asegúrese de estar conectado a wifi o datos móviles para actualizar y obtener los comentarios del grupo.", "selectLocations": "Seleccionar ubicaciones", - "noMembersMessage": "No hay miembros Toque aquí o en Editar para agregar algunos miembros a este grupo" + "noMembersMessage": "No hay miembros Toque aquí o en Editar para agregar algunos miembros a este grupo", + "groupName": { + "label": "Nombre del Grupo", + "error": "El nombre es requerido" + } }, "notificationsScreen": { "notifications": "Notificaciones", diff --git a/languages/fa.json b/languages/fa.json index c14855e7..0b993b49 100644 --- a/languages/fa.json +++ b/languages/fa.json @@ -24,12 +24,13 @@ "search": "جستجو کردن", "membersActivity": "اعضا", "moreFields": "زمینه های بیشتر", - "nameRequired": "نام لازم است" + "nameRequired": "نام لازم است", + "close": "نزدیک" }, "loginScreen": { "domain": { "label": "آدرس اینترنتی", - "placeholder": "به عنوان مثال. myteam.disciple.tools", + "placeholder": "به عنوان مثال. myteam.mydomain.com", "error": "دامنه مورد نیاز است", "errorForgotPass": "لطفاً URL را وارد کنید تا بازیابی رمز عبور خود را شروع کنید" }, @@ -47,9 +48,9 @@ "invalidUsername": "نام کاربری ناشناخته دوباره بررسی کنید یا آدرس ایمیل خود را امتحان کنید.", "incorrectPassword": "گذرواژه‌ای که برای نام کاربری خود وارد کردید اشتباه است" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "خوب", + "appRestart": "برنامه برای اجرای پیکربندی زیر مجدداً راه اندازی می شود:", + "textDirection": "جهت متن" }, "contactsScreen": { "contacts": "مخاطب", @@ -66,7 +67,15 @@ "helpSupport": "راهنما / پشتیبانی", "rememberPassword": "به یاد داشته باشید کلمه کلیدی", "rememberPasswordActive": "فعال شوید", - "rememberPasswordInactive": "وارد سیستم غیرفعال شوید" + "rememberPasswordInactive": "وارد سیستم غیرفعال شوید", + "pinCode": { + "remove": "کد پین را حذف کنید", + "set": "کد پین را تنظیم کنید", + "enter": "کد پین را وارد کنید", + "incorrect": "کد پین نادرست", + "saved": "کد پین با موفقیت ذخیره شد!", + "removed": "کد پین با موفقیت حذف شد!" + } }, "contactDetailScreen": { "addNewContact": "مخاطب جدید اضافه کنید", @@ -81,16 +90,16 @@ "addBaptized": "تعمید اضافه کنید", "addCoachedBy": "مربی اضافه شده توسط", "addCoaching": "مربیگری را اضافه کنید", - "fullName": { - "label": "نام و نام خانوادگی", - "error": "نام لازم است" - }, "phoneNumber": "شماره تلفن", "initialComment": "نظر اولیه", "socialMedia": "رسانه های اجتماعی", "noContactCommentPlacheHolder": "در حال حاضر هیچ گونه تماسی در دسترس نیست.", + "fullName": { + "label": "نام و نام خانوادگی", + "error": "نام لازم است" + }, "noContactCommentPlacheHolder1": "ممکن است مشکلی در گرفتن آنها از سایت شما وجود داشته باشد. پایین بکشید تا سعی کنید تازه کنید و دوباره امتحان کنید.", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "شما در حال حاضر آفلاین هستید. اطمینان حاصل کنید که برای تازه کردن و گرفتن نظر مخاطب خود به داده های wifi یا تلفن همراه وصل شده اید." }, "groupDetailScreen": { "addNewGroup": "گروه جدید را اضافه کنید", @@ -99,16 +108,16 @@ "searchPeerGroups": "جستجوی گروه های همتا", "childGroup": "گروه کودک", "searchChildGroups": "گروه های کودک را جستجو کنید", - "groupName": { - "label": "اسم گروه", - "error": "نام لازم است" - }, "addMember": "عضو را اضافه کنید", "noGroupCommentPlacheHolder": "هیچ نظر گروهی در حال حاضر موجود نیست", "noGroupCommentPlacheHolder1": "ممکن است مشکلی در گرفتن آنها از سایت شما وجود داشته باشد. پایین بکشید تا سعی کنید تازه کنید و دوباره امتحان کنید.", "noGroupCommentPlacheHolderOffline": "شما در حال حاضر آفلاین هستید. اطمینان حاصل کنید که به منظور تازه کردن و اظهار نظر گروه خود ، به داده های wifi یا تلفن همراه وصل شده اید.", "selectLocations": "مکان ها را انتخاب کنید", - "noMembersMessage": "هیچ عضو برای افزودن برخی از اعضا به این گروه ، اینجا یا بر روی ویرایش ضربه بزنید." + "noMembersMessage": "هیچ عضو برای افزودن برخی از اعضا به این گروه ، اینجا یا بر روی ویرایش ضربه بزنید.", + "groupName": { + "label": "اسم گروه", + "error": "نام لازم است" + } }, "notificationsScreen": { "notifications": "اطلاعیه", diff --git a/languages/fr.json b/languages/fr.json index 4ec4cee9..1c681051 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -24,12 +24,13 @@ "search": "Chercher", "membersActivity": "Membres", "moreFields": "Plus de champs", - "nameRequired": "Nom obligatoire" + "nameRequired": "Nom obligatoire", + "close": "proche" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Par exemple. myteam.disciple.tools", + "placeholder": "Par exemple. myteam.mydomain.com", "error": "Le domaine est requis", "errorForgotPass": "Veuillez saisir l'URL pour commencer à récupérer votre mot de passe" }, @@ -47,9 +48,9 @@ "invalidUsername": "Nom d'utilisateur inconnu. Vérifiez à nouveau ou essayez votre adresse e-mail.", "incorrectPassword": "Le mot de passe que vous avez entré pour le nom d'utilisateur est incorrect" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "D'accord", + "appRestart": "L'application sera redémarrée pour appliquer la configuration suivante:", + "textDirection": "Direction du texte" }, "contactsScreen": { "contacts": "Contacts", @@ -66,7 +67,15 @@ "helpSupport": "Aide", "rememberPassword": "se souvenir du mot de passe", "rememberPasswordActive": "Rester connecté activé", - "rememberPasswordInactive": "Rester connecté désactivé" + "rememberPasswordInactive": "Rester connecté désactivé", + "pinCode": { + "remove": "Supprimer le code PIN", + "set": "Définir le code PIN", + "enter": "Entrez le code PIN", + "incorrect": "Code PIN incorrect", + "saved": "Le code PIN a été enregistré avec succès!", + "removed": "Le code PIN a été supprimé avec succès!" + } }, "contactDetailScreen": { "addNewContact": "Ajouter un nouveau contact", @@ -81,16 +90,16 @@ "addBaptized": "Ajouter baptisé", "addCoachedBy": "Ajouter coaché par", "addCoaching": "Ajouter un coaching", - "fullName": { - "label": "Nom complet", - "error": "Nom obligatoire" - }, "phoneNumber": "Numéro de téléphone", "initialComment": "Commentaire initial", "socialMedia": "Des médias sociaux", "noContactCommentPlacheHolder": "Aucun commentaire de contact n'est actuellement disponible.", + "fullName": { + "label": "Nom complet", + "error": "Nom obligatoire" + }, "noContactCommentPlacheHolder1": "Il peut y avoir eu un problème pour les obtenir à partir de votre site. Faites glisser vers le bas pour essayer de rafraîchir et réessayez.", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "Vous êtes actuellement hors ligne. Assurez-vous que vous êtes connecté au wifi ou aux données mobiles afin de vous rafraîchir et d'obtenir votre commentaire de contact." }, "groupDetailScreen": { "addNewGroup": "Ajouter un nouveau groupe", @@ -99,16 +108,16 @@ "searchPeerGroups": "Rechercher des groupes de pairs", "childGroup": "Groupe d'enfants", "searchChildGroups": "Rechercher des groupes d'enfants", - "groupName": { - "label": "Nom de groupe", - "error": "Nom obligatoire" - }, "addMember": "Ajouter un membre", "noGroupCommentPlacheHolder": "Aucun commentaire de groupe n'est actuellement disponible.", "noGroupCommentPlacheHolder1": "Il peut y avoir eu un problème pour les obtenir à partir de votre site. Faites glisser vers le bas pour essayer de rafraîchir et réessayez.", "noGroupCommentPlacheHolderOffline": "Vous êtes actuellement hors ligne. Assurez-vous que vous êtes connecté au wifi ou aux données mobiles afin de vous rafraîchir et d'obtenir les commentaires de votre groupe.", "selectLocations": "Sélectionner des emplacements", - "noMembersMessage": "Aucun membre. Appuyez ici ou sur Modifier pour ajouter des membres à ce groupe." + "noMembersMessage": "Aucun membre. Appuyez ici ou sur Modifier pour ajouter des membres à ce groupe.", + "groupName": { + "label": "Nom de groupe", + "error": "Nom obligatoire" + } }, "notificationsScreen": { "notifications": "Notifications", diff --git a/languages/id.json b/languages/id.json index 90a9108a..7c981d29 100644 --- a/languages/id.json +++ b/languages/id.json @@ -24,12 +24,13 @@ "search": "Pencarian", "membersActivity": "Anggota", "moreFields": "Lebih banyak bidang", - "nameRequired": "Nama (wajib" + "nameRequired": "Nama (wajib", + "close": "Menutup" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Misalnya. myteam.disciple.tools", + "placeholder": "Misalnya. myteam.mydomain.com", "error": "Diperlukan domain", "errorForgotPass": "Silakan masukkan URL untuk mulai mengambil kata sandi Anda" }, @@ -47,9 +48,9 @@ "invalidUsername": "Nama pengguna tidak dikenal. Periksa lagi atau coba alamat email Anda.", "incorrectPassword": "Kata sandi yang Anda masukkan untuk nama pengguna salah" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "baik", + "appRestart": "Aplikasi akan dihidupkan ulang untuk menerapkan konfigurasi berikut:", + "textDirection": "Arah teks" }, "contactsScreen": { "contacts": "Kontak", @@ -66,7 +67,15 @@ "helpSupport": "Bantuan / Dukungan", "rememberPassword": "ingat kata Sandi", "rememberPasswordActive": "Tetap masuk log diaktifkan", - "rememberPasswordInactive": "Tetap masuk log dinonaktifkan" + "rememberPasswordInactive": "Tetap masuk log dinonaktifkan", + "pinCode": { + "remove": "Hapus kode PIN", + "set": "Setel kode PIN", + "enter": "Masukkan kode PIN", + "incorrect": "Kode PIN salah", + "saved": "Kode PIN berhasil disimpan!", + "removed": "Kode PIN berhasil dihapus!" + } }, "contactDetailScreen": { "addNewContact": "Tambahkan Kontak Baru", @@ -81,15 +90,16 @@ "addBaptized": "Tambahkan yang dibaptis", "addCoachedBy": "Tambahkan yang dilatih oleh", "addCoaching": "Tambahkan pelatihan", - "fullName": { - "label": "Nama lengkap", - "error": "Nama harus diisi" - }, "phoneNumber": "Nomor telepon", "initialComment": "Komentar awal", "socialMedia": "Media sosial", "noContactCommentPlacheHolder": "Tidak ada komentar kontak yang tersedia saat ini.", - "noContactCommentPlacheHolder1": "Mungkin ada masalah saat mendapatkannya dari situs Anda. Seret ke bawah untuk mencoba menyegarkan dan coba lagi." + "fullName": { + "label": "Nama lengkap", + "error": "Nama harus diisi" + }, + "noContactCommentPlacheHolder1": "Mungkin ada masalah saat mendapatkannya dari situs Anda. Seret ke bawah untuk mencoba menyegarkan dan coba lagi.", + "noContactCommentPlacheHolderOffline": "Anda sedang offline. Pastikan Anda terhubung ke wifi atau data seluler untuk menyegarkan dan mendapatkan komentar kontak Anda." }, "groupDetailScreen": { "addNewGroup": "Tambahkan Grup Baru", @@ -98,16 +108,16 @@ "searchPeerGroups": "Cari grup sebaya", "childGroup": "Kelompok Anak", "searchChildGroups": "Cari grup anak", - "groupName": { - "label": "Nama grup", - "error": "Nama harus diisi" - }, "addMember": "Tambahkan anggota", "noGroupCommentPlacheHolder": "Tidak ada komentar grup yang tersedia saat ini.", "noGroupCommentPlacheHolder1": "Mungkin ada masalah saat mendapatkannya dari situs Anda. Seret ke bawah untuk mencoba menyegarkan dan coba lagi.", "noGroupCommentPlacheHolderOffline": "Anda sedang offline. Pastikan Anda terhubung ke wifi atau data seluler untuk menyegarkan dan mendapatkan komentar grup Anda.", "selectLocations": "Pilih Lokasi", - "noMembersMessage": "noMembersMessage" + "noMembersMessage": "noMembersMessage", + "groupName": { + "label": "Nama grup", + "error": "Nama harus diisi" + } }, "notificationsScreen": { "notifications": "Notifikasi", diff --git a/languages/nl.json b/languages/nl.json index 260bfe55..edb12c7e 100644 --- a/languages/nl.json +++ b/languages/nl.json @@ -24,12 +24,13 @@ "search": "Zoeken", "membersActivity": "leden", "moreFields": "Meer velden", - "nameRequired": "Naam (vereist" + "nameRequired": "Naam (vereist", + "close": "Dichtbij" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Bijv. myteam.disciple.tools", + "placeholder": "Bijv. myteam.mydomain.com", "error": "Domein is verplicht", "errorForgotPass": "Voer de URL in om te beginnen met het ophalen van uw wachtwoord" }, @@ -47,9 +48,9 @@ "invalidUsername": "Onbekende gebruikersnaam. Controleer het opnieuw of probeer uw e-mailadres.", "incorrectPassword": "Het wachtwoord dat u hebt ingevoerd voor de gebruikersnaam is onjuist" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "OK", + "appRestart": "De applicatie wordt opnieuw opgestart om de volgende configuratie toe te passen:", + "textDirection": "Tekstrichting" }, "contactsScreen": { "contacts": "Contacten", @@ -66,7 +67,15 @@ "helpSupport": "Help & Ondersteuning", "rememberPassword": "Onthoud wachtwoord", "rememberPasswordActive": "Blijf ingelogd ingeschakeld", - "rememberPasswordInactive": "Blijf ingelogd uitgeschakeld" + "rememberPasswordInactive": "Blijf ingelogd uitgeschakeld", + "pinCode": { + "remove": "PIN-code verwijderen", + "set": "Pincode instellen", + "enter": "Voer pincode in", + "incorrect": "Onjuiste pincode", + "saved": "PIN-code succesvol opgeslagen!", + "removed": "PIN-code succesvol verwijderd!" + } }, "contactDetailScreen": { "addNewContact": "Voeg nieuw contact toe", @@ -81,15 +90,16 @@ "addBaptized": "Gedoopt toevoegen", "addCoachedBy": "Gecoacht door toevoegen", "addCoaching": "Coaching toevoegen", - "fullName": { - "label": "Voor-en achternaam", - "error": "Naam is vereist" - }, "phoneNumber": "Telefoonnummer", "initialComment": "Eerste opmerking", "socialMedia": "Sociale media", "noContactCommentPlacheHolder": "Er zijn momenteel geen contactreacties beschikbaar.", - "noContactCommentPlacheHolder1": "Mogelijk is er een probleem opgetreden bij het ophalen van uw site. Sleep naar beneden om te proberen te vernieuwen en probeer het opnieuw." + "fullName": { + "label": "Voor-en achternaam", + "error": "Naam is vereist" + }, + "noContactCommentPlacheHolder1": "Mogelijk is er een probleem opgetreden bij het ophalen van uw site. Sleep naar beneden om te proberen te vernieuwen en probeer het opnieuw.", + "noContactCommentPlacheHolderOffline": "Je bent momenteel offline. Zorg ervoor dat je verbonden bent met wifi of mobiele data om te vernieuwen en je contactcommentaar te krijgen." }, "groupDetailScreen": { "addNewGroup": "Nieuwe groep toevoegen", @@ -98,16 +108,16 @@ "searchPeerGroups": "Zoek naar peergroepen", "childGroup": "Kind groep", "searchChildGroups": "Zoek kindgroepen", - "groupName": { - "label": "Groepsnaam", - "error": "Naam is vereist" - }, "addMember": "Lid toevoegen", "noGroupCommentPlacheHolder": "Er zijn momenteel geen groepsreacties beschikbaar.", "noGroupCommentPlacheHolder1": "Er is mogelijk een probleem opgetreden bij het ophalen van uw site. Sleep omlaag om te proberen te vernieuwen en probeer het opnieuw.", "noGroupCommentPlacheHolderOffline": "U bent momenteel offline. Zorg ervoor dat je bent verbonden met wifi of mobiele gegevens om te vernieuwen en je groepsreactie te ontvangen.", "selectLocations": "Selecteer locaties", - "noMembersMessage": "Geen leden" + "noMembersMessage": "Geen leden", + "groupName": { + "label": "Groepsnaam", + "error": "Naam is vereist" + } }, "notificationsScreen": { "notifications": "meldingen", diff --git a/languages/pt.json b/languages/pt.json index d7dc4586..34f15054 100644 --- a/languages/pt.json +++ b/languages/pt.json @@ -24,12 +24,13 @@ "search": "Procurar", "membersActivity": "Membros", "moreFields": "Mais campos", - "nameRequired": "Nome necessário" + "nameRequired": "Nome necessário", + "close": "Fechar" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Exemplo myteam.disciple.tools", + "placeholder": "Exemplo myteam.mydomain.com", "error": "É necessário o domínio", "errorForgotPass": "Favor digitar a URL para começar a recuperação de senha" }, @@ -47,9 +48,9 @@ "invalidUsername": "Nome de Usuário desconhecido. Verifique novamente ou tente seu endereço de e-mail", "incorrectPassword": "A senha que você digitou para o usuário está incorreta" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "Está bem", + "appRestart": "O aplicativo será reiniciado para aplicar a seguinte configuração:", + "textDirection": "Direção do texto" }, "contactsScreen": { "contacts": "Contatos", @@ -66,7 +67,15 @@ "helpSupport": "Ajuda / Suporte", "rememberPassword": "Lembrar senha", "rememberPasswordActive": "Manter-se logado está ligado", - "rememberPasswordInactive": "Manter-se logado está desligado" + "rememberPasswordInactive": "Manter-se logado está desligado", + "pinCode": { + "remove": "Remover código PIN", + "set": "Definir código PIN", + "enter": "Digite o código PIN", + "incorrect": "Código PIN incorreto", + "saved": "Código PIN salvo com sucesso!", + "removed": "Código PIN removido com sucesso!" + } }, "contactDetailScreen": { "addNewContact": "Adicionar novo contato", @@ -81,16 +90,16 @@ "addBaptized": "Adicionar batizado", "addCoachedBy": "Adicionar recebendo coach de", "addCoaching": "Adicionar Coaching", - "fullName": { - "label": "Nome completo", - "error": "Nome é necessário" - }, "phoneNumber": "Telefone", "initialComment": "Comentário Inicial", "socialMedia": "Mídia Social", "noContactCommentPlacheHolder": "Não há comentários de contatos disponíveis atualmente", - "noContactCommentPlacheHolder1": "Pode haver um problema ao removê-los do seu local. Arraste-os para tentar atualizar e tente novamente.", - "noContactCommentPlacheHolderOffline": "" + "fullName": { + "label": "Nome completo", + "error": "Nome é necessário" + }, + "noContactCommentPlacheHolder1": "Pode haver um problema ao removê-los do seu local. Arraste-os para tentar atualizar e tente novamente. ", + "noContactCommentPlacheHolderOffline": "Você está atualmente offline. Verifique se você está conectado a dados móveis ou Wi-Fi para atualizar e obter seu comentário de contato." }, "groupDetailScreen": { "addNewGroup": "Adicionar novo grupo", @@ -99,16 +108,16 @@ "searchPeerGroups": "Buscar grupos de parceiros", "childGroup": "Grupo de crianças", "searchChildGroups": "Buscar por grupos de crianças", - "groupName": { - "label": "Nome do Grupo", - "error": "Nome é necessário" - }, "addMember": "Adicionar Membro", "noGroupCommentPlacheHolder": "Não há comentários de grupos disponíveis atualmente.", "noGroupCommentPlacheHolder1": "Pode haver um problema ao removê-los do seu local. Arraste-os para tentar atualizar e tente novamente. ", "noGroupCommentPlacheHolderOffline": "Você está offline. Certifique-se de que está conectado à rede Wifi ou dados móveis para atualizar e obter seus comentários de grupos. ", "selectLocations": "Selecionar locais", - "noMembersMessage": "Não há membros. Clique aqui em Editar para adicionar membros a este grupo." + "noMembersMessage": "Não há membros. Clique aqui em Editar para adicionar membros a este grupo.", + "groupName": { + "label": "Nome do Grupo", + "error": "Nome é necessário" + } }, "notificationsScreen": { "notifications": "Notificações", diff --git a/languages/ru.json b/languages/ru.json index 1ff565fd..5b67f55a 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -24,12 +24,13 @@ "search": "Поиск", "membersActivity": "Члены", "moreFields": "Больше полей", - "nameRequired": "Имя (обязательно" + "nameRequired": "Имя обязательно", + "close": "близко" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Например: myteam.disciple.tools", + "placeholder": "Например: myteam.mydomain.com", "error": "Требуется домен", "errorForgotPass": "Пожалуйста, введите URL, чтобы начать восстановление пароля" }, @@ -47,9 +48,9 @@ "invalidUsername": "Неизвестное имя пользователя. Проверьте еще раз или попробуйте свой адрес электронной почты.", "incorrectPassword": "Неверный пароль для этого пользователя" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "Хорошо", + "appRestart": "Приложение будет перезапущено для применения следующей конфигурации:", + "textDirection": "Направление текста" }, "contactsScreen": { "contacts": "Контакты", @@ -65,8 +66,16 @@ "logout": "Выход", "helpSupport": "Помощь / Поддержка", "rememberPassword": "Запомнить пароль", - "rememberPasswordActive": "Оставайтесь в системе активным", - "rememberPasswordInactive": "Оставайтесь в системе отключенным" + "rememberPasswordActive": "Режим \"Оставаться в системе\" включен", + "rememberPasswordInactive": "Режим \"Оставаться в системе\" выключен", + "pinCode": { + "remove": "Удалить ПИН код", + "set": "Установить PIN-код", + "enter": "Введите пин код", + "incorrect": "Неверный PIN-код", + "saved": "ПИН-код успешно сохранен!", + "removed": "PIN-код успешно удален!" + } }, "contactDetailScreen": { "addNewContact": "Добавить новый контакт", @@ -81,16 +90,16 @@ "addBaptized": "Добавить крещеного", "addCoachedBy": "Добавить наставника", "addCoaching": "Добавить наставляемого", - "fullName": { - "label": "Полное имя", - "error": "Имя обязательно" - }, "phoneNumber": "Номер телефона", "initialComment": "Начальный комментарий", "socialMedia": "Социальные сети", "noContactCommentPlacheHolder": "В данное время никаких комментариев по контакту нет.", - "noContactCommentPlacheHolder1": "Возможно, возникла проблема при получении их с вашего сайта. Перетащите вниз, чтобы попытаться обновить и повторить попытку.", - "noContactCommentPlacheHolderOffline": "" + "fullName": { + "label": "Полное имя", + "error": "Имя обязательно" + }, + "noContactCommentPlacheHolder1": "Возможно, возникла проблема при получении их с вашего сайта. Попытайтесь еще раз (обновите экран, потянув его вниз).", + "noContactCommentPlacheHolderOffline": "Вы в настоящее время не в сети. Убедитесь, что вы подключены к Wi-Fi или мобильным данным, чтобы обновить и получить свой контактный комментарий." }, "groupDetailScreen": { "addNewGroup": "Добавить новую группу", @@ -99,16 +108,16 @@ "searchPeerGroups": "Поиск одноранговых (пир) групп", "childGroup": "Дочерняя группа", "searchChildGroups": "Поиск дочерних групп", - "groupName": { - "label": "Имя группы", - "error": "Имя обязательно" - }, "addMember": "Добавить участников", "noGroupCommentPlacheHolder": "В настоящее время нет групповых комментариев.", "noGroupCommentPlacheHolder1": "Возможно, возникла проблема при получении их с вашего сайта. Попытайтесь еще раз (обновите экран, потянув его вниз).", "noGroupCommentPlacheHolderOffline": "Вы в настоящее время не в сети. Убедитесь, что вы подключены к Wi-Fi или мобильным данным, чтобы обновить и получить комментарий группы.", "selectLocations": "Выберите места", - "noMembersMessage": "Нет участников. Нажмите здесь или на Изменить, чтобы добавить некоторых участников в эту группу." + "noMembersMessage": "Нет участников. Нажмите здесь или на Редактировать, чтобы добавить участников в эту группу.", + "groupName": { + "label": "Имя группы", + "error": "Имя обязательно" + } }, "notificationsScreen": { "notifications": "Уведомления", diff --git a/languages/sw.json b/languages/sw.json index f58320a3..d23cef1a 100644 --- a/languages/sw.json +++ b/languages/sw.json @@ -24,12 +24,13 @@ "search": "Tafuta", "membersActivity": "Wajumbe", "moreFields": "Mashamba Zaidi", - "nameRequired": "Jina linalohitajika" + "nameRequired": "Jina linalohitajika", + "close": "Karibu" }, "loginScreen": { "domain": { "label": "URL", - "placeholder": "Kwa mfano myteam.disciple.tools", + "placeholder": "Kwa mfano myteam.mydomain.com", "error": "Kikoa inahitajika", "errorForgotPass": "Tafadhali ingiza URL kuanza kupata nenosiri lako" }, @@ -47,9 +48,9 @@ "invalidUsername": "Jina la mtumiaji lisilojulikana. Angalia tena au jaribu anwani yako ya barua pepe.", "incorrectPassword": "Nenosiri uliloliingiza jina la mtumiaji sio sahihi" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "sawa", + "appRestart": "Maombi yataanza tena kutumia usanidi ufuatao:", + "textDirection": "Miongozo ya maandishi" }, "contactsScreen": { "contacts": "Anwani", @@ -66,7 +67,15 @@ "helpSupport": "Msaada / Msaada", "rememberPassword": "Kumbuka Nenosiri", "rememberPasswordActive": "Endelea kuwezeshwa", - "rememberPasswordInactive": "Endelea kuwa umelemazwa" + "rememberPasswordInactive": "Endelea kuwa umelemazwa", + "pinCode": { + "remove": "Ondoa msimbo wa Pini", + "set": "Weka nambari ya PIN", + "enter": "Ingiza msimbo wa pini", + "incorrect": "Nambari ya Pini isiyo sahihi", + "saved": "Nambari ya pini iliyohifadhiwa kikamilifu!", + "removed": "Nambari ya pini imeondolewa vyema!" + } }, "contactDetailScreen": { "addNewContact": "Ongeza Mawasiliano Mpya", @@ -81,15 +90,16 @@ "addBaptized": "Ongeza kubatizwa", "addCoachedBy": "Ongeza kilichofunzwa na", "addCoaching": "Ongeza kufundisha", - "fullName": { - "label": "Jina kamili", - "error": "Jina inahitajika" - }, "phoneNumber": "Nambari ya simu", "initialComment": "Maoni ya awali", "socialMedia": "Mtandao wa kijamii", "noContactCommentPlacheHolder": "Hakuna maoni yoyote ya mawasiliano yanayopatikana kwa sasa.", - "noContactCommentPlacheHolder1": "Kunaweza kuwa na shida kuipata kutoka kwa wavuti yako. Buruta chini kujaribu kujaribu kuburudisha na kujaribu tena." + "fullName": { + "label": "Jina kamili", + "error": "Jina inahitajika" + }, + "noContactCommentPlacheHolder1": "Kunaweza kuwa na shida kuipata kutoka kwa wavuti yako. Buruta chini kujaribu kujaribu kuburudisha na kujaribu tena.", + "noContactCommentPlacheHolderOffline": "Hivi sasa uko nje ya mkondo. Hakikisha umeunganishwa na wifi au data ya rununu ili kuburudisha na upate maoni yako ya mawasiliano." }, "groupDetailScreen": { "addNewGroup": "Ongeza Kundi Mpya", @@ -98,16 +108,16 @@ "searchPeerGroups": "Tafuta vikundi vya rika", "childGroup": "Kundi la watoto", "searchChildGroups": "Tafuta vikundi vya watoto", - "groupName": { - "label": "Jina la Kikundi", - "error": "Jina inahitajika" - }, "addMember": "Ongeza Mwanachama", "noGroupCommentPlacheHolder": "Hakuna maoni yoyote ya kikundi yanayopatikana kwa sasa.", "noGroupCommentPlacheHolder1": "Kunaweza kuwa na shida kuzipata kutoka kwa wavuti yako. Buruta chini kujaribu kujaribu kuburudisha na kujaribu tena.", "noGroupCommentPlacheHolderOffline": "Hivi sasa uko nje ya mkondo. Hakikisha umeunganishwa na wifi au data ya rununu ili kuburudisha na upate maoni ya kikundi chako.", "selectLocations": "Chagua Maeneo", - "noMembersMessage": "Hakuna Wajumbe" + "noMembersMessage": "Hakuna Wajumbe", + "groupName": { + "label": "Jina la Kikundi", + "error": "Jina inahitajika" + } }, "notificationsScreen": { "notifications": "Arifa", diff --git a/languages/tr.json b/languages/tr.json index 1c97c98f..afc59967 100644 --- a/languages/tr.json +++ b/languages/tr.json @@ -24,12 +24,13 @@ "search": "Ara", "membersActivity": "Üyeler", "moreFields": "Diğer Alanlar", - "nameRequired": "İsim (gerekli" + "nameRequired": "İsim (gerekli", + "close": "Kapat" }, "loginScreen": { "domain": { - "label": "Disciple Tools URL", - "placeholder": "Örnek: myteam.disciple.tools", + "label": "URL", + "placeholder": "Örnek: myteam.mydomain.com", "error": "Alan adı gerekli", "errorForgotPass": "Lütfen şifrenizi almaya başlamak için URL girin" }, @@ -47,9 +48,9 @@ "invalidUsername": "Bilinmeyen kullanıcı adı. Tekrar kontrol edin veya e-posta adresinizi deneyin.", "incorrectPassword": "Kullanıcı adı için girdiğiniz şifre yanlış" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "tamam", + "appRestart": "Aşağıdaki yapılandırmayı uygulamak için uygulama yeniden başlatılır:", + "textDirection": "Metin yönü" }, "contactsScreen": { "contacts": "Bağlantılarım", @@ -66,7 +67,15 @@ "helpSupport": "yardım / destek", "rememberPassword": "şifre hatırlamak", "rememberPasswordActive": "Oturum açmayı etkin tut", - "rememberPasswordInactive": "Giriş yapmayı devre dışı bırak" + "rememberPasswordInactive": "Giriş yapmayı devre dışı bırak", + "pinCode": { + "remove": "PIN kodunu kaldır", + "set": "PIN kodunu ayarla", + "enter": "PIN kodunu girin", + "incorrect": "Yanlış PIN kodu", + "saved": "PIN kodu başarıyla kaydedildi!", + "removed": "PIN kodu başarıyla kaldırıldı!" + } }, "contactDetailScreen": { "addNewContact": "Yeni bağlantı ekle", @@ -81,16 +90,16 @@ "addBaptized": "Add baptized", "addCoachedBy": "Add coached by", "addCoaching": "Koçluk ekle", - "fullName": { - "label": "Ad Soyad", - "error": "İsim gerekli" - }, "phoneNumber": "Telefon Numarası", "initialComment": "İlk Yorum", "socialMedia": "Sosyal Medya", "noContactCommentPlacheHolder": "Şu anda kullanılabilecek kişi yorumu yok.", + "fullName": { + "label": "Ad Soyad", + "error": "İsim gerekli" + }, "noContactCommentPlacheHolder1": "Bunları sitenizden alırken bir sorun olabilir. Yenilemeyi denemek için aşağı sürükleyin ve tekrar deneyin.", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "Şu anda çevrimdışısınız. İrtibat yorumunuzu yenilemek ve almak için wifi veya mobil verilere bağlı olduğunuzdan emin olun." }, "groupDetailScreen": { "addNewGroup": "Grup Ekle", @@ -99,16 +108,16 @@ "searchPeerGroups": "Eş gruplarda ara", "childGroup": "Alt Grup", "searchChildGroups": "Alt gruplarda ara", - "groupName": { - "label": "Grup ismi", - "error": "İsim gerekli" - }, "addMember": "Üye Ekle", "noGroupCommentPlacheHolder": "Şu anda hiçbir grup yorumu mevcut değil.", "noGroupCommentPlacheHolder1": "Bunları sitenizden alırken bir sorun olabilir. Yenilemeyi denemek için aşağı sürükleyin ve tekrar deneyin.", "noGroupCommentPlacheHolderOffline": "Şu anda çevrimdışısınız. Grup yorumunuzu yenilemek ve almak için kablosuz bağlantıya veya mobil verilere bağlı olduğunuzdan emin olun.", "selectLocations": "Konumları Seçin", - "noMembersMessage": "Üye yok. Bu gruba bazı üyeler eklemek için burayı veya Değiştir'i tıklayın." + "noMembersMessage": "Üye yok. Bu gruba bazı üyeler eklemek için burayı veya Değiştir'i tıklayın.", + "groupName": { + "label": "Grup ismi", + "error": "İsim gerekli" + } }, "notificationsScreen": { "notifications": "Bildirim", diff --git a/languages/zhCn.json b/languages/zhCn.json index 83eaa600..a0b008db 100644 --- a/languages/zhCn.json +++ b/languages/zhCn.json @@ -24,12 +24,13 @@ "search": "搜索", "membersActivity": "会员", "moreFields": "更多领域", - "nameRequired": "姓名必填" + "nameRequired": "姓名必填", + "close": "关" }, "loginScreen": { "domain": { "label": "网址", - "placeholder": "例如。 myteam.disciple.tools", + "placeholder": "例如。 myteam.mydomain.com", "error": "域是必填项", "errorForgotPass": "请输入URL以开始检索密码" }, @@ -47,9 +48,9 @@ "invalidUsername": "未知的用户名。再次检查或尝试您的电子邮件地址。", "incorrectPassword": "您输入的用户名密码不正确" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "好", + "appRestart": "该应用程序将重新启动以应用以下配置:", + "textDirection": "文字方向" }, "contactsScreen": { "contacts": "联络人", @@ -66,7 +67,15 @@ "helpSupport": "帮助支持", "rememberPassword": "记住密码", "rememberPasswordActive": "保持登录状态", - "rememberPasswordInactive": "保持登录状态已禁用" + "rememberPasswordInactive": "保持登录状态已禁用", + "pinCode": { + "remove": "删除PIN码", + "set": "设置PIN码", + "enter": "输入PIN码", + "incorrect": "PIN码不正确", + "saved": "PIN码已成功保存!", + "removed": "PIN码已成功删除!" + } }, "contactDetailScreen": { "addNewContact": "新增联络人", @@ -81,16 +90,16 @@ "addBaptized": "添加受洗", "addCoachedBy": "添加教练", "addCoaching": "添加辅导", - "fullName": { - "label": "全名", - "error": "名称为必填项" - }, "phoneNumber": "电话号码", "initialComment": "初步评论", "socialMedia": "社交媒体", "noContactCommentPlacheHolder": "当前没有任何联系方式。", + "fullName": { + "label": "全名", + "error": "名称为必填项" + }, "noContactCommentPlacheHolder1": "从您的网站获取它们可能存在问题。向下拖动以尝试刷新,然后重试。", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "您目前离线。确保您已连接到wifi或移动数据,以便刷新并获取联系评论。" }, "groupDetailScreen": { "addNewGroup": "新增群组", @@ -99,16 +108,16 @@ "searchPeerGroups": "搜索同龄人组", "childGroup": "儿童组", "searchChildGroups": "搜索子组", - "groupName": { - "label": "组的名字", - "error": "名称为必填项" - }, "addMember": "添加会员", "noGroupCommentPlacheHolder": "当前没有任何组注释。", "noGroupCommentPlacheHolder1": "从您的网站获取它们可能存在问题。向下拖动以尝试刷新,然后重试。", "noGroupCommentPlacheHolderOffline": "您目前离线。确保您已连接到wifi或移动数据,以便刷新并获取群组评论。", "selectLocations": "选择地点", - "noMembersMessage": "没有成员。 单击此处或单击“更改”以将一些成员添加到该组。" + "noMembersMessage": "没有成员。 单击此处或单击“更改”以将一些成员添加到该组。", + "groupName": { + "label": "组的名字", + "error": "名称为必填项" + } }, "notificationsScreen": { "notifications": "通知事项", diff --git a/languages/zhTw.json b/languages/zhTw.json index 64fe02b5..72fdb0ff 100644 --- a/languages/zhTw.json +++ b/languages/zhTw.json @@ -24,12 +24,13 @@ "search": "搜索", "membersActivity": "會員", "moreFields": "更多領域", - "nameRequired": "姓名必填" + "nameRequired": "姓名必填", + "close": "關" }, "loginScreen": { "domain": { "label": "網址", - "placeholder": "例如。 myteam.disciple.tools", + "placeholder": "例如。 myteam.mydomain.com", "error": "域是必填項", "errorForgotPass": "請輸入URL以開始檢索密碼" }, @@ -47,9 +48,9 @@ "invalidUsername": "未知的用戶名。再次檢查或嘗試您的電子郵件地址。", "incorrectPassword": "您輸入的用戶名密碼不正確" }, - "ok": "", - "appRestart": "", - "textDirection": "" + "ok": "好", + "appRestart": "該應用程序將重新啟動以應用以下配置:", + "textDirection": "文字方向" }, "contactsScreen": { "contacts": "聯絡人", @@ -66,7 +67,15 @@ "helpSupport": "幫助支持", "rememberPassword": "記住密碼", "rememberPasswordActive": "保持登錄狀態", - "rememberPasswordInactive": "保持登錄狀態已禁用" + "rememberPasswordInactive": "保持登錄狀態已禁用", + "pinCode": { + "remove": "刪除PIN碼", + "set": "設置PIN碼", + "enter": "輸入PIN碼", + "incorrect": "PIN碼不正確", + "saved": "PIN碼已成功保存!", + "removed": "PIN碼已成功刪除!" + } }, "contactDetailScreen": { "addNewContact": "新增聯絡人", @@ -81,16 +90,16 @@ "addBaptized": "添加受洗", "addCoachedBy": "添加教練", "addCoaching": "添加教練", - "fullName": { - "label": "全名", - "error": "名稱為必填項" - }, "phoneNumber": "電話號碼", "initialComment": "初步評論", "socialMedia": "社交媒體", "noContactCommentPlacheHolder": "當前沒有任何联系方式。", + "fullName": { + "label": "全名", + "error": "名稱為必填項" + }, "noContactCommentPlacheHolder1": "從您的網站獲取它們可能存在問題。向下拖動以嘗試刷新,然後重試。", - "noContactCommentPlacheHolderOffline": "" + "noContactCommentPlacheHolderOffline": "您目前離線。確保您已連接到wifi或移動數據,以便刷新並獲取聯繫評論。" }, "groupDetailScreen": { "addNewGroup": "新增群組", @@ -99,16 +108,16 @@ "searchPeerGroups": "搜索同齡人組", "childGroup": "兒童組", "searchChildGroups": "搜索子組", - "groupName": { - "label": "組的名字", - "error": "名稱為必填項" - }, "addMember": "新增會員", "noGroupCommentPlacheHolder": "當前沒有任何組註釋。", "noGroupCommentPlacheHolder1": "從您的網站獲取它們可能存在問題。向下拖動以嘗試刷新,然後重試。", "noGroupCommentPlacheHolderOffline": "您目前離線。確保您已連接到wifi或移動數據,以便刷新並獲取群組評論。", "selectLocations": "選擇地點", - "noMembersMessage": "沒有成員。 單擊此處或單擊“更改”以將一些成員添加到該組。" + "noMembersMessage": "沒有成員。 單擊此處或單擊“更改”以將一些成員添加到該組。", + "groupName": { + "label": "組的名字", + "error": "名稱為必填項" + } }, "notificationsScreen": { "notifications": "通知事項", diff --git a/screens/LoginScreen.js b/screens/LoginScreen.js index 275031cf..14d3e0c6 100644 --- a/screens/LoginScreen.js +++ b/screens/LoginScreen.js @@ -829,7 +829,9 @@ class LoginScreen extends React.Component { ]}> - {this.props.pinCode.enabled ? 'Enter PIN' : 'Set new PIN'} + {this.props.pinCode.enabled + ? i18n.t('settingsScreen.pinCode.enter') + : i18n.t('settingsScreen.pinCode.set')} {this.state.incorrectPin ? ( - {'Incorrect PIN'} + {i18n.t('settingsScreen.pinCode.incorrect')} ) : null} diff --git a/screens/SettingsScreen.js b/screens/SettingsScreen.js index 8cfaea74..7fbd40a5 100644 --- a/screens/SettingsScreen.js +++ b/screens/SettingsScreen.js @@ -345,9 +345,11 @@ class SettingsScreen extends React.Component { - {`${ - this.props.pinCode.enabled ? 'Remove' : 'Set' - } PIN code`} + + {this.props.pinCode.enabled + ? i18n.t('settingsScreen.pinCode.remove') + : i18n.t('settingsScreen.pinCode.set')} + {/* === Help / Support === */} @@ -416,7 +418,9 @@ class SettingsScreen extends React.Component { color: Colors.gray, marginBottom: 5, }}> - {this.props.pinCode.enabled ? 'Enter PIN' : 'Set new PIN'} + {this.props.pinCode.enabled + ? i18n.t('settingsScreen.pinCode.enter') + : i18n.t('settingsScreen.pinCode.set')} {this.state.incorrectPin ? ( - {'Incorrect PIN'} + {i18n.t('settingsScreen.pinCode.incorrect')} ) : null} - {'Close'} + {i18n.t('global.close')} From e42c163bf95074f242666e331d6848a689bf2545 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Mon, 1 Jun 2020 15:17:06 -0500 Subject: [PATCH 03/14] Resolves #327 --- App.js | 3 - languages/ar.json | 22 ++- languages/bn.json | 22 ++- languages/en.json | 22 ++- languages/es.json | 22 ++- languages/fa.json | 22 ++- languages/fr.json | 22 ++- languages/id.json | 22 ++- languages/index.js | 63 +++--- languages/locales.js | 64 +++--- languages/nl.json | 22 ++- languages/pt.json | 22 ++- languages/ru.json | 22 ++- languages/sw.json | 22 ++- languages/tr.json | 22 ++- languages/zhCn.json | 22 ++- languages/zhTw.json | 22 ++- screens/LoginScreen.js | 99 +++++----- screens/SettingsScreen.js | 223 ++++++++++++++++----- store/actions/user.actions.js | 9 + store/reducers/i18n.reducer.js | 5 +- store/reducers/user.reducer.js | 14 ++ store/sagas/networkConnectivity.sagas.js | 242 +++++++++++------------ store/sagas/request.sagas.js | 6 + store/sagas/user.sagas.js | 50 ++++- 25 files changed, 711 insertions(+), 375 deletions(-) diff --git a/App.js b/App.js index 39befac5..6fdc6a09 100644 --- a/App.js +++ b/App.js @@ -12,9 +12,6 @@ import Reactotron from 'reactotron-react-native'; import AppNavigator from './navigation/AppNavigator'; import { store, persistor } from './store/store'; -// import i18n from './languages'; -// This import is added to enable momentJS library (do not delete) -import moment from './languages/moment'; // notifications diff --git a/languages/ar.json b/languages/ar.json index 47b65cb9..3629a5a6 100644 --- a/languages/ar.json +++ b/languages/ar.json @@ -26,6 +26,12 @@ "moreFields": "المزيد من الحقول", "nameRequired": "الاسم مطلوب" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "اسم نطاق أدوات التلمذة", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "اسم مستخدم غير معروف. تحقق مرة أخرى أو حاول عنوان بريدك الإلكتروني.", "incorrectPassword": "كلمة المرور التي أدخلتها لاسم المستخدم غير صحيحة" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "المتواصلون", @@ -66,7 +69,16 @@ "helpSupport": "ساعد لدعم", "rememberPassword": "تذكر كلمة المرور", "rememberPasswordActive": "تمكين تسجيل الدخول باستمرار", - "rememberPasswordInactive": "حافظ على تسجيل الدخول معطلة" + "rememberPasswordInactive": "حافظ على تسجيل الدخول معطلة", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "إضافة جهة اتصال جديدة", diff --git a/languages/bn.json b/languages/bn.json index 216fba36..40e10e48 100644 --- a/languages/bn.json +++ b/languages/bn.json @@ -26,6 +26,12 @@ "moreFields": "আরও ক্ষেত্র", "nameRequired": "নাম (প্রয়োজন" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL টি", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "অজানা ব্যবহারকারীর নাম। আবার চেক করুন বা আপনার ইমেল ঠিকানা চেষ্টা করুন।", "incorrectPassword": "আপনি ব্যবহারকারীর জন্য প্রবেশ করা পাসওয়ার্ডটি ভুল" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "যোগাযোগ", @@ -66,7 +69,16 @@ "helpSupport": "সাহায্য সহযোগীতা", "rememberPassword": "পাসওয়ার্ড মনে", "rememberPasswordActive": "সক্ষম থাকা লগ ইন করুন", - "rememberPasswordInactive": "লগ ইন অক্ষম রাখুন" + "rememberPasswordInactive": "লগ ইন অক্ষম রাখুন", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "নতুন পরিচিতি যোগ করুন", diff --git a/languages/en.json b/languages/en.json index 6d6fd184..9c605d6e 100644 --- a/languages/en.json +++ b/languages/en.json @@ -26,6 +26,12 @@ "moreFields": "More Fields", "nameRequired": "Name required" }, + "appRestart": { + "message": "The application will be restarted to apply the following configuration:", + "button": "Ok", + "textDirection": "Text direction", + "selectedLanguage": "Selected language" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Unknown username. Check again or try your email address.", "incorrectPassword": "The password you entered for username is incorrect" - }, - "ok": "Ok", - "appRestart": "The application will be restarted to apply the following configuration:", - "textDirection": "Text direction" + } }, "contactsScreen": { "contacts": "Contacts", @@ -66,7 +69,16 @@ "helpSupport": "Help / Support", "rememberPassword": "Keep logged in", "rememberPasswordActive": "Keep logged in enabled", - "rememberPasswordInactive": "Keep logged in disabled" + "rememberPasswordInactive": "Keep logged in disabled", + "remove": "Remove", + "set": "Set", + "pinCode": "PIN code", + "enterPin": "Enter PIN", + "setPin": "Set new PIN", + "incorrectPin": "Incorrect PIN", + "savedPinCode": "PIN code saved succesfully!.", + "removedPinCode": "PIN code removed succesfully!.", + "close": "Close" }, "contactDetailScreen": { "addNewContact": "Add New Contact", diff --git a/languages/es.json b/languages/es.json index 9c22713d..46884c4e 100644 --- a/languages/es.json +++ b/languages/es.json @@ -26,6 +26,12 @@ "moreFields": "Más Campos", "nameRequired": "Nombre requerido" }, + "appRestart": { + "message": "La aplicación sera reiniciada para aplicar la siguiente configuración:", + "button": "De acuerdo", + "textDirection": "Dirección del texto", + "selectedLanguage": "Lenguaje seleccionado" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Nombre de usuario desconocido. Vuelve a consultar o prueba tu dirección de correo electrónico.", "incorrectPassword": "La contraseña que ingresó para el usuario es incorrecta" - }, - "ok": "Muy bien", - "appRestart": "La aplicación sera reiniciada para aplicar la siguiente configuración:", - "textDirection": "Dirección del texto" + } }, "contactsScreen": { "contacts": "Contactos", @@ -66,7 +69,16 @@ "helpSupport": "Ayuda / Soporte", "rememberPassword": "Mantener iniciada la sesión", "rememberPasswordActive": "Mantener iniciada la sesión activo", - "rememberPasswordInactive": "Mantener iniciada la sesión inactivo" + "rememberPasswordInactive": "Mantener iniciada la sesión inactivo", + "remove": "Remover", + "set": "Agregar", + "pinCode": "código PIN", + "enterPin": "Ingrese el PIN", + "setPin": "Ingrese el nuevo PIN", + "incorrectPin": "PIN incorrecto", + "savedPinCode": "El código PIN se ha guardado correctamente!.", + "removedPinCode": "El código PIN se ha removido correctamente!.", + "close": "Cerrar" }, "contactDetailScreen": { "addNewContact": "Crear contacto", diff --git a/languages/fa.json b/languages/fa.json index c14855e7..c248ce59 100644 --- a/languages/fa.json +++ b/languages/fa.json @@ -26,6 +26,12 @@ "moreFields": "زمینه های بیشتر", "nameRequired": "نام لازم است" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "آدرس اینترنتی", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "نام کاربری ناشناخته دوباره بررسی کنید یا آدرس ایمیل خود را امتحان کنید.", "incorrectPassword": "گذرواژه‌ای که برای نام کاربری خود وارد کردید اشتباه است" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "مخاطب", @@ -66,7 +69,16 @@ "helpSupport": "راهنما / پشتیبانی", "rememberPassword": "به یاد داشته باشید کلمه کلیدی", "rememberPasswordActive": "فعال شوید", - "rememberPasswordInactive": "وارد سیستم غیرفعال شوید" + "rememberPasswordInactive": "وارد سیستم غیرفعال شوید", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "مخاطب جدید اضافه کنید", diff --git a/languages/fr.json b/languages/fr.json index 4ec4cee9..c3419c25 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -26,6 +26,12 @@ "moreFields": "Plus de champs", "nameRequired": "Nom obligatoire" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Nom d'utilisateur inconnu. Vérifiez à nouveau ou essayez votre adresse e-mail.", "incorrectPassword": "Le mot de passe que vous avez entré pour le nom d'utilisateur est incorrect" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Contacts", @@ -66,7 +69,16 @@ "helpSupport": "Aide", "rememberPassword": "se souvenir du mot de passe", "rememberPasswordActive": "Rester connecté activé", - "rememberPasswordInactive": "Rester connecté désactivé" + "rememberPasswordInactive": "Rester connecté désactivé", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Ajouter un nouveau contact", diff --git a/languages/id.json b/languages/id.json index 90a9108a..16d937b9 100644 --- a/languages/id.json +++ b/languages/id.json @@ -26,6 +26,12 @@ "moreFields": "Lebih banyak bidang", "nameRequired": "Nama (wajib" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Nama pengguna tidak dikenal. Periksa lagi atau coba alamat email Anda.", "incorrectPassword": "Kata sandi yang Anda masukkan untuk nama pengguna salah" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Kontak", @@ -66,7 +69,16 @@ "helpSupport": "Bantuan / Dukungan", "rememberPassword": "ingat kata Sandi", "rememberPasswordActive": "Tetap masuk log diaktifkan", - "rememberPasswordInactive": "Tetap masuk log dinonaktifkan" + "rememberPasswordInactive": "Tetap masuk log dinonaktifkan", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Tambahkan Kontak Baru", diff --git a/languages/index.js b/languages/index.js index 02c8c194..1b529e58 100644 --- a/languages/index.js +++ b/languages/index.js @@ -1,25 +1,10 @@ import { I18nManager } from 'react-native'; -import * as Localization from 'expo-localization'; import i18n from 'i18n-js'; - -// Intl fix -import 'intl'; -// Imports by supported languages (When adding or removing a new language, modify this imports and imports of 'languages/moment.js' file) -import 'intl/locale-data/jsonp/en'; -import 'intl/locale-data/jsonp/ar'; -import 'intl/locale-data/jsonp/bn'; -import 'intl/locale-data/jsonp/es'; -import 'intl/locale-data/jsonp/fa'; -import 'intl/locale-data/jsonp/fr'; -import 'intl/locale-data/jsonp/id'; -import 'intl/locale-data/jsonp/nl'; -import 'intl/locale-data/jsonp/pt'; -import 'intl/locale-data/jsonp/ru'; -import 'intl/locale-data/jsonp/sw'; -import 'intl/locale-data/jsonp/tr'; -import 'intl/locale-data/jsonp/zh'; +// This import is added to enable momentJS library (do not delete) +import moment from './moment'; import * as en from './en.json'; +import * as ar from './ar.json'; import * as bn from './bn.json'; import * as es from './es.json'; import * as fr from './fr.json'; @@ -31,38 +16,40 @@ import * as sw from './sw.json'; import * as tr from './tr.json'; import * as zhCn from './zhCn.json'; import * as zhTw from './zhTw.json'; -import * as ar from './ar.json'; import * as fa from './fa.json'; i18n.fallbacks = true; +// Locale codes names as expo-localization -> Localization.locale format (device) i18n.translations = { - en, - bn, - es, - fr, - id, - nl, + 'en-US': en, + ar, + 'bn-BD': bn, + 'es-ES': es, + 'fa-IR': fa, + 'fr-FR': fr, + 'id-ID': id, + 'nl-NL': nl, 'pt-BR': ptBR, - ru, + 'ru-RU': ru, sw, - tr, - 'zh-hans': zhCn, - 'zh-hant': zhTw, - ar, - fa, + 'tr-TR': tr, + 'zh-CN': zhCn, + 'zh-TW': zhTw, }; -i18n.locale = Localization.locale; -i18n.isRTL = I18nManager.isRTL; - -I18nManager.allowRTL(true); // Do not try to set I18nManager.isRTL here as it will have no effect. // To change RTL, use I18nManager.forceRTL(bool) and then refresh the app // to see the direction changed. -i18n.setLocale = function setLocale(locale) { - Localization.locale = locale; - this.locale = Localization.locale; +i18n.setLocale = function setLocale(locale, isRTL) { + this.locale = locale; + // Enable/Disable RTL + I18nManager.allowRTL(isRTL); + I18nManager.forceRTL(isRTL); + // Update momentJS locale + let momentLocale = + locale.substring(0, 2) === 'zh' ? locale.toLowerCase() : locale.substring(0, 2); + moment.locale(momentLocale); }; export default i18n; diff --git a/languages/locales.js b/languages/locales.js index b7c0f08c..87eebeec 100644 --- a/languages/locales.js +++ b/languages/locales.js @@ -1,72 +1,72 @@ export default [ { - code: 'en', + code: 'en-US', name: 'English', - direction: 'ltr', + rtl: false, }, { - code: 'bn', + code: 'ar', + name: 'عربي', + rtl: true, + }, + { + code: 'bn-BD', name: 'বাংলা', - direction: 'ltr', + rtl: true, }, { - code: 'es', + code: 'es-ES', name: 'Español', - direction: 'ltr', + rtl: false, + }, + { + code: 'fa-IR', + name: 'فارسی', + rtl: true, }, { - code: 'fr', + code: 'fr-FR', name: 'Français', - direction: 'ltr', + rtl: false, }, { - code: 'id', + code: 'id-ID', name: 'bahasa Indonesia', - direction: 'ltr', + rtl: false, }, { - code: 'nl', + code: 'nl-NL', name: 'Nederlands', - direction: 'ltr', + rtl: false, }, { code: 'pt-BR', name: 'Português', - direction: 'ltr', + rtl: false, }, { - code: 'ru', + code: 'ru-RU', name: 'русский язык', - direction: 'ltr', + rtl: false, }, { code: 'sw', name: 'Kiswahili', - direction: 'ltr', + rtl: true, }, { - code: 'tr', + code: 'tr-TR', name: 'Türkçe', - direction: 'ltr', + rtl: false, }, { - code: 'zh-hans', + code: 'zh-CN', name: '汉语', - direction: 'ltr', + rtl: false, }, { - code: 'zh-hant', + code: 'zh-TW', name: '漢語', - direction: 'ltr', - }, - { - code: 'ar', - name: 'عربي', - direction: 'rtl', - }, - { - code: 'fa', - name: 'فارسی', - direction: 'rtl', + rtl: false, }, ]; diff --git a/languages/nl.json b/languages/nl.json index 260bfe55..c0c57738 100644 --- a/languages/nl.json +++ b/languages/nl.json @@ -26,6 +26,12 @@ "moreFields": "Meer velden", "nameRequired": "Naam (vereist" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Onbekende gebruikersnaam. Controleer het opnieuw of probeer uw e-mailadres.", "incorrectPassword": "Het wachtwoord dat u hebt ingevoerd voor de gebruikersnaam is onjuist" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Contacten", @@ -66,7 +69,16 @@ "helpSupport": "Help & Ondersteuning", "rememberPassword": "Onthoud wachtwoord", "rememberPasswordActive": "Blijf ingelogd ingeschakeld", - "rememberPasswordInactive": "Blijf ingelogd uitgeschakeld" + "rememberPasswordInactive": "Blijf ingelogd uitgeschakeld", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Voeg nieuw contact toe", diff --git a/languages/pt.json b/languages/pt.json index d7dc4586..bca88206 100644 --- a/languages/pt.json +++ b/languages/pt.json @@ -26,6 +26,12 @@ "moreFields": "Mais campos", "nameRequired": "Nome necessário" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Nome de Usuário desconhecido. Verifique novamente ou tente seu endereço de e-mail", "incorrectPassword": "A senha que você digitou para o usuário está incorreta" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Contatos", @@ -66,7 +69,16 @@ "helpSupport": "Ajuda / Suporte", "rememberPassword": "Lembrar senha", "rememberPasswordActive": "Manter-se logado está ligado", - "rememberPasswordInactive": "Manter-se logado está desligado" + "rememberPasswordInactive": "Manter-se logado está desligado", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Adicionar novo contato", diff --git a/languages/ru.json b/languages/ru.json index 1ff565fd..88b010c3 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -26,6 +26,12 @@ "moreFields": "Больше полей", "nameRequired": "Имя (обязательно" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Неизвестное имя пользователя. Проверьте еще раз или попробуйте свой адрес электронной почты.", "incorrectPassword": "Неверный пароль для этого пользователя" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Контакты", @@ -66,7 +69,16 @@ "helpSupport": "Помощь / Поддержка", "rememberPassword": "Запомнить пароль", "rememberPasswordActive": "Оставайтесь в системе активным", - "rememberPasswordInactive": "Оставайтесь в системе отключенным" + "rememberPasswordInactive": "Оставайтесь в системе отключенным", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Добавить новый контакт", diff --git a/languages/sw.json b/languages/sw.json index f58320a3..6bd16d4f 100644 --- a/languages/sw.json +++ b/languages/sw.json @@ -26,6 +26,12 @@ "moreFields": "Mashamba Zaidi", "nameRequired": "Jina linalohitajika" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Jina la mtumiaji lisilojulikana. Angalia tena au jaribu anwani yako ya barua pepe.", "incorrectPassword": "Nenosiri uliloliingiza jina la mtumiaji sio sahihi" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Anwani", @@ -66,7 +69,16 @@ "helpSupport": "Msaada / Msaada", "rememberPassword": "Kumbuka Nenosiri", "rememberPasswordActive": "Endelea kuwezeshwa", - "rememberPasswordInactive": "Endelea kuwa umelemazwa" + "rememberPasswordInactive": "Endelea kuwa umelemazwa", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Ongeza Mawasiliano Mpya", diff --git a/languages/tr.json b/languages/tr.json index 1c97c98f..0a5b647d 100644 --- a/languages/tr.json +++ b/languages/tr.json @@ -26,6 +26,12 @@ "moreFields": "Diğer Alanlar", "nameRequired": "İsim (gerekli" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "Disciple Tools URL", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "Bilinmeyen kullanıcı adı. Tekrar kontrol edin veya e-posta adresinizi deneyin.", "incorrectPassword": "Kullanıcı adı için girdiğiniz şifre yanlış" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "Bağlantılarım", @@ -66,7 +69,16 @@ "helpSupport": "yardım / destek", "rememberPassword": "şifre hatırlamak", "rememberPasswordActive": "Oturum açmayı etkin tut", - "rememberPasswordInactive": "Giriş yapmayı devre dışı bırak" + "rememberPasswordInactive": "Giriş yapmayı devre dışı bırak", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "Yeni bağlantı ekle", diff --git a/languages/zhCn.json b/languages/zhCn.json index 83eaa600..a565bd59 100644 --- a/languages/zhCn.json +++ b/languages/zhCn.json @@ -26,6 +26,12 @@ "moreFields": "更多领域", "nameRequired": "姓名必填" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "网址", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "未知的用户名。再次检查或尝试您的电子邮件地址。", "incorrectPassword": "您输入的用户名密码不正确" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "联络人", @@ -66,7 +69,16 @@ "helpSupport": "帮助支持", "rememberPassword": "记住密码", "rememberPasswordActive": "保持登录状态", - "rememberPasswordInactive": "保持登录状态已禁用" + "rememberPasswordInactive": "保持登录状态已禁用", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "新增联络人", diff --git a/languages/zhTw.json b/languages/zhTw.json index 64fe02b5..61603ed2 100644 --- a/languages/zhTw.json +++ b/languages/zhTw.json @@ -26,6 +26,12 @@ "moreFields": "更多領域", "nameRequired": "姓名必填" }, + "appRestart": { + "message": "", + "button": "", + "textDirection": "", + "selectedLanguage": "" + }, "loginScreen": { "domain": { "label": "網址", @@ -46,10 +52,7 @@ "errors": { "invalidUsername": "未知的用戶名。再次檢查或嘗試您的電子郵件地址。", "incorrectPassword": "您輸入的用戶名密碼不正確" - }, - "ok": "", - "appRestart": "", - "textDirection": "" + } }, "contactsScreen": { "contacts": "聯絡人", @@ -66,7 +69,16 @@ "helpSupport": "幫助支持", "rememberPassword": "記住密碼", "rememberPasswordActive": "保持登錄狀態", - "rememberPasswordInactive": "保持登錄狀態已禁用" + "rememberPasswordInactive": "保持登錄狀態已禁用", + "remove": "", + "set": "", + "pinCode": "", + "enterPin": "", + "setPin": "", + "incorrectPin": "", + "savedPinCode": "", + "removedPinCode": "", + "close": "" }, "contactDetailScreen": { "addNewContact": "新增聯絡人", diff --git a/screens/LoginScreen.js b/screens/LoginScreen.js index 275031cf..e634e8ea 100644 --- a/screens/LoginScreen.js +++ b/screens/LoginScreen.js @@ -25,6 +25,7 @@ import ExpoFileSystemStorage from 'redux-persist-expo-filesystem'; import SmoothPinCodeInput from 'react-native-smooth-pincode-input'; import { BlurView } from 'expo-blur'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import * as Localization from 'expo-localization'; import i18n from '../languages'; import locales from '../languages/locales'; @@ -41,7 +42,6 @@ import { } from '../store/actions/groups.actions'; import { getUsers } from '../store/actions/users.actions'; import { getContactSettings, getAll as getAllContacts } from '../store/actions/contacts.actions'; -import moment from '../languages/moment'; const styles = StyleSheet.create({ container: { @@ -225,7 +225,24 @@ class LoginScreen extends React.Component { passwordIsInvalid: false, hidePassword: true, }; - i18n.setLocale(props.i18n.locale, props.i18n.isRTL); + + // Set locale in APP + if (props.i18n.locale) { + // Set locale and RTL in i18n Library + i18n.setLocale(props.i18n.locale, props.i18n.isRTL); + } else { + // On first time app launch + let locale = locales.find((item) => { + return ( + item.code === Localization.locale || + item.code.substring(0, 2) === Localization.locale.substring(0, 2) + ); + }); + // Set locale and RTL in i18n Library + i18n.setLocale(locale.code, locale.rtl); + // Set locale and RTL in State + this.props.setLanguage(locale.code, locale.rtl); + } } static getDerivedStateFromProps(nextProps, prevState) { @@ -400,26 +417,16 @@ class LoginScreen extends React.Component { peopleGroupsRetrieved, usersRetrieved, } = this.state; - // If the RTL value in the store does not match what is - // in I18nManager (which controls content flow), call - // forceRTL(...) to set it in I18nManager and reload app - // so that new RTL value is used for content flow. - /* if (this.props.i18n.isRTL !== I18nManager.isRTL) { - I18nManager.forceRTL(this.props.i18n.isRTL); - // a bit of a hack to wait and make sure the reducer is persisted to storage - setTimeout(() => { - Updates.reloadFromCache(); - }, 500); - } */ // User logged successfully if (userData && prevProps.userData.token !== userData.token) { this.getDataLists(); this.getUserInfo(); } + // User locale retrieved if (userData && userData.locale && prevProps.userData.locale !== userData.locale) { - this.setAppLanguage(); + this.changeLanguage(userData.locale.replace('_', '-'), true); } // peopleGroupsList retrieved @@ -458,16 +465,6 @@ class LoginScreen extends React.Component { this.props.navigation.navigate('ContactList'); } - if (this.props.i18n && prevProps.i18n !== this.props.i18n) { - i18n.setLocale(this.props.i18n.locale, this.props.i18n.isRTL); - if (prevProps.i18n.isRTL !== this.props.i18n.isRTL) { - I18nManager.forceRTL(this.props.i18n.isRTL); - } else { - // TODO: refactor this code so this force re-render is no longer necessary - this.forceUpdate(); - } - } - const userError = prevProps.userReducerError !== userReducerError && userReducerError; let groupsError = prevProps.groupsReducerError !== groupsReducerError; groupsError = groupsError && groupsReducerError; @@ -530,16 +527,6 @@ class LoginScreen extends React.Component { this.focusListener.remove(); } - setAppLanguage = () => { - let localeCode; - if (this.props.userData.locale.substring(0, 2) === 'zh') { - localeCode = this.props.userData.locale; - } else { - localeCode = this.props.userData.locale.substring(0, 2); - } - this.changeLanguage(localeCode); - }; - getDataLists = () => { this.props.getContactSettings(this.props.userData.domain, this.props.userData.token); this.props.getGroupSettings(this.props.userData.domain, this.props.userData.token); @@ -612,21 +599,20 @@ class LoginScreen extends React.Component { }; /* eslint-enable class-methods-use-this, no-console */ - changeLanguage(languageCode) { - const locale = locales.find((item) => item.code === languageCode); - if (locale) { - const isRTL = locale.direction === 'rtl'; - //New 'isRTL' value same as old value - if (isRTL === this.props.i18n.isRTL) { - this.setState({ - appLanguageSet: true, - }); - } else { - this.showRestartDialog(); - } - this.props.setLanguage(locale.code, isRTL); - // Update momentJS locale - moment.locale(languageCode); + changeLanguage(languageCode, logIn = false) { + let locale = locales.find((item) => { + return item.code === languageCode; + }); + // Set locale and RTL in i18n Library + i18n.setLocale(locale.code, locale.rtl); + // Set locale and RTL in State + this.props.setLanguage(locale.code, locale.rtl); + if (locale.rtl !== this.props.i18n.isRTL) { + this.showRestartDialog(); + } else if (logIn) { + this.setState({ + appLanguageSet: true, + }); } } @@ -738,7 +724,9 @@ class LoginScreen extends React.Component { {userErrorMessage} - {i18n.t('loginScreen.password.label')} + + {i18n.t('loginScreen.password.label')} + - {i18n.t('loginScreen.appRestart')} + {i18n.t('appRestart.message')} + + {i18n.t('appRestart.selectedLanguage') + + ': ' + + locales.find((item) => item.code === this.props.i18n.locale).name} + - {i18n.t('loginScreen.textDirection') + + {i18n.t('appRestart.textDirection') + ': ' + (this.props.i18n.isRTL ? 'RTL' : 'LTR')} diff --git a/screens/SettingsScreen.js b/screens/SettingsScreen.js index 8cfaea74..6d94b3c5 100644 --- a/screens/SettingsScreen.js +++ b/screens/SettingsScreen.js @@ -1,13 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import { - I18nManager, - StyleSheet, - Text, - View, - KeyboardAvoidingView, - Dimensions, -} from 'react-native'; +import { StyleSheet, Text, View, KeyboardAvoidingView, Dimensions } from 'react-native'; import { Body, Button as NbButton, @@ -16,7 +9,7 @@ import { Icon, Left, ListItem, - // Picker, + Picker, Right, Switch, Thumbnail, @@ -34,10 +27,11 @@ import { toggleRememberPassword, savePINCode, removePINCode, + updateUserInfo, } from '../store/actions/user.actions'; import { toggleNetworkConnectivity } from '../store/actions/networkConnectivity.actions'; import i18n from '../languages'; -// import locales from '../languages/locales'; +import locales from '../languages/locales'; import { BlurView } from 'expo-blur'; import SmoothPinCodeInput from 'react-native-smooth-pincode-input'; @@ -55,7 +49,7 @@ const propTypes = { displayName: PropTypes.string, }).isRequired, logout: PropTypes.func.isRequired, - // setLanguage: PropTypes.func.isRequired, + setLanguage: PropTypes.func.isRequired, toggleNetworkConnectivity: PropTypes.func.isRequired, pinCode: PropTypes.shape({ enabled: PropTypes.bool, @@ -120,15 +114,45 @@ const styles = StyleSheet.create({ textAlignVertical: 'center', textAlign: 'center', }, + dialogBackground: { + position: 'absolute', + justifyContent: 'center', + alignItems: 'center', + top: 0, + left: 0, + }, + dialogBox: { + backgroundColor: '#FFFFFF', + padding: 20, + marginLeft: 10, + marginRight: 10, + }, + dialogButton: { + backgroundColor: Colors.tintColor, + borderRadius: 5, + width: 150, + alignSelf: 'center', + marginTop: 20, + }, + dialogContent: { + fontSize: 20, + textAlign: 'center', + color: Colors.grayDark, + marginBottom: 5, + }, }); let toastError; -let codePinRef; const { height, width } = Dimensions.get('window'); class SettingsScreen extends React.Component { state = { toggleShowPIN: false, pin: '', incorrectPin: false, + toggleRestartDialog: false, + selectedNewRTLDirection: false, + i18n: { + ...this.props.i18n, + }, }; constructor(props) { @@ -137,19 +161,31 @@ class SettingsScreen extends React.Component { this.onFABPress = this.onFABPress.bind(this); } + static getDerivedStateFromProps(nextProps, prevState) { + const { userData, i18n } = nextProps; + + let newState = { + ...prevState, + ...i18n, + }; + + if (userData) { + newState = { + ...newState, + selectedNewRTLDirection: prevState.i18n.isRTL !== i18n.isRTL, + }; + } + + return newState; + } + componentDidUpdate(prevProps) { - const { rememberPassword } = this.props; - // If the RTL value in the store does not match what is - // in I18nManager (which controls content flow), call - // forceRTL(...) to set it in I18nManager and reload app - // so that new RTL value is used for content flow. - if (this.props.i18n.isRTL !== I18nManager.isRTL) { - I18nManager.forceRTL(this.props.i18n.isRTL); - // a bit of a hack to wait and make sure the reducer is persisted to storage - setTimeout(() => { - Updates.reloadFromCache(); - }, 500); + const { rememberPassword, userData, userReducerError } = this.props; + + if (userData && prevProps.userData !== userData && userData.locale !== this.props.i18n.locale) { + this.changeLanguage(userData.locale.replace('_', '-')); } + if (rememberPassword !== undefined && prevProps.rememberPassword !== rememberPassword) { this.showToast( rememberPassword @@ -157,6 +193,23 @@ class SettingsScreen extends React.Component { : i18n.t('settingsScreen.rememberPasswordInactive'), ); } + + const userError = prevProps.userReducerError !== userReducerError && userReducerError; + if (userError) { + toastError.show( + + + {i18n.t('global.error.code')} + + {userError.code} + + {i18n.t('global.error.message')} + + {userError.message} + , + 3000, + ); + } } signOutAsync = async () => { @@ -241,11 +294,44 @@ class SettingsScreen extends React.Component { ); - render() { - // const languagePickerItems = locales.map((locale) => ( - // - // )); + renderLanguagePickerItems = () => + locales.map((locale) => ( + + )); + + selectLanguage = (languageCode) => { + // Set language in Server + this.updateUserInfo({ + locale: languageCode.replace('-', '_'), + }); + }; + + updateUserInfo = (userInfo) => { + this.props.updateUserInfo(this.props.userData.domain, this.props.userData.token, userInfo); + }; + + changeLanguage(languageCode) { + let locale = locales.find((item) => { + return item.code === languageCode; + }); + // Set locale and RTL in i18n Library + i18n.setLocale(locale.code, locale.rtl); + // Set locale and RTL in State + this.props.setLanguage(locale.code, locale.rtl); + this.showRestartDialog(); + } + + showRestartDialog = () => { + this.setState({ + toggleRestartDialog: true, + }); + }; + + restartApp = () => { + Updates.reload(); + }; + render() { return ( {!this.props.isConnected && this.offlineBarRender()} @@ -291,10 +377,9 @@ class SettingsScreen extends React.Component { {/* === Language === */} - {/* - + @@ -303,26 +388,13 @@ class SettingsScreen extends React.Component { { - const locale = locales.find(item => item.code === itemValue); - if (locale) { - const isRTL = locale.direction === 'rtl'; - // store locale/rtl instore for next load of app - this.props.setLanguage(locale.code, isRTL); - // set current locale for all language strings - i18n.setLocale(locale.code, isRTL); - } - }} - enabled={false} - > - {languagePickerItems} + onValueChange={this.selectLanguage}> + {this.renderLanguagePickerItems()} - */} {/* === Remember password === */} @@ -345,9 +417,13 @@ class SettingsScreen extends React.Component { - {`${ - this.props.pinCode.enabled ? 'Remove' : 'Set' - } PIN code`} + + {`${ + this.props.pinCode.enabled + ? i18n.t('settingsScreen.remove') + : i18n.t('settingsScreen.set') + } ${i18n.t('settingsScreen.pinCode')}`} + {/* === Help / Support === */} @@ -416,7 +492,9 @@ class SettingsScreen extends React.Component { color: Colors.gray, marginBottom: 5, }}> - {this.props.pinCode.enabled ? 'Enter PIN' : 'Set new PIN'} + {this.props.pinCode.enabled + ? i18n.t('settingsScreen.enterPin') + : i18n.t('settingsScreen.setPin')} {this.state.incorrectPin ? ( - {'Incorrect PIN'} + {i18n.t('settingsScreen.incorrectPin')} ) : null} - {'Close'} + {i18n.t('settingsScreen.close')} ) : null} + {this.state.toggleRestartDialog ? ( + + + {i18n.t('appRestart.message')} + + {i18n.t('appRestart.selectedLanguage') + + ': ' + + locales.find((item) => item.code === this.props.i18n.locale).name} + + {this.state.selectedNewRTLDirection ? ( + + {i18n.t('appRestart.textDirection') + + ': ' + + (this.props.i18n.isRTL ? 'RTL' : 'LTR')} + + ) : null} + + {i18n.t('appRestart.button')} + + + + ) : null} ); } } SettingsScreen.propTypes = propTypes; - +SettingsScreen.defaultProps = { + userReducerError: null, +}; const mapStateToProps = (state) => ({ i18n: state.i18nReducer, isConnected: state.networkConnectivityReducer.isConnected, userData: state.userReducer.userData, rememberPassword: state.userReducer.rememberPassword, pinCode: state.userReducer.pinCode, + userReducerError: state.userReducer.error, }); const mapDispatchToProps = (dispatch) => ({ toggleNetworkConnectivity: (isConnected) => { @@ -510,5 +622,8 @@ const mapDispatchToProps = (dispatch) => ({ removePINCode: () => { dispatch(removePINCode()); }, + updateUserInfo: (domain, token, userInfo) => { + dispatch(updateUserInfo(domain, token, userInfo)); + }, }); export default connect(mapStateToProps, mapDispatchToProps)(SettingsScreen); diff --git a/store/actions/user.actions.js b/store/actions/user.actions.js index dc804bc9..2758e198 100644 --- a/store/actions/user.actions.js +++ b/store/actions/user.actions.js @@ -26,6 +26,11 @@ export const REMEMBER_PASSWORD = 'REMEMBER_PASSWORD'; export const SAVE_PIN_CODE = 'SAVE_PIN_CODE'; export const REMOVE_PIN_CODE = 'REMOVE_PIN_CODE'; +export const UPDATE_USER_INFO = 'UPDATE_USER_INFO'; +export const UPDATE_USER_INFO_RESPONSE = 'UPDATE_USER_INFO_RESPONSE'; +export const UPDATE_USER_INFO_SUCCESS = 'UPDATE_USER_INFO_SUCCESS'; +export const UPDATE_USER_INFO_FAILURE = 'UPDATE_USER_INFO_FAILURE'; + /* * Action Creators */ @@ -69,3 +74,7 @@ export function savePINCode(value) { export function removePINCode() { return { type: REMOVE_PIN_CODE }; } + +export function updateUserInfo(domain, token, userInfo) { + return { type: UPDATE_USER_INFO, domain, token, userInfo }; +} diff --git a/store/reducers/i18n.reducer.js b/store/reducers/i18n.reducer.js index 4a3ff944..93abf2b5 100644 --- a/store/reducers/i18n.reducer.js +++ b/store/reducers/i18n.reducer.js @@ -1,17 +1,18 @@ import * as actions from '../actions/i18n.actions'; const initialState = { - locale: 'en', + locale: null, isRTL: false, }; export default function i18nReducer(state = initialState, action) { switch (action.type) { - case actions.I18N_SETLANGUAGE: + case actions.I18N_SETLANGUAGE: { return { locale: action.locale, isRTL: action.isRTL, }; + } default: return state; } diff --git a/store/reducers/user.reducer.js b/store/reducers/user.reducer.js index ae2e5e9b..0ed509f8 100644 --- a/store/reducers/user.reducer.js +++ b/store/reducers/user.reducer.js @@ -134,6 +134,20 @@ export default function userReducer(state = initialState, action) { value: null, }, }; + case actions.UPDATE_USER_INFO_SUCCESS: { + return { + ...newState, + userData: { + ...newState.userData, + locale: action.locale, + }, + }; + } + case actions.UPDATE_USER_INFO_FAILURE: + return { + ...newState, + error: action.error, + }; default: return newState; } diff --git a/store/sagas/networkConnectivity.sagas.js b/store/sagas/networkConnectivity.sagas.js index fa1ef195..c21e2fab 100644 --- a/store/sagas/networkConnectivity.sagas.js +++ b/store/sagas/networkConnectivity.sagas.js @@ -1,141 +1,145 @@ -import { - actionChannel, take, select, put, -} from 'redux-saga/effects'; +import { actionChannel, take, select, put } from 'redux-saga/effects'; export default function* networkConnectivitySaga() { - const onlineChannel = yield actionChannel('ONLINE'); while (true) { - yield take(onlineChannel); - let queue = yield select(state => state.requestReducer.queue); + let queue = yield select((state) => state.requestReducer.queue); for (let action of queue) { - let mappedRequest = { ...action, }; // Only process POST and SAVE requests - if (action.data.method === 'POST' && action.action.includes('SAVE')) { - - mappedRequest = { - ...mappedRequest, - isConnected: true, - }; - - yield put({ - type: 'REQUEST', - payload: mappedRequest, - }); - - let response = yield take(mappedRequest.action); - response = response.payload; - - if (Object.prototype.hasOwnProperty.call(response, 'status')) { - - let jsonData = response.data; - - if (response.oldID) { - - jsonData = { - ...jsonData, - oldID: response.oldID, - }; - - } - - if (response.status === 200) { - - if (jsonData.oldID) { - - const entityListName = action.action.substr(0, action.action.indexOf('_')).toLowerCase(); - // Map comments requests of entity - queue.forEach(function (requestTwo, index) { - - let mappedRequestTwo = { - ...requestTwo, - }; - // update oldID to ID in URL - if (requestTwo.url.includes(`${entityListName}/${jsonData.oldID}/comments`)) { + if (action.data.method === 'POST') { + if (action.action.includes('SAVE')) { + mappedRequest = { + ...mappedRequest, + isConnected: true, + }; + + yield put({ + type: 'REQUEST', + payload: mappedRequest, + }); + + let response = yield take(mappedRequest.action); + response = response.payload; + + if (Object.prototype.hasOwnProperty.call(response, 'status')) { + let jsonData = response.data; + + if (response.oldID) { + jsonData = { + ...jsonData, + oldID: response.oldID, + }; + } - mappedRequestTwo = { - ...mappedRequestTwo, - url: requestTwo.url.replace(jsonData.oldID, jsonData.ID) + if (response.status === 200) { + if (jsonData.oldID) { + const entityListName = action.action + .substr(0, action.action.indexOf('_')) + .toLowerCase(); + // Map comments requests of entity + queue.forEach(function (requestTwo, index) { + let mappedRequestTwo = { + ...requestTwo, }; - - } - // search only in POST requests - if (requestTwo.data.method === 'POST') { - - let requestBody = { ...JSON.parse(requestTwo.data.body) }; - - Object.keys(requestBody).forEach((key) => { - - const value = requestBody[key]; - // Update temporal ID in multi-value fields with back-end ID - if (Object.prototype.hasOwnProperty.call(value, 'values') && value.values.length > 0) { - - let mappedValue = value.values; - - mappedValue = mappedValue.map((object) => { - - let copyObject = { ...object }; - - if (copyObject.value === jsonData.oldID) { - - copyObject = { - value: jsonData.ID.toString() - }; - - } - - return copyObject; - - }); - - requestBody = { - ...requestBody, - [key]: { - values: mappedValue - } - }; - - } - - }); - - mappedRequestTwo = { + // update oldID to ID in URL + if (requestTwo.url.includes(`${entityListName}/${jsonData.oldID}/comments`)) { + mappedRequestTwo = { + ...mappedRequestTwo, + url: requestTwo.url.replace(jsonData.oldID, jsonData.ID), + }; + } + // search only in POST requests + if (requestTwo.data.method === 'POST') { + let requestBody = { ...JSON.parse(requestTwo.data.body) }; + + Object.keys(requestBody).forEach((key) => { + const value = requestBody[key]; + // Update temporal ID in multi-value fields with back-end ID + if ( + Object.prototype.hasOwnProperty.call(value, 'values') && + value.values.length > 0 + ) { + let mappedValue = value.values; + + mappedValue = mappedValue.map((object) => { + let copyObject = { ...object }; + + if (copyObject.value === jsonData.oldID) { + copyObject = { + value: jsonData.ID.toString(), + }; + } + + return copyObject; + }); + + requestBody = { + ...requestBody, + [key]: { + values: mappedValue, + }, + }; + } + }); + + mappedRequestTwo = { + ...mappedRequestTwo, + data: { + ...mappedRequestTwo.data, + body: JSON.stringify(requestBody), + }, + }; + } + + queue[index] = { ...mappedRequestTwo, - data: { - ...mappedRequestTwo.data, - body: JSON.stringify(requestBody) - } }; - - } - - queue[index] = { - ...mappedRequestTwo - }; - - }); - - } - - if (!mappedRequest.url.includes('/comments')) { - - const entityName = action.action.substr(0, action.action.indexOf('_') - 1).toLowerCase(); - + }); + } + + if (!mappedRequest.url.includes('/comments')) { + const entityName = action.action + .substr(0, action.action.indexOf('_') - 1) + .toLowerCase(); + + yield put({ + type: mappedRequest.action.replace('RESPONSE', 'SUCCESS'), + [entityName]: jsonData, + }); + } + } else { yield put({ - type: mappedRequest.action.replace('RESPONSE', 'SUCCESS'), - [entityName]: jsonData, + type: mappedRequest.action.replace('RESPONSE', 'FAILURE'), + error: { + code: jsonData.code, + message: jsonData.message, + }, }); - } + } + } else if (action.action.includes('UPDATE_USER_INFO')) { + yield put({ + type: 'REQUEST', + payload: mappedRequest, + }); + let response = yield take(mappedRequest.action); + response = response.payload; + + if (response.status === 200) { + yield put({ + type: mappedRequest.action.replace('RESPONSE', 'SUCCESS'), + ...JSON.parse(mappedRequest.data.body), + }); } else { + let jsonData = response.data; yield put({ type: mappedRequest.action.replace('RESPONSE', 'FAILURE'), @@ -144,15 +148,9 @@ export default function* networkConnectivitySaga() { message: jsonData.message, }, }); - } - } - } - } - } - } diff --git a/store/sagas/request.sagas.js b/store/sagas/request.sagas.js index bef656a1..fac7b536 100644 --- a/store/sagas/request.sagas.js +++ b/store/sagas/request.sagas.js @@ -151,6 +151,12 @@ export default function* requestSaga() { payload: { data: { location_grid: list }, status: 200 }, }); } + if (payload.data.method === 'POST' && payload.action.includes('UPDATE_USER_INFO')) { + yield put({ + type: payload.action, + payload: { data: {}, status: 200 }, + }); + } } } } else if (request) { diff --git a/store/sagas/user.sagas.js b/store/sagas/user.sagas.js index 3f9d4267..268fb58d 100644 --- a/store/sagas/user.sagas.js +++ b/store/sagas/user.sagas.js @@ -1,4 +1,4 @@ -import { put, take, takeLatest, all, takeEvery, race, call } from 'redux-saga/effects'; +import { put, take, takeLatest, all, takeEvery } from 'redux-saga/effects'; import Constants from 'expo-constants'; import * as Device from 'expo-device'; import * as Permissions from 'expo-permissions'; @@ -182,11 +182,59 @@ export function* getUserInfo({ domain, token }) { } } +export function* updateUserInfo({ domain, token, userInfo }) { + yield put({ + type: 'REQUEST', + payload: { + url: `https://${domain}/wp-json/dt/v1/user/update`, + data: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(userInfo), + }, + action: actions.UPDATE_USER_INFO_RESPONSE, + }, + }); + + try { + let response = yield take(actions.UPDATE_USER_INFO_RESPONSE); + response = response.payload; + let jsonData = response.data; + + if (response.status === 200) { + yield put({ + type: actions.UPDATE_USER_INFO_SUCCESS, + ...userInfo, + }); + } else { + yield put({ + type: actions.UPDATE_USER_INFO_FAILURE, + error: { + code: jsonData.code, + message: jsonData.message, + }, + }); + } + } catch (error) { + yield put({ + type: actions.UPDATE_USER_INFO_FAILURE, + error: { + code: '400', + message: 'Unable to process the request. Please try again later.', + }, + }); + } +} + export default function* userSaga() { yield all([ takeLatest(actions.USER_LOGIN, login), takeEvery(actions.GET_MY_USER_INFO, getUserInfo), takeLatest(actions.USER_GET_PUSH_TOKEN, getExpoPushToken), takeLatest(actions.USER_ADD_PUSH_TOKEN, addPushToken), + takeLatest(actions.UPDATE_USER_INFO, updateUserInfo), ]); } From ea7608d3941c9b097df36163cffbb2e993bb0339 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Wed, 3 Jun 2020 18:43:52 -0500 Subject: [PATCH 04/14] Resolves #333 --- screens/LoginScreen.js | 58 +++++++++++++++---------- store/reducers/contacts.reducer.js | 1 + store/reducers/groups.reducer.js | 5 ++- store/reducers/notifications.reducer.js | 5 +++ store/reducers/request.reducer.js | 6 +++ 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/screens/LoginScreen.js b/screens/LoginScreen.js index 1ecc45db..a4ed0cf1 100644 --- a/screens/LoginScreen.js +++ b/screens/LoginScreen.js @@ -10,7 +10,6 @@ import { ActivityIndicator, Platform, ScrollView, - I18nManager, Picker, Dimensions, TextInput, @@ -42,6 +41,7 @@ import { } from '../store/actions/groups.actions'; import { getUsers } from '../store/actions/users.actions'; import { getContactSettings, getAll as getAllContacts } from '../store/actions/contacts.actions'; +import { logout } from '../store/actions/user.actions'; const styles = StyleSheet.create({ container: { @@ -364,35 +364,39 @@ class LoginScreen extends React.Component { userIsAuthenticated = () => { // User is authenticated (logged) - if (this.props.userData && this.props.userData.token && this.props.rememberPassword) { - if (this.props.isConnected) { - if (this.props.pinCode.enabled) { - this.toggleShowPIN(); - } else { - if (this.props) + if (this.props.userData && this.props.userData.token !== null) { + if (this.props.rememberPassword) { + if (this.props.isConnected) { + if (this.props.pinCode.enabled) { + this.toggleShowPIN(); + } else { this.props.loginDispatch( this.props.userData.domain, this.props.userData.username, this.props.userData.password, ); + } + } else { + this.setState( + { + loading: true, + }, + () => { + this.setState({ + contactSettingsRetrieved: true, + groupSettingsRetrieved: true, + geonamesRetrieved: true, + peopleGroupsRetrieved: true, + usersRetrieved: true, + appLanguageSet: true, + userDataRetrieved: true, + }); + }, + ); } } else { - this.setState( - { - loading: true, - }, - () => { - this.setState({ - contactSettingsRetrieved: true, - groupSettingsRetrieved: true, - geonamesRetrieved: true, - peopleGroupsRetrieved: true, - usersRetrieved: true, - appLanguageSet: true, - userDataRetrieved: true, - }); - }, - ); + // Clear previous user's stored data (User closed app with rememberPassword: false) + this.props.logout(); } } }; @@ -419,7 +423,7 @@ class LoginScreen extends React.Component { } = this.state; // User logged successfully - if (userData && prevProps.userData.token !== userData.token) { + if (userData && userData.token && prevProps.userData.token !== userData.token) { this.getDataLists(); this.getUserInfo(); } @@ -451,6 +455,7 @@ class LoginScreen extends React.Component { if (geonames && prevProps.geonames !== geonames) { ExpoFileSystemStorage.setItem('locationsList', JSON.stringify(geonames)); } + if ( contactSettingsRetrieved && groupSettingsRetrieved && @@ -471,6 +476,7 @@ class LoginScreen extends React.Component { const usersError = prevProps.usersReducerError !== usersReducerError && usersReducerError; const contactsError = prevProps.contactsReducerError !== contactsReducerError && contactsReducerError; + if (userError || groupsError || usersError || contactsError) { const error = userError || groupsError || usersError; if (error.code === '[jwt_auth] incorrect_password') { @@ -966,6 +972,7 @@ LoginScreen.propTypes = { getContacts: PropTypes.func.isRequired, getGroups: PropTypes.func.isRequired, getUserInfo: PropTypes.func.isRequired, + logout: PropTypes.func.isRequired, }; LoginScreen.defaultProps = { userData: { @@ -1041,5 +1048,8 @@ const mapDispatchToProps = (dispatch) => ({ getLocationModifiedDate: (domain, token) => { dispatch(getLocationListLastModifiedDate(domain, token)); }, + logout: () => { + dispatch(logout()); + }, }); export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen); diff --git a/store/reducers/contacts.reducer.js b/store/reducers/contacts.reducer.js index b483f466..18aa3488 100644 --- a/store/reducers/contacts.reducer.js +++ b/store/reducers/contacts.reducer.js @@ -768,6 +768,7 @@ export default function contactsReducer(state = initialState, action) { return { ...newState, settings: null, + contacts: [], }; default: return newState; diff --git a/store/reducers/groups.reducer.js b/store/reducers/groups.reducer.js index 1df465a8..0884b2bd 100644 --- a/store/reducers/groups.reducer.js +++ b/store/reducers/groups.reducer.js @@ -835,8 +835,11 @@ export default function groupsReducer(state = initialState, action) { case userActions.USER_LOGOUT: return { ...newState, + groups: [], geonames: null, - peopleGroups: null, + settings: null, + geonamesLastModifiedDate: null, + geonamesLength: 0, }; case actions.GROUPS_GET_SETTINGS_SUCCESS: { const { settings } = action; diff --git a/store/reducers/notifications.reducer.js b/store/reducers/notifications.reducer.js index 396ab652..29704cd4 100644 --- a/store/reducers/notifications.reducer.js +++ b/store/reducers/notifications.reducer.js @@ -1,4 +1,5 @@ import * as actions from '../actions/notifications.actions'; +import * as userActions from '../actions/user.actions'; const initialState = { loading: false, @@ -42,6 +43,10 @@ export default function notificationsReducer(state = initialState, action) { error: action.error, loading: false, }; + case userActions.USER_LOGOUT: + return { + notificationsCount: null, + }; default: return newState; } diff --git a/store/reducers/request.reducer.js b/store/reducers/request.reducer.js index b0b40190..0493c9fd 100644 --- a/store/reducers/request.reducer.js +++ b/store/reducers/request.reducer.js @@ -1,4 +1,5 @@ import * as actions from '../actions/request.actions'; +import * as userActions from '../actions/user.actions'; const initialState = { queue: [], @@ -401,6 +402,11 @@ export default function requestReducer(state = initialState, action) { ...newState, queue: [...newQueue], }; + case userActions.USER_LOGOUT: + return { + queue: [], + currentAction: {}, + }; return newState; default: return newState; From 99928ae4d418c72fbb9cde296871abf6b7111b07 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Thu, 4 Jun 2020 14:31:14 -0500 Subject: [PATCH 05/14] Resolves #327 --- languages/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/en.json b/languages/en.json index 35cc15f4..9e23d671 100644 --- a/languages/en.json +++ b/languages/en.json @@ -77,8 +77,8 @@ "enterPin": "Enter PIN", "setPin": "Set new PIN", "incorrectPin": "Incorrect PIN", - "savedPinCode": "PIN code saved succesfully!.", - "removedPinCode": "PIN code removed succesfully!.", + "savedPinCode": "PIN code saved successfully!.", + "removedPinCode": "PIN code removed successfully!.", "close": "Close" }, "contactDetailScreen": { From 94f9a633c940f65e9077e93abb3c41f32d5ba454 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Mon, 8 Jun 2020 11:28:52 -0500 Subject: [PATCH 06/14] Resolves #245 --- screens/Contact/ContactDetailScreen.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index e45e903a..477061cf 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -384,7 +384,7 @@ class ContactDetailScreen extends React.Component { type="Feather" name="arrow-left" onPress={() => { - params.backButtonClick(); + params.backButtonTap(); }} style={{ paddingLeft: 16, color: '#FFFFFF', paddingRight: 16 }} /> @@ -442,7 +442,7 @@ class ContactDetailScreen extends React.Component { onEnableEdit: this.onEnableEdit, onDisableEdit: this.onDisableEdit, onSaveContact: this.onSaveContact, - backButtonClick: this.backButtonClick, + backButtonTap: this.backButtonTap, }); keyboardDidShowListener = Keyboard.addListener( 'keyboardDidShow', @@ -469,7 +469,7 @@ class ContactDetailScreen extends React.Component { }); hardwareBackPressListener = BackHandler.addEventListener( 'hardwareBackPress', - this.backButtonClick, + this.backButtonTap, ); } @@ -765,9 +765,22 @@ class ContactDetailScreen extends React.Component { }); } - backButtonClick = () => { + backButtonTap = () => { const { navigation } = this.props; const { params } = navigation.state; + if (params.hideTabBar) { + navigation.setParams({ + hideTabBar: false, + }); + setTimeout(() => { + return this.executeBack(navigation, params); + }, 600); + } else { + return this.executeBack(navigation, params); + } + }; + + executeBack = (navigation, params) => { if (params.previousList.length > 0) { navigation.goBack(); params.onBackFromSameScreen(); @@ -1321,7 +1334,7 @@ class ContactDetailScreen extends React.Component { this.state.usersContacts.find((user) => user.value === contactID), 'name', ), - backButtonClick: this.backButtonClick.bind(this), + backButtonTap: this.backButtonTap.bind(this), onBackFromSameScreen: this.onBackFromSameScreen.bind(this), }); /* eslint-enable */ From 830f5bb92c7a893af02d5b263f1846f41bb73f1a Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Mon, 8 Jun 2020 15:19:15 -0500 Subject: [PATCH 07/14] Resolves #245 --- screens/Group/GroupDetailScreen.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/screens/Group/GroupDetailScreen.js b/screens/Group/GroupDetailScreen.js index 4f1c8f88..20215ab6 100644 --- a/screens/Group/GroupDetailScreen.js +++ b/screens/Group/GroupDetailScreen.js @@ -517,7 +517,7 @@ class GroupDetailScreen extends React.Component { type="Feather" name="arrow-left" onPress={() => { - params.backButtonClick(); + params.backButtonTap(); }} style={[{ paddingLeft: 16, color: '#FFFFFF', paddingRight: 16 }]} /> @@ -576,7 +576,7 @@ class GroupDetailScreen extends React.Component { onEnableEdit: this.onEnableEdit, onDisableEdit: this.onDisableEdit, onSaveGroup: this.onSaveGroup, - backButtonClick: this.backButtonClick, + backButtonTap: this.backButtonTap, }); keyboardDidShowListener = Keyboard.addListener( 'keyboardDidShow', @@ -603,7 +603,7 @@ class GroupDetailScreen extends React.Component { }); hardwareBackPressListener = BackHandler.addEventListener( 'hardwareBackPress', - this.backButtonClick, + this.backButtonTap, ); } @@ -864,9 +864,22 @@ class GroupDetailScreen extends React.Component { }); } - backButtonClick = () => { + backButtonTap = () => { const { navigation } = this.props; const { params } = navigation.state; + if (params.hideTabBar) { + navigation.setParams({ + hideTabBar: false, + }); + setTimeout(() => { + return this.executeBack(navigation, params); + }, 600); + } else { + return this.executeBack(navigation, params); + } + }; + + executeBack = (navigation, params) => { if (params.previousList.length > 0) { navigation.goBack(); params.onBackFromSameScreen(); @@ -1707,7 +1720,7 @@ class GroupDetailScreen extends React.Component { groupId: groupData.value, onlyView: true, groupName: groupData.name, - backButtonClick: this.backButtonClick.bind(this), + backButtonTap: this.backButtonTap.bind(this), onBackFromSameScreen: this.onBackFromSameScreen.bind(this), }); /* eslint-enable */ From ae2013507806f1db28feb23b203f86a4ef4dd6d0 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Tue, 9 Jun 2020 12:15:37 -0500 Subject: [PATCH 08/14] Resolves #245 --- screens/Contact/ContactDetailScreen.js | 28 ++++++++++++++++++-------- screens/Group/GroupDetailScreen.js | 28 ++++++++++++++++++-------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index 477061cf..35dfd9ae 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -325,6 +325,7 @@ const initialState = { footerLocation: 0, footerHeight: 0, nameRequired: false, + executingBack: false, }; const safeFind = (found, prop) => { @@ -695,6 +696,14 @@ class ContactDetailScreen extends React.Component { 3000, ); } + + if (prevProps.navigation.state.params.hideTabBar !== navigation.state.params.hideTabBar) { + if (!navigation.state.params.hideTabBar && this.state.executingBack) { + setTimeout(() => { + this.executeBack(navigation, navigation.state.params); + }, 1000); + } + } } onLoad() { @@ -769,14 +778,18 @@ class ContactDetailScreen extends React.Component { const { navigation } = this.props; const { params } = navigation.state; if (params.hideTabBar) { - navigation.setParams({ - hideTabBar: false, - }); - setTimeout(() => { - return this.executeBack(navigation, params); - }, 600); + this.setState( + { + executingBack: true, + }, + () => { + navigation.setParams({ + hideTabBar: false, + }); + }, + ); } else { - return this.executeBack(navigation, params); + this.executeBack(navigation, params); } }; @@ -798,7 +811,6 @@ class ContactDetailScreen extends React.Component { params.onGoBack(); } } - return true; }; onRefresh(contactId) { diff --git a/screens/Group/GroupDetailScreen.js b/screens/Group/GroupDetailScreen.js index 20215ab6..1695fc2c 100644 --- a/screens/Group/GroupDetailScreen.js +++ b/screens/Group/GroupDetailScreen.js @@ -462,6 +462,7 @@ const initialState = { footerLocation: 0, footerHeight: 0, nameRequired: false, + executingBack: false, }; const safeFind = (found, prop) => { @@ -850,6 +851,14 @@ class GroupDetailScreen extends React.Component { 3000, ); } + + if (prevProps.navigation.state.params.hideTabBar !== navigation.state.params.hideTabBar) { + if (!navigation.state.params.hideTabBar && this.state.executingBack) { + setTimeout(() => { + this.executeBack(navigation, navigation.state.params); + }, 1000); + } + } } keyboardDidShow(event) { @@ -868,14 +877,18 @@ class GroupDetailScreen extends React.Component { const { navigation } = this.props; const { params } = navigation.state; if (params.hideTabBar) { - navigation.setParams({ - hideTabBar: false, - }); - setTimeout(() => { - return this.executeBack(navigation, params); - }, 600); + this.setState( + { + executingBack: true, + }, + () => { + navigation.setParams({ + hideTabBar: false, + }); + }, + ); } else { - return this.executeBack(navigation, params); + this.executeBack(navigation, params); } }; @@ -897,7 +910,6 @@ class GroupDetailScreen extends React.Component { params.onGoBack(); } } - return true; }; onLoad() { From c2265ffa935974c39d933e259ff23f068eb7c97a Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Tue, 9 Jun 2020 15:03:55 -0500 Subject: [PATCH 09/14] Resolves #245 --- App.js | 14 +++----------- screens/Contact/ContactDetailScreen.js | 18 ++++++++++++------ screens/Group/GroupDetailScreen.js | 18 ++++++++++++------ shared/index.js | 11 +++++++++++ 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/App.js b/App.js index 6fdc6a09..da7740ac 100644 --- a/App.js +++ b/App.js @@ -12,6 +12,7 @@ import Reactotron from 'reactotron-react-native'; import AppNavigator from './navigation/AppNavigator'; import { store, persistor } from './store/store'; +import sharedTools from './shared'; // notifications @@ -26,16 +27,7 @@ const styles = StyleSheet.create({ }); // App -let unsubscribe, handle; -let onlyExecuteLastCall = (parameter, functionName, timeout) => { - if (handle) { - clearTimeout(handle); - } - handle = setTimeout(function () { - handle = 0; - functionName(parameter); - }, timeout); -}; +let unsubscribe; class App extends React.Component { constructor() { super(); @@ -52,7 +44,7 @@ class App extends React.Component { }); // add network connectivity handler unsubscribe = NetInfo.addEventListener((state) => - onlyExecuteLastCall(state.isConnected, this.handleConnectivityChange, 1000), + sharedTools.onlyExecuteLastCall(state.isConnected, this.handleConnectivityChange, 1000), ); if (__DEV__) { diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index 35dfd9ae..bae9cd09 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -468,10 +468,16 @@ class ContactDetailScreen extends React.Component { ); } }); - hardwareBackPressListener = BackHandler.addEventListener( - 'hardwareBackPress', - this.backButtonTap, - ); + hardwareBackPressListener = BackHandler.addEventListener('hardwareBackPress', () => { + sharedTools.onlyExecuteLastCall( + null, + () => { + this.backButtonTap(); + }, + 1000, + ); + return true; + }); } componentWillUnmount() { @@ -775,8 +781,8 @@ class ContactDetailScreen extends React.Component { } backButtonTap = () => { - const { navigation } = this.props; - const { params } = navigation.state; + let { navigation } = this.props; + let { params } = navigation.state; if (params.hideTabBar) { this.setState( { diff --git a/screens/Group/GroupDetailScreen.js b/screens/Group/GroupDetailScreen.js index 1695fc2c..c1a81fe5 100644 --- a/screens/Group/GroupDetailScreen.js +++ b/screens/Group/GroupDetailScreen.js @@ -602,10 +602,16 @@ class GroupDetailScreen extends React.Component { ); } }); - hardwareBackPressListener = BackHandler.addEventListener( - 'hardwareBackPress', - this.backButtonTap, - ); + hardwareBackPressListener = BackHandler.addEventListener('hardwareBackPress', () => { + sharedTools.onlyExecuteLastCall( + null, + () => { + this.backButtonTap(); + }, + 1000, + ); + return true; + }); } componentWillUnmount() { @@ -874,8 +880,8 @@ class GroupDetailScreen extends React.Component { } backButtonTap = () => { - const { navigation } = this.props; - const { params } = navigation.state; + let { navigation } = this.props; + let { params } = navigation.state; if (params.hideTabBar) { this.setState( { diff --git a/shared/index.js b/shared/index.js index 71509509..94af4134 100644 --- a/shared/index.js +++ b/shared/index.js @@ -187,6 +187,16 @@ const debounce = (fn, time) => { const commentFieldMinHeight = 35; const commentFieldMinContainerHeight = 120; +let handle, + onlyExecuteLastCall = (parameter, functionName, timeout) => { + if (handle) { + clearTimeout(handle); + } + handle = setTimeout(function () { + handle = 0; + functionName(parameter); + }, timeout); + }; export default { diff, @@ -195,4 +205,5 @@ export default { debounce, commentFieldMinHeight, commentFieldMinContainerHeight, + onlyExecuteLastCall, }; From c44ba2e40152b280e04d5918cd793456df86d831 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Thu, 11 Jun 2020 14:40:51 -0500 Subject: [PATCH 10/14] Resolves #342 --- languages/index.js | 2 +- screens/LoginScreen.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/languages/index.js b/languages/index.js index 1b529e58..90c5b7dd 100644 --- a/languages/index.js +++ b/languages/index.js @@ -18,7 +18,7 @@ import * as zhCn from './zhCn.json'; import * as zhTw from './zhTw.json'; import * as fa from './fa.json'; -i18n.fallbacks = true; +//i18n.fallbacks = true; // Locale codes names as expo-localization -> Localization.locale format (device) i18n.translations = { 'en-US': en, diff --git a/screens/LoginScreen.js b/screens/LoginScreen.js index a4ed0cf1..5a5b3325 100644 --- a/screens/LoginScreen.js +++ b/screens/LoginScreen.js @@ -238,6 +238,8 @@ class LoginScreen extends React.Component { item.code.substring(0, 2) === Localization.locale.substring(0, 2) ); }); + // If phone locale does not exist, set English locale + if (!locale) locale = locales[0]; // Set locale and RTL in i18n Library i18n.setLocale(locale.code, locale.rtl); // Set locale and RTL in State From b6726d3624f327f87bf7be7d2b027853ddae5581 Mon Sep 17 00:00:00 2001 From: Hans Rasch Date: Thu, 11 Jun 2020 14:46:22 -0500 Subject: [PATCH 11/14] Resolves #343 --- screens/LoginScreen.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/screens/LoginScreen.js b/screens/LoginScreen.js index 5a5b3325..7102964a 100644 --- a/screens/LoginScreen.js +++ b/screens/LoginScreen.js @@ -571,21 +571,16 @@ class LoginScreen extends React.Component { onLoginPress = () => { Keyboard.dismiss(); - if (this.props.pinCode.enabled) { - // User with PIN=true and AutoLogin=FALSE - this.toggleShowPIN(); + const { domain, username, password } = this.state; + if (domain && username && password) { + const cleanedDomain = (domain || '').replace('http://', '').replace('https://', ''); + this.props.loginDispatch(cleanedDomain, username, password); } else { - const { domain, username, password } = this.state; - if (domain && username && password) { - const cleanedDomain = (domain || '').replace('http://', '').replace('https://', ''); - this.props.loginDispatch(cleanedDomain, username, password); - } else { - this.setState({ - domainValidation: !domain, - userValidation: !username, - passwordValidation: !password, - }); - } + this.setState({ + domainValidation: !domain, + userValidation: !username, + passwordValidation: !password, + }); } }; @@ -826,8 +821,8 @@ class LoginScreen extends React.Component { {this.props.pinCode.enabled - ? i18n.t('settingsScreen.pinCode.enter') - : i18n.t('settingsScreen.pinCode.set')} + ? i18n.t('settingsScreen.enterPin') + : i18n.t('settingsScreen.setPin')} {this.state.incorrectPin ? ( - {i18n.t('settingsScreen.pinCode.incorrect')} + {i18n.t('settingsScreen.incorrectPin')} ) : null} Date: Tue, 23 Jun 2020 20:27:49 -0500 Subject: [PATCH 12/14] Resolves #332 --- package-lock.json | 13 ++ package.json | 2 + screens/Contact/ContactDetailScreen.js | 232 ++++++++++++++++------- screens/Group/GroupDetailScreen.js | 243 +++++++++++++++++-------- screens/NotificationsScreen.js | 6 +- screens/SettingsScreen.js | 86 +++++++-- shared/index.js | 13 +- 7 files changed, 435 insertions(+), 160 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4cd4a79..3d99d761 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18151,11 +18151,24 @@ "prop-types": "^15.6.2" } }, + "react-native-mentions": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/react-native-mentions/-/react-native-mentions-1.1.4.tgz", + "integrity": "sha1-3FDvLwlqLr+IGaRHlPazBGvGkwg=" + }, "react-native-modal-filter-picker": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-native-modal-filter-picker/-/react-native-modal-filter-picker-2.0.0.tgz", "integrity": "sha512-QioTeGSL53A0q4mJsaahJaXIdxbvTIXdyIn9WEfwRoTldhZtp6L/8ZmxLMiRKvt2qUCA4BIus9eMfSaqGkm5WQ==" }, + "react-native-parsed-text": { + "version": "0.0.22-beta.3", + "resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.22-beta.3.tgz", + "integrity": "sha512-jhh/Ipees0f1rCsX47wBKzhrGFGPb/9d3+ZuzxiN4FmFi18idUb78FycbE0GZWintQ4rDP9u8ENT6psIbcUDnw==", + "requires": { + "prop-types": "^15.7.x" + } + }, "react-native-progress-bar-animated": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/react-native-progress-bar-animated/-/react-native-progress-bar-animated-1.0.6.tgz", diff --git a/package.json b/package.json index 45ebcfab..9a0cf2d5 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,9 @@ "react-native-gesture-handler": "~1.6.1", "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-material-selectize": "^1.18.0", + "react-native-mentions": "^1.1.4", "react-native-modal-filter-picker": "^2.0.0", + "react-native-parsed-text": "0.0.22-beta.3", "react-native-progress-bar-animated": "^1.0.6", "react-native-reanimated": "~1.7.0", "react-native-safe-area-context": "0.7.3", diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index bae9cd09..5d1c13fc 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -1,5 +1,4 @@ import React from 'react'; -import { connect } from 'react-redux'; import { ScrollView, Text, @@ -10,14 +9,15 @@ import { Image, Dimensions, FlatList, - TextInput, RefreshControl, Platform, TouchableHighlight, Linking, - StatusBar, BackHandler, + ActivityIndicator, } from 'react-native'; + +import { connect } from 'react-redux'; import ExpoFileSystemStorage from 'redux-persist-expo-filesystem'; import PropTypes from 'prop-types'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; @@ -29,8 +29,10 @@ import { Chip, Selectize } from 'react-native-material-selectize'; import ActionButton from 'react-native-action-button'; import { TabView, TabBar } from 'react-native-tab-view'; import { NavigationActions, StackActions } from 'react-navigation'; -import moment from '../../languages/moment'; +import MentionsTextInput from 'react-native-mentions'; +import ParsedText from 'react-native-parsed-text'; +import moment from '../../languages/moment'; import sharedTools from '../../shared'; import { save, @@ -62,8 +64,9 @@ const windowWidth = Dimensions.get('window').width; const progressBarWidth = windowWidth - 100; const milestonesGridSize = windowWidth + 5; let keyboardDidShowListener, keyboardDidHideListener, focusListener, hardwareBackPressListener; -const hasNotch = Platform.OS === 'android' && StatusBar.currentHeight > 25; -const extraNotchHeight = hasNotch ? StatusBar.currentHeight : 0; +//const hasNotch = Platform.OS === 'android' && StatusBar.currentHeight > 25; +//const extraNotchHeight = hasNotch ? StatusBar.currentHeight : 0; +const isIOS = Platform.OS === 'ios'; /* eslint-disable */ let commentsFlatList, subAssignedSelectizeRef, @@ -287,6 +290,35 @@ const styles = StyleSheet.create({ validationErrorMessage: { color: Colors.errorBackground, }, + suggestionsRowContainer: { + flexDirection: 'row', + }, + userIconBox: { + height: 45, + width: 45, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.tintColor, + }, + usernameInitials: { + color: '#fff', + fontWeight: '800', + fontSize: 14, + }, + userDetailsBox: { + flex: 1, + justifyContent: 'center', + paddingLeft: 10, + paddingRight: 15, + }, + displayNameText: { + fontSize: 13, + fontWeight: '500', + }, + usernameText: { + fontSize: 12, + color: 'rgba(0,0,0,0.6)', + }, }); const initialState = { @@ -326,6 +358,9 @@ const initialState = { footerHeight: 0, nameRequired: false, executingBack: false, + keyword: '', + suggestedUsers: [], + height: sharedTools.commentFieldMinHeight, }; const safeFind = (found, prop) => { @@ -354,7 +389,7 @@ class ContactDetailScreen extends React.Component { name="check" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, + self && self.props.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, ]} /> @@ -373,7 +408,7 @@ class ContactDetailScreen extends React.Component { name="pencil" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, + self && self.props.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, ]} /> @@ -398,7 +433,7 @@ class ContactDetailScreen extends React.Component { name="close" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingRight: 16 } : { paddingLeft: 16 }, + self && self.props.isRTL ? { paddingRight: 16 } : { paddingLeft: 16 }, ]} /> @@ -633,7 +668,6 @@ class ContactDetailScreen extends React.Component { commentsFlatList.scrollToOffset({ animated: true, offset: 0 }); } this.setComment(''); - this.setHeight(); } // CONTACT SAVE / GET BY ID @@ -770,7 +804,7 @@ class ContactDetailScreen extends React.Component { keyboardDidShow(event) { this.setState({ - footerLocation: event.endCoordinates.height + extraNotchHeight, + footerLocation: isIOS ? event.endCoordinates.height /*+ extraNotchHeight*/ : 0, }); } @@ -1183,14 +1217,6 @@ class ContactDetailScreen extends React.Component { }); }; - setHeight = (newHeight = 0) => { - newHeight = Math.max(sharedTools.commentFieldMinHeight, newHeight); - this.setState({ - height: Math.min(sharedTools.commentFieldMinContainerHeight, newHeight), - footerHeight: Math.min(sharedTools.commentFieldMinContainerHeight, newHeight) + 20, - }); - }; - onSaveComment = () => { const { comment } = this.state; if (!this.state.loadComments) { @@ -1463,7 +1489,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.subassigned ? ( this.state.contact.subassigned.values.map((contact, index) => @@ -1512,7 +1538,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.contact_phone ? ( this.state.contact.contact_phone @@ -1547,7 +1573,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.contact_email ? ( this.state.contact.contact_email @@ -1604,7 +1630,7 @@ class ContactDetailScreen extends React.Component { key={socialMediaIndex.toString()} style={[ socialMediaIndex === 0 ? { marginTop: 10 } : {}, - i18n.isRTL ? { textAlign: 'left', flex: 1 } : {}, + this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}, ]}> {socialMedia.value} @@ -1613,7 +1639,7 @@ class ContactDetailScreen extends React.Component { {channelName} @@ -1638,7 +1664,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.contact_address ? this.state.contact.contact_address @@ -1663,7 +1689,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.location_grid ? this.state.contact.location_grid.values @@ -1697,7 +1723,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.people_groups ? this.state.contact.people_groups.values @@ -1731,7 +1757,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.age ? this.state.contact.age : ''} @@ -1751,7 +1777,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.gender ? this.props.contactSettings.fields.gender.values[this.state.contact.gender] @@ -1778,7 +1804,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.sources ? `${this.state.contact.sources.values @@ -2581,7 +2607,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.seeker_path ? this.props.contactSettings.fields.seeker_path.values[ @@ -2613,7 +2639,7 @@ class ContactDetailScreen extends React.Component { style={[ styles.formLabel, { fontWeight: 'bold', marginBottom: 10, marginTop: 20 }, - i18n.isRTL ? { textAlign: 'left', flex: 1 } : {}, + this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}, ]}> {this.props.contactSettings.fields.milestones.name} @@ -2726,6 +2752,46 @@ class ContactDetailScreen extends React.Component { ); + onSuggestionTap(username, hidePanel) { + hidePanel(); + let comment = this.state.comment.slice(0, -this.state.keyword.length), + mentionFormat = `@[${username.label}](${username.key})`; + this.setState({ + suggestedUsers: [], + comment: `${comment}${mentionFormat}`, + }); + } + + filterUsers(keyword) { + let newKeyword = keyword.replace('@', ''); + this.setState((state) => { + return { + suggestedUsers: state.users.filter((user) => + user.label.toLowerCase().includes(newKeyword.toLowerCase()), + ), + keyword, + }; + }); + } + + renderSuggestionsRow({ item }, hidePanel) { + return ( + this.onSuggestionTap(item, hidePanel)}> + + + + {!!item.label && item.label.substring(0, 2).toUpperCase()} + + + + {item.label} + @{item.label} + + + + ); + } + commentsView = () => ( {this.state.comments.length == 0 && @@ -2799,29 +2865,44 @@ class ContactDetailScreen extends React.Component { } }} /> - - + this.setHeight(event.nativeEvent.contentSize.height)} - editable={!this.state.loadComments} - multiline - style={[ - styles.commentInput, - { height: this.state.height }, - i18n.isRTL ? { textAlign: 'right', flex: 1 } : {}, - this.state.loadComments ? { backgroundColor: '#e6e6e6' } : { backgroundColor: 'white' }, - ]} + style={this.props.isRTL ? { textAlign: 'right', flex: 1 } : {}} + textInputStyle={{ + borderColor: '#B4B4B4', + borderRadius: 5, + borderWidth: 1, + padding: 5, + margin: 10, + width: windowWidth - 80, + backgroundColor: this.state.loadComments ? '#e6e6e6' : '#FFFFFF', + }} + loadingComponent={() => ( + + + + )} + textInputMinHeight={40} + textInputMaxHeight={80} + trigger={'@'} + triggerLocation={'new-word-only'} + triggerCallback={this.filterUsers.bind(this)} + renderSuggestionsRow={this.renderSuggestionsRow.bind(this)} + suggestionsData={this.state.suggestedUsers} + keyExtractor={(item, index) => item.key.toString()} + suggestionRowHeight={45} + horizontal={false} + MaxVisibleRowCount={3} /> this.onSaveComment()} @@ -2829,16 +2910,20 @@ class ContactDetailScreen extends React.Component { { borderRadius: 80, height: 40, - margin: 10, - paddingTop: 7, width: 40, + paddingTop: 7, + marginRight: 10, + marginBottom: 10, + position: 'absolute', + right: 0, + bottom: 0, }, this.state.loadComments ? { backgroundColor: '#e6e6e6' } : { backgroundColor: Colors.tintColor }, - i18n.isRTL ? { paddingRight: 10 } : { paddingLeft: 10 }, + this.props.isRTL ? { paddingRight: 10 } : { paddingLeft: 10 }, ]}> - + @@ -2869,7 +2954,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.groups ? ( this.state.contact.groups.values.map((group, index) => @@ -2914,7 +2999,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.relation ? ( this.state.contact.relation.values.map((relation, index) => @@ -2961,7 +3046,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.baptized_by ? ( this.state.contact.baptized_by.values.map((baptizedBy, index) => @@ -3008,7 +3093,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.baptized ? ( this.state.contact.baptized.values.map((baptized, index) => @@ -3059,7 +3144,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.coached_by ? ( this.state.contact.coached_by.values.map((coachedBy, index) => @@ -3110,7 +3195,7 @@ class ContactDetailScreen extends React.Component { {this.state.contact.coaching ? ( this.state.contact.coaching.values.map((coaching, index) => @@ -3826,7 +3911,8 @@ class ContactDetailScreen extends React.Component { - + {commentOrActivity.author} @@ -3840,7 +3926,8 @@ class ContactDetailScreen extends React.Component { - + {commentOrActivity.name} @@ -3851,7 +3938,7 @@ class ContactDetailScreen extends React.Component { )} - {Object.prototype.hasOwnProperty.call(commentOrActivity, 'content') ? commentOrActivity.content : this.formatActivityDate(commentOrActivity.object_note)} - + ); @@ -4116,7 +4210,7 @@ class ContactDetailScreen extends React.Component { {foundUser ? foundUser.label : ''} @@ -5542,6 +5636,7 @@ ContactDetailScreen.defaultProps = { saved: null, isConnected: null, contactSettings: null, + isRTL: false, }; const mapStateToProps = (state) => ({ @@ -5563,6 +5658,7 @@ const mapStateToProps = (state) => ({ foundGeonames: state.groupsReducer.foundGeonames, groupsList: state.groupsReducer.groups, contactsList: state.contactsReducer.contacts, + isRTL: state.i18nReducer.isRTL, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/screens/Group/GroupDetailScreen.js b/screens/Group/GroupDetailScreen.js index c1a81fe5..0b7f2016 100644 --- a/screens/Group/GroupDetailScreen.js +++ b/screens/Group/GroupDetailScreen.js @@ -1,5 +1,4 @@ import React from 'react'; -import { connect } from 'react-redux'; import { ScrollView, Keyboard, @@ -10,13 +9,14 @@ import { StyleSheet, Dimensions, FlatList, - TextInput, RefreshControl, Platform, TouchableHighlight, - StatusBar, BackHandler, + ActivityIndicator, } from 'react-native'; + +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ExpoFileSystemStorage from 'redux-persist-expo-filesystem'; import { Label, Input, Icon, Picker, DatePicker } from 'native-base'; @@ -26,8 +26,10 @@ import { Chip, Selectize } from 'react-native-material-selectize'; import { TabView, TabBar } from 'react-native-tab-view'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { NavigationActions, StackActions } from 'react-navigation'; -import moment from '../../languages/moment'; +import MentionsTextInput from 'react-native-mentions'; +import ParsedText from 'react-native-parsed-text'; +import moment from '../../languages/moment'; import sharedTools from '../../shared'; import { saveGroup, @@ -72,8 +74,9 @@ const spacing = windowWidth * 0.025; const sideSize = windowWidth - 2 * spacing; const circleSideSize = windowWidth / 3 + 20; let keyboardDidShowListener, keyboardDidHideListener, focusListener, hardwareBackPressListener; -const hasNotch = Platform.OS === 'android' && StatusBar.currentHeight > 25; -const extraNotchHeight = hasNotch ? StatusBar.currentHeight : 0; +//const hasNotch = Platform.OS === 'android' && StatusBar.currentHeight > 25; +//const extraNotchHeight = hasNotch ? StatusBar.currentHeight : 0; +const isIOS = Platform.OS === 'ios'; /* eslint-disable */ let commentsFlatList, coachesSelectizeRef, @@ -117,6 +120,7 @@ const tabViewRoutes = [ title: 'global.groups', }, ]; +let self; const styles = StyleSheet.create({ activeImage: { opacity: 1, @@ -425,6 +429,35 @@ const styles = StyleSheet.create({ paddingBottom: 8, textDecorationLine: 'underline', }, + suggestionsRowContainer: { + flexDirection: 'row', + }, + userIconBox: { + height: 45, + width: 45, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.tintColor, + }, + usernameInitials: { + color: '#fff', + fontWeight: '800', + fontSize: 14, + }, + userDetailsBox: { + flex: 1, + justifyContent: 'center', + paddingLeft: 10, + paddingRight: 15, + }, + displayNameText: { + fontSize: 13, + fontWeight: '500', + }, + usernameText: { + fontSize: 12, + color: 'rgba(0,0,0,0.6)', + }, }); const initialState = { @@ -463,6 +496,9 @@ const initialState = { footerHeight: 0, nameRequired: false, executingBack: false, + keyword: '', + suggestedUsers: [], + height: sharedTools.commentFieldMinHeight, }; const safeFind = (found, prop) => { @@ -471,6 +507,11 @@ const safeFind = (found, prop) => { }; class GroupDetailScreen extends React.Component { + constructor(props) { + super(props); + self = this; + } + static navigationOptions = ({ navigation }) => { const { params } = navigation.state; let navigationTitle = Object.prototype.hasOwnProperty.call(params, 'groupName') @@ -486,7 +527,7 @@ class GroupDetailScreen extends React.Component { name="check" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, + self.props.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, ]} /> @@ -505,7 +546,7 @@ class GroupDetailScreen extends React.Component { name="pencil" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, + self.props.isRTL ? { paddingLeft: 16 } : { paddingRight: 16 }, ]} /> @@ -531,7 +572,7 @@ class GroupDetailScreen extends React.Component { name="close" style={[ { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, - i18n.isRTL ? { paddingRight: 16 } : { paddingLeft: 16 }, + self.props.isRTL ? { paddingRight: 16 } : { paddingLeft: 16 }, ]} /> @@ -794,7 +835,6 @@ class GroupDetailScreen extends React.Component { if (newComment && prevProps.newComment !== newComment) { commentsFlatList.scrollToOffset({ animated: true, offset: 0 }); this.setComment(''); - this.setHeight(); } // GROUP SAVE / GET BY ID @@ -869,7 +909,7 @@ class GroupDetailScreen extends React.Component { keyboardDidShow(event) { this.setState({ - footerLocation: event.endCoordinates.height + extraNotchHeight, + footerLocation: isIOS ? event.endCoordinates.height /*+ extraNotchHeight*/ : 0, }); } @@ -1273,12 +1313,14 @@ class GroupDetailScreen extends React.Component { - + {commentOrActivity.author} - + {this.onFormatDateToView(commentOrActivity.date)} @@ -1289,12 +1331,14 @@ class GroupDetailScreen extends React.Component { - + {commentOrActivity.name} - + {this.onFormatDateToView(commentOrActivity.date)} @@ -1302,16 +1346,23 @@ class GroupDetailScreen extends React.Component { )} - + ? [styles.commentMessage, this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}] + : [styles.activityMessage, this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}] + } + parse={[ + { + pattern: sharedTools.mentionPattern, + style: { color: Colors.primary }, + renderText: sharedTools.renderMention, + }, + ]}> {Object.prototype.hasOwnProperty.call(commentOrActivity, 'content') ? commentOrActivity.content : this.formatActivityDate(commentOrActivity.object_note)} - + ); @@ -1450,14 +1501,6 @@ class GroupDetailScreen extends React.Component { }); }; - setHeight = (newHeight = 0) => { - newHeight = Math.max(sharedTools.commentFieldMinHeight, newHeight); - this.setState({ - height: Math.min(sharedTools.commentFieldMinContainerHeight, newHeight), - footerHeight: Math.min(sharedTools.commentFieldMinContainerHeight, newHeight) + 20, - }); - }; - onAddMember = (selectedValue) => { this.setState((prevState) => ({ group: { @@ -1716,7 +1759,7 @@ class GroupDetailScreen extends React.Component { {foundUser ? foundUser.label : ''} @@ -1802,7 +1845,7 @@ class GroupDetailScreen extends React.Component { fontWeight: 'bold', marginTop: 10, }, - i18n.isRTL ? { textAlign: 'left', flex: 1 } : {}, + this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}, ]}> {this.props.groupSettings.fields.group_status.name} @@ -1829,7 +1872,7 @@ class GroupDetailScreen extends React.Component { backgroundColor: this.state.groupStatusBackgroundColor, }, }), - i18n.isRTL ? { textAlign: 'left', flex: 1 } : {}, + this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}, ]} textStyle={{ color: '#ffffff', @@ -1865,7 +1908,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.coaches ? ( this.state.group.coaches.values.map((coach, index) => @@ -1908,7 +1951,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.location_grid ? this.state.group.location_grid.values @@ -1938,7 +1981,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.people_groups ? this.state.group.people_groups.values @@ -1968,7 +2011,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.contact_address ? this.state.group.contact_address.map((address) => address.value).join(', ') @@ -1990,7 +2033,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.start_date ? moment(new Date(this.state.group.start_date * 1000)).format('LL') @@ -2012,7 +2055,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.church_start_date ? moment(new Date(this.state.group.church_start_date * 1000)).format('LL') @@ -2034,7 +2077,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.end_date ? moment(new Date(this.state.group.end_date * 1000)).format('LL') @@ -2091,7 +2134,7 @@ class GroupDetailScreen extends React.Component { backgroundColor: this.state.groupStatusBackgroundColor, }, }), - i18n.isRTL ? { textAlign: 'left', flex: 1 } : {}, + this.props.isRTL ? { textAlign: 'left', flex: 1 } : {}, ]} textStyle={{ color: '#ffffff', @@ -2577,7 +2620,7 @@ class GroupDetailScreen extends React.Component { {this.state.group.group_type ? this.props.groupSettings.fields.group_type.values[ @@ -2656,6 +2699,46 @@ class GroupDetailScreen extends React.Component { ); + onSuggestionTap(username, hidePanel) { + hidePanel(); + let comment = this.state.comment.slice(0, -this.state.keyword.length), + mentionFormat = `@[${username.label}](${username.key})`; + this.setState({ + suggestedUsers: [], + comment: `${comment}${mentionFormat}`, + }); + } + + filterUsers(keyword) { + let newKeyword = keyword.replace('@', ''); + this.setState((state) => { + return { + suggestedUsers: state.users.filter((user) => + user.label.toLowerCase().includes(newKeyword.toLowerCase()), + ), + keyword, + }; + }); + } + + renderSuggestionsRow({ item }, hidePanel) { + return ( + this.onSuggestionTap(item, hidePanel)}> + + + + {!!item.label && item.label.substring(0, 2).toUpperCase()} + + + + {item.label} + @{item.label} + + + + ); + } + commentsView = () => ( {this.state.comments.length == 0 && @@ -2734,37 +2817,44 @@ class GroupDetailScreen extends React.Component { } }} /> - - + this.setHeight(event.nativeEvent.contentSize.height)} - editable={!this.state.loadComments} - multiline - style={[ - { - borderColor: '#B4B4B4', - borderRadius: 5, - borderWidth: 1, - flex: 1, - margin: 10, - paddingLeft: 5, - paddingRight: 5, - height: this.state.height, - }, - i18n.isRTL ? { textAlign: 'right', flex: 1 } : {}, - this.state.loadComments ? { backgroundColor: '#e6e6e6' } : { backgroundColor: 'white' }, - ]} + style={this.props.isRTL ? { textAlign: 'right', flex: 1 } : {}} + textInputStyle={{ + borderColor: '#B4B4B4', + borderRadius: 5, + borderWidth: 1, + padding: 5, + margin: 10, + width: windowWidth - 80, + backgroundColor: this.state.loadComments ? '#e6e6e6' : '#FFFFFF', + }} + loadingComponent={() => ( + + + + )} + textInputMinHeight={40} + textInputMaxHeight={80} + trigger={'@'} + triggerLocation={'new-word-only'} + triggerCallback={this.filterUsers.bind(this)} + renderSuggestionsRow={this.renderSuggestionsRow.bind(this)} + suggestionsData={this.state.suggestedUsers} + keyExtractor={(item, index) => item.key.toString()} + suggestionRowHeight={45} + horizontal={false} + MaxVisibleRowCount={3} /> this.onSaveComment()} @@ -2772,16 +2862,20 @@ class GroupDetailScreen extends React.Component { { borderRadius: 80, height: 40, - margin: 10, - paddingTop: 7, width: 40, + paddingTop: 7, + marginRight: 10, + marginBottom: 10, + position: 'absolute', + right: 0, + bottom: 0, }, this.state.loadComments ? { backgroundColor: '#e6e6e6' } : { backgroundColor: Colors.tintColor }, - i18n.isRTL ? { paddingRight: 10 } : { paddingLeft: 10 }, + this.props.isRTL ? { paddingRight: 10 } : { paddingLeft: 10 }, ]}> - + @@ -2823,7 +2917,9 @@ class GroupDetailScreen extends React.Component { {membersGroup.name} @@ -4214,6 +4310,7 @@ const mapStateToProps = (state) => ({ foundGeonames: state.groupsReducer.foundGeonames, groupsList: state.groupsReducer.groups, contactsList: state.contactsReducer.contacts, + isRTL: state.i18nReducer.isRTL, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/screens/NotificationsScreen.js b/screens/NotificationsScreen.js index 10e2b39f..94afaa5f 100644 --- a/screens/NotificationsScreen.js +++ b/screens/NotificationsScreen.js @@ -317,7 +317,7 @@ class NotificationsScreen extends React.Component { }> - + {newNotificationNoteA} - + {moment(notification.date_notified).fromNow() + ' | ' + moment(notification.date_notified).format('L')} @@ -485,6 +486,7 @@ const mapStateToProps = (state) => ({ contactSettings: state.contactsReducer.settings, isConnected: state.networkConnectivityReducer.isConnected, notificationsCount: state.notificationsReducer.notificationsCount, + isRTL: state.i18nReducer.isRTL, }); const mapDispatchToProps = (dispatch) => ({ getAllNotifications: (domain, token, all, offset, limit) => { diff --git a/screens/SettingsScreen.js b/screens/SettingsScreen.js index 6d94b3c5..01150b5e 100644 --- a/screens/SettingsScreen.js +++ b/screens/SettingsScreen.js @@ -90,10 +90,6 @@ const styles = StyleSheet.create({ fontStyle: 'italic', color: '#888', }, - text: { - writingDirection: i18n.isRTL ? 'rtl' : 'ltr', - textAlign: i18n.isRTL ? 'right' : 'left', - }, body: { alignItems: 'flex-start', }, @@ -341,8 +337,26 @@ class SettingsScreen extends React.Component { - {this.props.userData.displayName} - {this.props.userData.domain} + + {this.props.userData.displayName} + + + {this.props.userData.domain} + @@ -355,10 +369,16 @@ class SettingsScreen extends React.Component { - {i18n.t('settingsScreen.storybook')} + + {i18n.t('settingsScreen.storybook')} + - + )} @@ -370,7 +390,13 @@ class SettingsScreen extends React.Component { - {i18n.t('global.online')} + + {i18n.t('global.online')} + @@ -384,7 +410,13 @@ class SettingsScreen extends React.Component { - {i18n.t('global.language')} + + {i18n.t('global.language')} + - {i18n.t('settingsScreen.rememberPassword')} + + {i18n.t('settingsScreen.rememberPassword')} + @@ -417,7 +455,11 @@ class SettingsScreen extends React.Component { - + {`${ this.props.pinCode.enabled ? i18n.t('settingsScreen.remove') @@ -434,21 +476,33 @@ class SettingsScreen extends React.Component { - {i18n.t('settingsScreen.helpSupport')} + + {i18n.t('settingsScreen.helpSupport')} + {/* === Logout === */} - + - {i18n.t('settingsScreen.logout')} + + {i18n.t('settingsScreen.logout')} + - + diff --git a/shared/index.js b/shared/index.js index 94af4134..6b07008c 100644 --- a/shared/index.js +++ b/shared/index.js @@ -185,7 +185,7 @@ const debounce = (fn, time) => { }; }; -const commentFieldMinHeight = 35; +const commentFieldMinHeight = 50; const commentFieldMinContainerHeight = 120; let handle, onlyExecuteLastCall = (parameter, functionName, timeout) => { @@ -198,6 +198,15 @@ let handle, }, timeout); }; +const mentionPattern = /@\[.+?\]\((.*)\)/g; +const renderMention = (matchingString, matches) => { + let mentionText = matchingString.substring( + matchingString.lastIndexOf('[') + 1, + matchingString.lastIndexOf(']'), + ); + return `@${mentionText}`; +}; + export default { diff, formatDateToBackEnd, @@ -206,4 +215,6 @@ export default { commentFieldMinHeight, commentFieldMinContainerHeight, onlyExecuteLastCall, + mentionPattern, + renderMention, }; From 94c42ddf93be61af4909c2769ad5f06bfcdf4560 Mon Sep 17 00:00:00 2001 From: zdmc23 <191707+zdmc23@users.noreply.github.com> Date: Sat, 11 Jul 2020 17:02:28 -0400 Subject: [PATCH 13/14] Resolves #363 - distinguish between baptism icons by using FontAwesome5/water in addition to Entypo/water --- screens/Contact/ContactDetailScreen.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index 5d1c13fc..098c3b1f 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -3087,7 +3087,11 @@ class ContactDetailScreen extends React.Component { - + - + From cf05d4cfc118994c0efdea3e62d022dd4dce5935 Mon Sep 17 00:00:00 2001 From: zdmc23 <191707+zdmc23@users.noreply.github.com> Date: Sat, 11 Jul 2020 17:40:39 -0400 Subject: [PATCH 14/14] Resolves #68 - update icons to better match D.T site --- screens/Contact/ContactDetailScreen.js | 54 +++++++++++++++----------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index 098c3b1f..5ca7474c 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -1751,7 +1751,7 @@ class ContactDetailScreen extends React.Component { - + - + - + - + @@ -2441,8 +2441,8 @@ class ContactDetailScreen extends React.Component { @@ -2462,7 +2462,11 @@ class ContactDetailScreen extends React.Component { - + @@ -2475,8 +2479,8 @@ class ContactDetailScreen extends React.Component { @@ -2496,11 +2500,7 @@ class ContactDetailScreen extends React.Component { - + @@ -2601,7 +2601,11 @@ class ContactDetailScreen extends React.Component { - + - + @@ -5230,7 +5238,7 @@ class ContactDetailScreen extends React.Component { - + @@ -5243,8 +5251,8 @@ class ContactDetailScreen extends React.Component {