diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7201c639..38486851 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -7,18 +7,18 @@ on: - development jobs: publish: - name: CI/CD + name: CI/CD runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: 12.x - - uses: microsoft/variable-substitution@v1 - with: - files: './app.json' - env: - expo.hooks.postPublish.0.config.authToken: ${{ secrets.SENTRY_AUTH_TOKEN }} + #- uses: microsoft/variable-substitution@v1 + # with: + # files: './app.json' + # env: + # expo.hooks.postPublish.0.config.authToken: ${{ secrets.SENTRY_AUTH_TOKEN }} - uses: microsoft/variable-substitution@v1 with: files: './google-services.json' diff --git a/App.js b/App.js index 89a030ba..c2243593 100644 --- a/App.js +++ b/App.js @@ -8,8 +8,8 @@ import * as Font from 'expo-font'; import { Asset } from 'expo-asset'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; -import Reactotron from 'reactotron-react-native'; -import * as Sentry from 'sentry-expo'; +//import Reactotron from 'reactotron-react-native'; +//import * as Sentry from 'sentry-expo'; import AppNavigator from './navigation/AppNavigator'; import { store, persistor } from './store/store'; @@ -19,11 +19,11 @@ import sharedTools from './shared'; import { setNetworkConnectivity } from './store/actions/networkConnectivity.actions'; -Sentry.init({ +/*Sentry.init({ dsn: 'https://aaa9d833ba5942d59c69e290ffbd3f36@o424480.ingest.sentry.io/5356329', enableInExpoDevelopment: true, debug: true, -}); +});*/ // Styles const styles = StyleSheet.create({ @@ -59,9 +59,9 @@ class App extends React.Component { // If Reactotron gets no connection, this is the solution that worked for me (cairocoder01: 2019-08-15) // https://github.com/expo/expo-cli/issues/153#issuecomment-358925525 // May need to then run this before `npm start`: `adb reverse tcp:9090 tcp:9090` - Reactotron.configure() // controls connection & communication settings - .useReactNative() // add all built-in react native plugins - .connect(); // let's connect! + //Reactotron.configure() // controls connection & communication settings + // .useReactNative() // add all built-in react native plugins + // .connect(); // let's connect! } // Handle notifications that are received or selected while the app diff --git a/app.json b/app.json index f671e76e..2261f972 100644 --- a/app.json +++ b/app.json @@ -4,7 +4,7 @@ "slug": "discipletoolsapp", "privacy": "public", "platforms": ["ios", "android"], - "version": "1.6.0", + "version": "1.7.0", "orientation": "portrait", "icon": "./assets/images/icon.png", "splash": { @@ -23,20 +23,8 @@ "android": { "package": "tools.disciple.app", "googleServicesFile": "./google-services.json", - "versionCode": 160 + "versionCode": 170 }, - "description": "", - "hooks": { - "postPublish": [ - { - "file": "sentry-expo/upload-sourcemaps", - "config": { - "organization": "Disciple Tools", - "project": "DT Mobile", - "authToken": "" - } - } - ] - } + "description": "" } } diff --git a/constants/Colors.js b/constants/Colors.js index 810a9ebd..4aeea22a 100644 --- a/constants/Colors.js +++ b/constants/Colors.js @@ -4,6 +4,12 @@ const tintColorRGBA = 'rgba(54,93,134,1)'; const grayLight = '#eeeeee'; const gray = '#cccccc'; const grayDark = '#666666'; +const colorYes = '#2ABE1E'; +const colorNo = '#C70039'; +const colorWait = tintColor; +const addRemoveIcons = tintColor; +const addIcons = 'green'; +const removeIcons = 'red'; export default { primary: tintColor, @@ -18,6 +24,8 @@ export default { tabIconDefault: gray, tabIconSelected: tintColor, tabBar: '#fefefe', + headerTintColor: '#FFFFFF', + onPressIcons: '#FFFFFF', successBackground: 'rgba(229, 229, 229, 0.85)', sucessText: '#000', errorBackground: 'rgba(228, 132, 129, 0.85)', @@ -26,4 +34,15 @@ export default { warningText: '#ffa0aa', noticeBackground: tintColor, noticeText: '#fff', + colorYes: '#2ABE1E', + colorNo: '#C70039', + colorWait: tintColor, + addRemoveIcons: tintColor, + addIcons: 'green', + removeIcons: 'red', + button: tintColor, + buttonText: '#FFFFFF', + dialogButton: '#FFFFFF', + buttonDelete: '#d9534f', + iconDelete: '#d9534f', }; diff --git a/languages/ar.json b/languages/ar.json index ab0deb86..e3e58112 100644 --- a/languages/ar.json +++ b/languages/ar.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "اكتب تعليقك أو ملاحظتك هنا", "requiredField": "حقل مطلوب", "edit": "تغيير", + "delete": "حذف", "cancel": "إلغاء", "save": "حفظ", "online": "متصل بالأنترنت", @@ -25,13 +26,9 @@ "membersActivity": "أفراد", "moreFields": "المزيد من الحقول", "nameRequired": "الاسم مطلوب", - "close": "قريب" - }, - "appRestart": { - "message": "سيتم إعادة تشغيل التطبيق لتطبيق التكوين التالي:", - "button": "حسنا", - "textDirection": "اتجاه النص", - "selectedLanguage": "اللغة المختارة" + "close": "قريب", + "editComment": "تعديل", + "deleteComment": "حذف" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "تذكر كلمة المرور", "rememberPasswordActive": "تمكين تسجيل الدخول باستمرار", "rememberPasswordInactive": "حافظ على تسجيل الدخول معطلة", - "remove": "إزالة رمز PIN", - "set": "قم بتعيين رمز PIN", + "remove": "إزالة", + "set": "جلس", "pinCode": "كود PIN", - "enterPin": "أدخل رمز PIN", + "enterPin": "أدخل رقم التعريف الشخصي", "setPin": "قم بتعيين رقم تعريف شخصي جديد", - "incorrectPin": "رمز PIN غير صحيح", - "savedPinCode": "تم حفظ رمز PIN بنجاح!", - "removedPinCode": "تمت إزالة رمز PIN بنجاح!", - "close": "أغلق" + "incorrectPin": "رقم التعريف الشخصي غير صحيح", + "savedPinCode": "تم حفظ رمز PIN بنجاح", + "removedPinCode": "تمت إزالة رمز PIN بنجاح", + "close": "قريب" }, "contactDetailScreen": { "addNewContact": "إضافة جهة اتصال جديدة", @@ -121,6 +118,11 @@ "groupName": { "label": "أسم المجموعة", "error": "مطلوب اسم" + }, + "fab": { + "quick_button_meeting_scheduled": "جدول الاجتماع", + "quick_button_meeting_postponed": "تم تأجيل الاجتماع", + "quick_button_meeting_complete": "اكتمل الاجتماع" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "لا توجد أي مجموعات متاحة حاليًا.", "noGroupPlacheHolder1": "ربما كانت هناك مشكلة في الحصول عليها من موقعك. اسحب لأسفل لمحاولة التحديث والمحاولة مرة أخرى.", "noGroupPlacheHolderOffline": "أنت غير متصل حاليًا. تأكد من اتصالك بشبكة wifi أو بيانات الجوال من أجل التحديث والحصول على مجموعاتك." + }, + "appRestart": { + "message": "سيتم إعادة تشغيل التطبيق لتطبيق التكوين التالي:", + "button": "حسنا", + "textDirection": "اتجاه النص", + "selectedLanguage": "اللغة المختارة" } } diff --git a/languages/bn.json b/languages/bn.json index 59d301da..6b838865 100644 --- a/languages/bn.json +++ b/languages/bn.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "আপনার মন্তব্য বা নোট এখানে লিখুন", "requiredField": "প্রয়োজনীয় ক্ষেত্র", "edit": "সম্পাদন করা", + "delete": "মুছে ফেলা", "cancel": "বাতিল", "save": "সংরক্ষণ করুন", "online": "অনলাইন", @@ -25,13 +26,9 @@ "membersActivity": "সদস্য", "moreFields": "আরও ক্ষেত্র", "nameRequired": "নাম (প্রয়োজন", - "close": "ঘনিষ্ঠ" - }, - "appRestart": { - "message": "নিম্নলিখিত কনফিগারেশন প্রয়োগ করার জন্য অ্যাপ্লিকেশনটি আবার শুরু হবে:", - "button": "ঠিক আছে", - "textDirection": "লেখার দিকবিন্যাস", - "selectedLanguage": "নির্বাচিত ভাষা" + "close": "ঘনিষ্ঠ", + "editComment": "সম্পাদনা করুন", + "deleteComment": "মুছে ফেলা" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "পাসওয়ার্ড মনে", "rememberPasswordActive": "সক্ষম থাকা লগ ইন করুন", "rememberPasswordInactive": "লগ ইন অক্ষম রাখুন", - "remove": "পিন কোড সরান", - "set": "পিন কোড সেট করুন", + "remove": "অপসারণ", + "set": "সেট", "pinCode": "পিনকোড", - "enterPin": "পিন কোড প্রবেশ করান", + "enterPin": "পিন প্রবেশ করান", "setPin": "নতুন পিন সেট করুন", - "incorrectPin": "ভুল পিন কোড", - "savedPinCode": "পিন কোড সফলভাবে সংরক্ষণ করা হয়েছে!", - "removedPinCode": "পিন কোড সফলভাবে সরানো হয়েছে!", - "close": "ঘনিষ্ঠ" + "incorrectPin": "ভুল পিন", + "savedPinCode": "পিন কোড সাফল্যের সাথে সংরক্ষিত হয়েছে", + "removedPinCode": "পিন কোড সফলভাবে সরানো হয়েছে", + "close": "বন্ধ" }, "contactDetailScreen": { "addNewContact": "নতুন পরিচিতি যোগ করুন", @@ -121,6 +118,11 @@ "groupName": { "label": "দলের নাম", "error": "নাম প্রয়োজন" + }, + "fab": { + "quick_button_meeting_scheduled": "সভা নির্ধারিত", + "quick_button_meeting_postponed": "সভা স্থগিত", + "quick_button_meeting_complete": "সভা সম্পূর্ণ" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "বর্তমানে কোনও গোষ্ঠী উপলব্ধ নেই।", "noGroupPlacheHolder1": "আপনার সাইট থেকে এগুলি পেতে কোনও সমস্যা হতে পারে। রিফ্রেশ করার চেষ্টা করতে নীচে টানুন এবং আবার চেষ্টা করুন।", "noGroupPlacheHolderOffline": "আপনি বর্তমানে অফলাইন আপনার গ্রুপগুলি রিফ্রেশ করার জন্য আপনি ওয়াইফাই বা মোবাইল ডেটার সাথে সংযুক্ত আছেন তা নিশ্চিত করুন।" + }, + "appRestart": { + "message": "নিম্নলিখিত কনফিগারেশন প্রয়োগ করার জন্য অ্যাপ্লিকেশনটি পুনরায় শুরু হবে:", + "button": "ঠিক আছে", + "textDirection": "লেখার দিকবিন্যাস", + "selectedLanguage": "নির্বাচিত ভাষা" } } diff --git a/languages/en.json b/languages/en.json index 9e23d671..b90bcd51 100644 --- a/languages/en.json +++ b/languages/en.json @@ -16,22 +16,19 @@ "writeYourCommentNoteHere": "Write your comment or note here", "requiredField": "Required field", "edit": "Edit", + "delete": "Delete", "cancel": "Cancel", "save": "Save", "online": "Online", - "offline": "offline", + "offline": "Offline", "language": "Language", "search": "Search", "membersActivity": "Members", "moreFields": "More Fields", "nameRequired": "Name required", - "close": "Close" - }, - "appRestart": { - "message": "The application will be restarted to apply the following configuration:", - "button": "Ok", - "textDirection": "Text direction", - "selectedLanguage": "Selected language" + "close": "Close", + "editComment": "Edit", + "deleteComment": "Delete" }, "loginScreen": { "domain": { @@ -121,6 +118,11 @@ "groupName": { "label": "Group Name", "error": "Name is required" + }, + "fab": { + "quick_button_meeting_scheduled": "Meeting Scheduled", + "quick_button_meeting_postponed": "Meeting Postponed", + "quick_button_meeting_complete": "Meeting Complete" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "There are not any groups currently available.", "noGroupPlacheHolder1": "There may have been a problem getting them from your site. Drag down to try to refresh and try again.", "noGroupPlacheHolderOffline": "You are currently offline. Ensure you are connected to wifi or mobile data in order to refresh and get your groups." + }, + "appRestart": { + "message": "The application will be restarted to apply the following configuration:", + "button": "Ok", + "textDirection": "Text direction", + "selectedLanguage": "Selected language" } } diff --git a/languages/es.json b/languages/es.json index c8d44ac4..18f9989b 100644 --- a/languages/es.json +++ b/languages/es.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Escriba su comentario o nota aquí", "requiredField": "Campo requerido", "edit": "Editar", + "delete": "Eliminar", "cancel": "Cancelar", "save": "Guardar", "online": "En línea", @@ -25,13 +26,9 @@ "membersActivity": "Miembros", "moreFields": "Más Campos", "nameRequired": "Nombre requerido", - "close": "Cerrar" - }, - "appRestart": { - "message": "La aplicación sera reiniciada para aplicar la siguiente configuración:", - "button": "De acuerdo", - "textDirection": "Dirección del texto", - "selectedLanguage": "Lenguaje seleccionado" + "close": "Cerrar", + "editComment": "Editar", + "deleteComment": "Eliminar" }, "loginScreen": { "domain": { @@ -121,6 +118,11 @@ "groupName": { "label": "Nombre del Grupo", "error": "El nombre es requerido" + }, + "fab": { + "quick_button_meeting_scheduled": "Reunión Programada", + "quick_button_meeting_postponed": "Reunión Pospuso", + "quick_button_meeting_complete": "Reunión Completa" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "No hay grupos disponibles actualmente.", "noGroupPlacheHolder1": "Es posible que haya habido un problema para obtenerlos de su sitio. Arrastre hacia abajo para intentar actualizar e intente nuevamente.", "noGroupPlacheHolderOffline": "Actualmente estás desconectado. Asegúrese de estar conectado a wifi o datos móviles para actualizar y obtener sus grupos." + }, + "appRestart": { + "message": "La aplicación sera reiniciada para aplicar la siguiente configuración:", + "button": "De acuerdo", + "textDirection": "Dirección del texto", + "selectedLanguage": "Lenguaje seleccionado" } } diff --git a/languages/fa.json b/languages/fa.json index 3617ce33..945acc00 100644 --- a/languages/fa.json +++ b/languages/fa.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "نظر یا یادداشت خود را اینجا بنویسید", "requiredField": "زمینه مورد نیاز", "edit": "ویرایش کنید", + "delete": "حذف", "cancel": "لغو", "save": "صرفه جویی", "online": "برخط", @@ -25,13 +26,9 @@ "membersActivity": "اعضا", "moreFields": "زمینه های بیشتر", "nameRequired": "نام لازم است", - "close": "نزدیک" - }, - "appRestart": { - "message": "برنامه برای اجرای پیکربندی زیر مجدداً راه اندازی می شود:", - "button": "خوب", - "textDirection": "جهت متن", - "selectedLanguage": "زبان انتخاب شده" + "close": "نزدیک", + "editComment": "ویرایش کنید", + "deleteComment": "حذف" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "به یاد داشته باشید کلمه کلیدی", "rememberPasswordActive": "فعال شوید", "rememberPasswordInactive": "وارد سیستم غیرفعال شوید", - "remove": "کد پین را حذف کنید", - "set": "کد پین را تنظیم کنید", + "remove": "برداشتن", + "set": "تنظیم", "pinCode": "کد پین", - "enterPin": "کد پین را وارد کنید", - "setPin": "پین جدید را تنظیم کنید", - "incorrectPin": "کد پین نادرست", - "savedPinCode": "کد پین با موفقیت ذخیره شد!", - "removedPinCode": "کد پین با موفقیت حذف شد!", + "enterPin": "پین را وارد کنید", + "setPin": "تنظیم پین جدید", + "incorrectPin": "پین نادرست", + "savedPinCode": "کد پین با موفقیت ذخیره شد", + "removedPinCode": "کد پین با موفقیت حذف شد", "close": "نزدیک" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "اسم گروه", "error": "نام لازم است" + }, + "fab": { + "quick_button_meeting_scheduled": "جلسه برنامه ریزی شده", + "quick_button_meeting_postponed": "جلسه به تعویق افتاد", + "quick_button_meeting_complete": "جلسه کامل است" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "در حال حاضر هیچ گروهی موجود نیست", "noGroupPlacheHolder1": "ممکن است مشکلی در گرفتن آنها از سایت شما وجود داشته باشد. پایین بکشید تا سعی کنید تازه کنید و دوباره امتحان کنید.", "noGroupPlacheHolderOffline": "شما در حال حاضر آفلاین هستید. برای اطمینان از گروه های خود ، اطمینان حاصل کنید که به داده های wifi یا تلفن همراه وصل شده اید." + }, + "appRestart": { + "message": "برنامه برای اجرای پیکربندی زیر مجدداً راه اندازی می شود:", + "button": "خوب", + "textDirection": "جهت متن", + "selectedLanguage": "زبان انتخاب شده" } } diff --git a/languages/fr.json b/languages/fr.json index efe66ea0..d1cc4036 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Écrivez votre commentaire ou note ici", "requiredField": "champs requis", "edit": "modifier", + "delete": "Supprimer", "cancel": "Annuler", "save": "sauver", "online": "En ligne", @@ -25,13 +26,9 @@ "membersActivity": "Membres", "moreFields": "Plus de champs", "nameRequired": "Nom obligatoire", - "close": "proche" - }, - "appRestart": { - "message": "L'application sera redémarrée pour appliquer la configuration suivante:", - "button": "D'accord", - "textDirection": "Direction du texte", - "selectedLanguage": "Langue choisie" + "close": "proche", + "editComment": "Éditer", + "deleteComment": "Supprimer" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "se souvenir du mot de passe", "rememberPasswordActive": "Rester connecté activé", "rememberPasswordInactive": "Rester connecté désactivé", - "remove": "Supprimer le code PIN", - "set": "Définir le code PIN", + "remove": "Retirer", + "set": "Ensemble", "pinCode": "Code PIN", "enterPin": "Entrez le code PIN", "setPin": "Définir un nouveau code PIN", - "incorrectPin": "Code PIN incorrect", - "savedPinCode": "Le code PIN a été enregistré avec succès!", - "removedPinCode": "Le code PIN a été supprimé avec succès!", + "incorrectPin": "PIN incorrect", + "savedPinCode": "Code PIN enregistré avec succès", + "removedPinCode": "Code PIN supprimé avec succès", "close": "Fermer" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "Nom de groupe", "error": "Nom obligatoire" + }, + "fab": { + "quick_button_meeting_scheduled": "Réunion programmée", + "quick_button_meeting_postponed": "Réunion reportée", + "quick_button_meeting_complete": "Réunion terminée" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Aucun groupe n'est actuellement disponible.", "noGroupPlacheHolder1": "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.", "noGroupPlacheHolderOffline": "Vous êtes actuellement hors ligne. Assurez-vous que vous êtes connecté au wifi ou aux données mobiles afin de rafraîchir et d'obtenir vos groupes." + }, + "appRestart": { + "message": "L'application sera redémarrée pour appliquer la configuration suivante:", + "button": "D'accord", + "textDirection": "Direction du texte", + "selectedLanguage": "Langue sélectionnée" } } diff --git a/languages/hr.json b/languages/hr.json new file mode 100644 index 00000000..754ad822 --- /dev/null +++ b/languages/hr.json @@ -0,0 +1,149 @@ +{ + "global": { + "error": { + "code": "Kodirati: ", + "message": "Poruka: " + }, + "success": { + "save": "Podaci su uspješno spremljeni!" + }, + "groups": "Grupe", + "details": "Pojedinosti", + "selectPeopleGroups": "Odaberite Grupe ljudi", + "address": "Adresa", + "progress": "Napredak", + "commentsActivity": "Komentari i aktivnosti", + "writeYourCommentNoteHere": "Ovdje napišite svoj komentar ili bilješku", + "requiredField": "Obavezno polje", + "edit": "Uredi", + "delete": "Izbrisati", + "cancel": "Otkazati", + "save": "Uštedjeti", + "online": "Na liniji", + "offline": "offline", + "language": "Jezik", + "search": "traži", + "membersActivity": "Članovi", + "moreFields": "Još polja", + "nameRequired": "Ime (obavezno", + "close": "Zatvoriti", + "editComment": "Uredi", + "deleteComment": "Izbrisati" + }, + "loginScreen": { + "domain": { + "label": "URL", + "placeholder": "Npr. myteam.mydomain.com", + "error": "Domena je obavezna", + "errorForgotPass": "Unesite URL da biste započeli s preuzimanjem lozinke" + }, + "username": { + "label": "Korisničko ime", + "error": "Korisničko ime je obavezno" + }, + "password": { + "label": "Zaporka", + "error": "potrebna je lozinka" + }, + "logIn": "Prijaviti se", + "forgotPassword": "Izgubili ste lozinku?", + "errors": { + "invalidUsername": "Nepoznato korisničko ime. Provjerite ponovo ili isprobajte svoju adresu e-pošte.", + "incorrectPassword": "Lozinka koju ste unijeli za korisničko ime je netočna" + } + }, + "contactsScreen": { + "contacts": "Kontakti", + "noContactPlacheHolder": "Trenutno nema dostupnih kontakata.", + "noContactPlacheHolder1": "Možda je došlo do problema pri preuzimanju s vaše web lokacije. Povucite prema dolje da biste se pokušali osvježiti i pokušajte ponovo.", + "noContactPlacheHolderOffline": "Trenutno ste offline. Provjerite jeste li povezani na WiFi ili mobilne podatke kako biste osvježili i dobili svoje kontakte." + }, + "settingsScreen": { + "settings": "Postavke", + "networkUnavailable": "Mreža nije dostupna. Sada u OFFLINE načinu", + "networkAvailable": "Otkrivena mreža. Povratak u ONLINE način", + "storybook": "Knjiga pripovjedaka", + "logout": "Odjavi se", + "helpSupport": "Pomoć / podrška", + "rememberPassword": "Zapamti lozinku", + "rememberPasswordActive": "Neka prijava bude omogućena", + "rememberPasswordInactive": "Ostanite prijavljeni onemogućeni", + "remove": "Ukloniti", + "set": "Postavi", + "pinCode": "PIN kod", + "enterPin": "Unesite PIN", + "setPin": "Postavite novi PIN", + "incorrectPin": "Neispravan PIN", + "savedPinCode": "PIN kod je uspješno spremljen", + "removedPinCode": "PIN kod je uspješno uklonjen", + "close": "Zatvoriti" + }, + "contactDetailScreen": { + "addNewContact": "Dodaj novi kontakt", + "subAssignThisContact": "Pododijelite ovaj kontakt", + "mobile": "Telefon", + "selectLocations": "Odaberite Lokacije", + "selectSources": "Odaberite izvore", + "connections": "Veze", + "addGroup": "Dodaj grupu", + "addConnection": "Dodaj vezu", + "addBaptizedBy": "Dodaj krštenik", + "addBaptized": "Dodaj kršteno", + "addCoachedBy": "Dodaj trenirao", + "addCoaching": "Dodajte treniranje", + "phoneNumber": "Broj telefona", + "initialComment": "Početni komentar", + "socialMedia": "Društveni mediji", + "noContactCommentPlacheHolder": "Trenutno nema dostupnih komentara na kontakt.", + "fullName": { + "label": "Puno ime", + "error": "Ime je obavezno" + }, + "noContactCommentPlacheHolder1": "Možda je došlo do problema pri preuzimanju s vaše web lokacije. Povucite prema dolje da biste se pokušali osvježiti i pokušajte ponovo.", + "noContactCommentPlacheHolderOffline": "Trenutno ste offline. Provjerite jeste li povezani na WiFi ili mobilne podatke kako biste osvježili i dobili komentar na kontakt." + }, + "groupDetailScreen": { + "addNewGroup": "Dodaj novu grupu", + "selectCoaches": "Odaberite Grupni trener", + "searchGroups": "Pretražite grupe", + "searchPeerGroups": "Pretražite grupe vršnjaka", + "childGroup": "Dječja grupa", + "searchChildGroups": "Pretražite dječje skupine", + "addMember": "Dodaj člana", + "noGroupCommentPlacheHolder": "Trenutno nema dostupnih komentara grupe.", + "noGroupCommentPlacheHolder1": "Možda je došlo do problema pri preuzimanju s vaše web lokacije. Povucite prema dolje da biste se pokušali osvježiti i pokušajte ponovo.", + "noGroupCommentPlacheHolderOffline": "Trenutno ste offline. Provjerite jeste li povezani na WiFi ili mobilne podatke kako biste osvježili i dobili komentar svoje grupe.", + "selectLocations": "Odaberite Lokacije", + "noMembersMessage": "Nema članova. Dodirnite ovdje ili na Uredi da biste dodali neke članove u ovu grupu.", + "groupName": { + "label": "Grupno ime", + "error": "Ime je obavezno" + }, + "fab": { + "quick_button_meeting_scheduled": "Sastanak zakazan", + "quick_button_meeting_postponed": "Sastanak odgođen", + "quick_button_meeting_complete": "Sastanak završen" + } + }, + "notificationsScreen": { + "notifications": "Obavijesti", + "loadMore": "Učitaj više", + "new": "Novi", + "unRead": "Nepročitano", + "all": "svi", + "markAll": "Označi sve", + "dontHaveNotificationsUnread": "Nema nepročitanih obavijesti", + "dontHaveNotifications": "Nema obavijesti" + }, + "groupsScreen": { + "noGroupPlacheHolder": "Trenutno nema dostupnih grupa.", + "noGroupPlacheHolder1": "Možda je došlo do problema pri preuzimanju s vaše web lokacije. Povucite prema dolje da biste se pokušali osvježiti i pokušajte ponovo.", + "noGroupPlacheHolderOffline": "Trenutno ste offline. Provjerite jeste li povezani na WiFi ili mobilne podatke kako biste osvježili i prikupili svoje grupe." + }, + "appRestart": { + "message": "Program će se ponovo pokrenuti kako bi primijenio sljedeću konfiguraciju:", + "button": "U redu", + "textDirection": "Smjer teksta", + "selectedLanguage": "Odabrani jezik" + } +} diff --git a/languages/hr.test.js b/languages/hr.test.js new file mode 100644 index 00000000..2ab4a839 --- /dev/null +++ b/languages/hr.test.js @@ -0,0 +1,14 @@ +import i18n from '.'; +const locale = 'hr'; + +describe('languages/' + locale, () => { + beforeAll(() => i18n.setLocale(locale, false)); + + test('test missing translation', () => { + expect(i18n.t('global.zz')).toEqual('[missing "' + locale + '.global.zz" translation]'); + }); + + test('test global.success.save translation', () => { + expect(i18n.t('global.success.save')).toEqual('Podaci su uspješno spremljeni!'); + }); +}); diff --git a/languages/id.json b/languages/id.json index 77db7615..cc5e0fce 100644 --- a/languages/id.json +++ b/languages/id.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Tulis komentar atau komentar Anda di sini", "requiredField": "kolom yang harus diisi", "edit": "Edit", + "delete": "Menghapus", "cancel": "Membatalkan", "save": "Menyimpan", "online": "On line", @@ -25,13 +26,9 @@ "membersActivity": "Anggota", "moreFields": "Lebih banyak bidang", "nameRequired": "Nama (wajib", - "close": "Menutup" - }, - "appRestart": { - "message": "Aplikasi akan dihidupkan ulang untuk menerapkan konfigurasi berikut:", - "button": "baik", - "textDirection": "Arah teks", - "selectedLanguage": "Bahasa yang dipilih" + "close": "Menutup", + "editComment": "Edit", + "deleteComment": "Menghapus" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "ingat kata Sandi", "rememberPasswordActive": "Tetap masuk log diaktifkan", "rememberPasswordInactive": "Tetap masuk log dinonaktifkan", - "remove": "Hapus kode PIN", - "set": "Setel kode PIN", + "remove": "Menghapus", + "set": "Set", "pinCode": "Kode PIN", - "enterPin": "Masukkan kode PIN", - "setPin": "Tetapkan PIN baru", - "incorrectPin": "Kode PIN salah", - "savedPinCode": "Kode PIN berhasil disimpan!", - "removedPinCode": "Kode PIN berhasil dihapus!", + "enterPin": "Masukkan PIN", + "setPin": "Setel PIN baru", + "incorrectPin": "PIN salah", + "savedPinCode": "Kode PIN berhasil disimpan", + "removedPinCode": "Kode PIN berhasil dihapus", "close": "Menutup" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "Nama grup", "error": "Nama harus diisi" + }, + "fab": { + "quick_button_meeting_scheduled": "Rapat Terjadwal", + "quick_button_meeting_postponed": "Rapat Ditunda", + "quick_button_meeting_complete": "Rapat Selesai" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Tidak ada grup yang tersedia saat ini.", "noGroupPlacheHolder1": "Mungkin ada masalah saat mendapatkannya dari situs Anda. Seret ke bawah untuk mencoba menyegarkan dan coba lagi.", "noGroupPlacheHolderOffline": "Anda sedang offline. Pastikan Anda terhubung ke wifi atau data seluler untuk menyegarkan dan mendapatkan grup Anda." + }, + "appRestart": { + "message": "Aplikasi akan dimulai ulang untuk menerapkan konfigurasi berikut:", + "button": "Baik", + "textDirection": "Arah teks", + "selectedLanguage": "Bahasa yang dipilih" } } diff --git a/languages/index.js b/languages/index.js index 7f321548..1c733a58 100644 --- a/languages/index.js +++ b/languages/index.js @@ -12,6 +12,9 @@ import * as id from './id.json'; import * as nl from './nl.json'; import * as ptBR from './pt.json'; import * as ru from './ru.json'; +import * as sr from './sr.json'; +import * as sl from './sl.json'; +import * as hr from './hr.json'; import * as sw from './sw.json'; import * as tr from './tr.json'; import * as zhCn from './zhCn.json'; @@ -32,6 +35,9 @@ i18n.translations = { 'nl-NL': nl, 'pt-BR': ptBR, 'ru-RU': ru, + sr, + sl, + hr, sw, 'tr-TR': tr, 'zh-CN': zhCn, diff --git a/languages/locales.js b/languages/locales.js index 64b60282..19e57c52 100644 --- a/languages/locales.js +++ b/languages/locales.js @@ -41,12 +41,27 @@ export default [ }, { code: 'pt-BR', - name: 'Português', + name: 'Português do Brasil', rtl: false, }, { code: 'ru-RU', - name: 'русский язык', + name: 'русский', + rtl: false, + }, + { + code: 'sr', + name: 'српски', + rtl: false, + }, + { + code: 'sl', + name: 'slovenščina', + rtl: false, + }, + { + code: 'hr', + name: 'hrvatski', rtl: false, }, { diff --git a/languages/moment.js b/languages/moment.js index 055a0020..c5403013 100644 --- a/languages/moment.js +++ b/languages/moment.js @@ -10,6 +10,9 @@ require('moment/locale/id'); require('moment/locale/nl'); require('moment/locale/pt'); require('moment/locale/ru'); +require('moment/locale/sr'); +require('moment/locale/sl'); +require('moment/locale/hr'); require('moment/locale/sw'); require('moment/locale/tr'); require('moment/locale/zh-cn'); diff --git a/languages/nl.json b/languages/nl.json index 79dc6811..e00169cd 100644 --- a/languages/nl.json +++ b/languages/nl.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Schrijf hier je reactie of opmerking", "requiredField": "verplicht veld", "edit": "Bewerk", + "delete": "Verwijderen", "cancel": "annuleren", "save": "Opslaan", "online": "Online", @@ -25,13 +26,9 @@ "membersActivity": "leden", "moreFields": "Meer velden", "nameRequired": "Naam (vereist", - "close": "Dichtbij" - }, - "appRestart": { - "message": "De applicatie wordt opnieuw opgestart om de volgende configuratie toe te passen:", - "button": "OK", - "textDirection": "Tekstrichting", - "selectedLanguage": "Geselecteerde taal" + "close": "Dichtbij", + "editComment": "Bewerk", + "deleteComment": "Verwijderen" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "Onthoud wachtwoord", "rememberPasswordActive": "Blijf ingelogd ingeschakeld", "rememberPasswordInactive": "Blijf ingelogd uitgeschakeld", - "remove": "PIN-code verwijderen", - "set": "Pincode instellen", + "remove": "Verwijderen", + "set": "Set", "pinCode": "Pincode", - "enterPin": "Voer pincode in", - "setPin": "Nieuwe pincode instellen", + "enterPin": "Voer Pin-code in", + "setPin": "Stel een nieuwe pincode in", "incorrectPin": "Onjuiste pincode", - "savedPinCode": "PIN-code succesvol opgeslagen!", - "removedPinCode": "PIN-code succesvol verwijderd!", - "close": "dichtbij" + "savedPinCode": "PIN-code succesvol opgeslagen", + "removedPinCode": "PIN-code succesvol verwijderd", + "close": "Dichtbij" }, "contactDetailScreen": { "addNewContact": "Voeg nieuw contact toe", @@ -121,6 +118,11 @@ "groupName": { "label": "Groepsnaam", "error": "Naam is vereist" + }, + "fab": { + "quick_button_meeting_scheduled": "Vergadering gepland", + "quick_button_meeting_postponed": "Vergadering uitgesteld", + "quick_button_meeting_complete": "Vergadering voltooid" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Er zijn momenteel geen groepen beschikbaar.", "noGroupPlacheHolder1": "Er is mogelijk een probleem opgetreden bij het ophalen van uw site. Sleep omlaag om te proberen te vernieuwen en probeer het opnieuw.", "noGroupPlacheHolderOffline": "U bent momenteel offline. Zorg ervoor dat u bent verbonden met wifi of mobiele gegevens om uw groepen te vernieuwen en op te halen." + }, + "appRestart": { + "message": "De applicatie wordt opnieuw gestart om de volgende configuratie toe te passen:", + "button": "OK", + "textDirection": "Tekstrichting", + "selectedLanguage": "Geselecteerde taal" } } diff --git a/languages/pt.json b/languages/pt.json index 6dc1d167..5f3c09e4 100644 --- a/languages/pt.json +++ b/languages/pt.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Escreva seu comentário ou anotação aqui", "requiredField": "Campo obrigatório", "edit": "editar", + "delete": "Excluir", "cancel": "Cancelar", "save": "Salvar", "online": "online", @@ -25,13 +26,9 @@ "membersActivity": "Membros", "moreFields": "Mais campos", "nameRequired": "Nome necessário", - "close": "Fechar" - }, - "appRestart": { - "message": "O aplicativo será reiniciado para aplicar a seguinte configuração:", - "button": "Está bem", - "textDirection": "Direção do texto", - "selectedLanguage": "Idioma selecionado" + "close": "Fechar", + "editComment": "Editar", + "deleteComment": "Excluir" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "Lembrar senha", "rememberPasswordActive": "Manter-se logado está ligado", "rememberPasswordInactive": "Manter-se logado está desligado", - "remove": "Remover código PIN", - "set": "Definir código PIN", + "remove": "Remover", + "set": "Conjunto", "pinCode": "Código PIN", - "enterPin": "Digite o código PIN", + "enterPin": "Entrar no pino", "setPin": "Definir novo PIN", - "incorrectPin": "Código PIN incorreto", - "savedPinCode": "Código PIN salvo com sucesso!", - "removedPinCode": "Código PIN removido com sucesso!", - "close": "Fechar" + "incorrectPin": "PIN incorreto", + "savedPinCode": "Código PIN salvo com sucesso", + "removedPinCode": "Código PIN removido com sucesso", + "close": "Perto" }, "contactDetailScreen": { "addNewContact": "Adicionar novo contato", @@ -121,6 +118,11 @@ "groupName": { "label": "Nome do Grupo", "error": "Nome é necessário" + }, + "fab": { + "quick_button_meeting_scheduled": "Reunião agendada", + "quick_button_meeting_postponed": "Reunião adiada", + "quick_button_meeting_complete": "Reunião Completa" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Não há grupos disponíveis no momento", "noGroupPlacheHolder1": "Pode haver um problema ao removê-los do seu local. Arraste-os para tentar atualizar e tente novamente. ", "noGroupPlacheHolderOffline": "Você está offline. Certifique-se de que está conectado à rede Wifi ou dados móveis para atualizar e obter seus grupos." + }, + "appRestart": { + "message": "O aplicativo será reiniciado para aplicar a seguinte configuração:", + "button": "Está bem", + "textDirection": "Direção do texto", + "selectedLanguage": "Idioma selecionado" } } diff --git a/languages/ru.json b/languages/ru.json index 7e4d2a7e..db6cbcec 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Напишите свой комментарий или заметку здесь", "requiredField": "Обязательное поле", "edit": "Редактировать", + "delete": "Удалить", "cancel": "Отмена", "save": "Сохранить", "online": "Онлайн", @@ -25,13 +26,9 @@ "membersActivity": "Члены", "moreFields": "Больше полей", "nameRequired": "Имя обязательно", - "close": "близко" - }, - "appRestart": { - "message": "Приложение будет перезапущено для применения следующей конфигурации:", - "button": "Хорошо", - "textDirection": "Направление текста", - "selectedLanguage": "Выбранный язык" + "close": "близко", + "editComment": "редактировать", + "deleteComment": "Удалить" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "Запомнить пароль", "rememberPasswordActive": "Режим \"Оставаться в системе\" включен", "rememberPasswordInactive": "Режим \"Оставаться в системе\" выключен", - "remove": "Удалить ПИН код", - "set": "Установить PIN-код", + "remove": "удалять", + "set": "Устанавливать", "pinCode": "Пин-код", - "enterPin": "Введите пин код", + "enterPin": "Введите PIN-код", "setPin": "Установить новый PIN-код", "incorrectPin": "Неверный PIN-код", - "savedPinCode": "ПИН-код успешно сохранен!", - "removedPinCode": "PIN-код успешно удален!", + "savedPinCode": "PIN-код успешно сохранен", + "removedPinCode": "PIN-код успешно удален", "close": "близко" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "Имя группы", "error": "Имя обязательно" + }, + "fab": { + "quick_button_meeting_scheduled": "Встреча запланирована", + "quick_button_meeting_postponed": "Встреча отложена", + "quick_button_meeting_complete": "Встреча завершена" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "В настоящее время нет доступных групп.", "noGroupPlacheHolder1": "Возможно, возникла проблема при получении их с вашего сайта. Попытайтесь еще раз (обновите экран, потянув его вниз).", "noGroupPlacheHolderOffline": "Вы в настоящее время не в сети. Убедитесь, что вы подключены к Wi-Fi или мобильным данным, чтобы обновить и получить ваши группы." + }, + "appRestart": { + "message": "Приложение будет перезапущено для применения следующей конфигурации:", + "button": "Хорошо", + "textDirection": "Направление текста", + "selectedLanguage": "Выбранный язык" } } diff --git a/languages/sl.json b/languages/sl.json new file mode 100644 index 00000000..4fc1c6a3 --- /dev/null +++ b/languages/sl.json @@ -0,0 +1,149 @@ +{ + "global": { + "error": { + "code": "Koda: ", + "message": "Sporočilo: " + }, + "success": { + "save": "Podatki so bili uspešno shranjeni!" + }, + "groups": "Skupine", + "details": "Podrobnosti", + "selectPeopleGroups": "Izberite Skupine ljudi", + "address": "Naslov", + "progress": "Napredek", + "commentsActivity": "Pripombe in dejavnost", + "writeYourCommentNoteHere": "Tu napišite svoj komentar ali opombo", + "requiredField": "Zahtevano polje", + "edit": "Uredi", + "delete": "Izbriši", + "cancel": "Prekliči", + "save": "Shrani", + "online": "Na spletu", + "offline": "brez povezave", + "language": "Jezik", + "search": "Iskanje", + "membersActivity": "Člani", + "moreFields": "Več polj", + "nameRequired": "Ime je potrebno", + "close": "Zapri", + "editComment": "Uredi", + "deleteComment": "Izbriši" + }, + "loginScreen": { + "domain": { + "label": "URL", + "placeholder": "Npr. myteam.mydomain.com", + "error": "Domena je obvezna", + "errorForgotPass": "Vnesite URL za začetek pridobivanja gesla" + }, + "username": { + "label": "Uporabniško ime", + "error": "Uporabniško ime je potrebno" + }, + "password": { + "label": "Geslo", + "error": "zahtevano je geslo" + }, + "logIn": "Vpiši se", + "forgotPassword": "Pozabili geslo?", + "errors": { + "invalidUsername": "Neznano uporabniško ime. Ponovno preverite ali poskusite svoj e-poštni naslov.", + "incorrectPassword": "Geslo, ki ste ga vnesli za uporabniško ime, ni pravilno" + } + }, + "contactsScreen": { + "contacts": "Stiki", + "noContactPlacheHolder": "Trenutno ni na voljo nobenega stika.", + "noContactPlacheHolder1": "Morda je prišlo do težave pri pridobivanju le-teh z vašega spletnega mesta. Povlecite navzdol, da se poskusite osvežiti in poskusite znova.", + "noContactPlacheHolderOffline": "Trenutno ste brez povezave. Zagotovite, da ste povezani z wifi ali mobilnimi podatki, da osvežite in dobite stike." + }, + "settingsScreen": { + "settings": "Nastavitve", + "networkUnavailable": "Omrežje ni na voljo. Zdaj v načinu OFFLINE", + "networkAvailable": "Zaznano je omrežje. Nazaj v ONLINE način", + "storybook": "Zbirka zgodb", + "logout": "Odjava", + "helpSupport": "Pomoč / podpora", + "rememberPassword": "Zapomni si geslo", + "rememberPasswordActive": "Naj bo prijava omogočena", + "rememberPasswordInactive": "Prijavi se onemogočeno", + "remove": "Odstrani", + "set": "Nastavite", + "pinCode": "PIN koda", + "enterPin": "Vnesite PIN", + "setPin": "Nastavite novo kodo PIN", + "incorrectPin": "Napačna koda PIN", + "savedPinCode": "Koda PIN je bila uspešno shranjena", + "removedPinCode": "Koda PIN je bila uspešno odstranjena", + "close": "Zapri" + }, + "contactDetailScreen": { + "addNewContact": "Dodaj nov stik", + "subAssignThisContact": "Pododelite ta stik", + "mobile": "Mobilni", + "selectLocations": "Izberite Lokacije", + "selectSources": "Izberite vire", + "connections": "Povezave", + "addGroup": "Dodaj skupino", + "addConnection": "Dodaj povezavo", + "addBaptizedBy": "Dodaj krščen z", + "addBaptized": "Dodajte krst", + "addCoachedBy": "Dodaj trener", + "addCoaching": "Dodaj trenerje", + "phoneNumber": "Telefonska številka", + "initialComment": "Začetni komentar", + "socialMedia": "Socialni mediji", + "noContactCommentPlacheHolder": "Trenutno ni na voljo nobenih komentarjev za stike.", + "fullName": { + "label": "Polno ime", + "error": "Ime je obvezno" + }, + "noContactCommentPlacheHolder1": "Morda je prišlo do težave pri pridobivanju le-teh z vašega spletnega mesta. Povlecite navzdol, da se poskusite osvežiti in poskusite znova.", + "noContactCommentPlacheHolderOffline": "Trenutno ste brez povezave. Zagotovite, da ste povezani z wifi ali mobilnimi podatki, da osvežite in dobite komentar." + }, + "groupDetailScreen": { + "addNewGroup": "Dodaj novo skupino", + "selectCoaches": "Izberite Coach Coach", + "searchGroups": "Iskanje skupin", + "searchPeerGroups": "Iščite skupine vrstnikov", + "childGroup": "Otroška skupina", + "searchChildGroups": "Poiščite otroške skupine", + "addMember": "Dodaj člana", + "noGroupCommentPlacheHolder": "Trenutno ni na voljo nobenih komentarjev skupin.", + "noGroupCommentPlacheHolder1": "Morda je prišlo do težave pri pridobivanju le-teh z vašega spletnega mesta. Povlecite navzdol, da se poskusite osvežiti in poskusite znova.", + "noGroupCommentPlacheHolderOffline": "Trenutno ste brez povezave. Zagotovite, da ste povezani z wifi ali mobilnimi podatki, da osvežite in dobite komentar svoje skupine.", + "selectLocations": "Izberite Lokacije", + "noMembersMessage": "Ni članov", + "groupName": { + "label": "Ime skupine", + "error": "Ime je obvezno" + }, + "fab": { + "quick_button_meeting_scheduled": "Načrtovan sestanek", + "quick_button_meeting_postponed": "Seja prestavljena", + "quick_button_meeting_complete": "Sestanek končan" + } + }, + "notificationsScreen": { + "notifications": "Obvestila", + "loadMore": "Naloži več", + "new": "Novo", + "unRead": "Neprebrano", + "all": "Vse", + "markAll": "Označi vse", + "dontHaveNotificationsUnread": "Brez prebranih obvestil", + "dontHaveNotifications": "Brez obvestil" + }, + "groupsScreen": { + "noGroupPlacheHolder": "Trenutno ni na voljo nobene skupine.", + "noGroupPlacheHolder1": "Morda je prišlo do težave pri pridobivanju le-teh z vašega spletnega mesta. Povlecite navzdol, da se poskusite osvežiti in poskusite znova.", + "noGroupPlacheHolderOffline": "Trenutno ste brez povezave. Zagotovite, da ste povezani z wifi ali mobilnimi podatki, da osvežite in dobite svoje skupine." + }, + "appRestart": { + "message": "Aplikacija se bo znova zagnala, da bo uporabila naslednjo konfiguracijo:", + "button": "V redu", + "textDirection": "Smer besedila", + "selectedLanguage": "Izbrani jezik" + } +} diff --git a/languages/sl.test.js b/languages/sl.test.js new file mode 100644 index 00000000..6bd4ee7a --- /dev/null +++ b/languages/sl.test.js @@ -0,0 +1,14 @@ +import i18n from '.'; +const locale = 'sl'; + +describe('languages/' + locale, () => { + beforeAll(() => i18n.setLocale(locale, false)); + + test('test missing translation', () => { + expect(i18n.t('global.zz')).toEqual('[missing "' + locale + '.global.zz" translation]'); + }); + + test('test global.success.save translation', () => { + expect(i18n.t('global.success.save')).toEqual('Podatki so bili uspešno shranjeni!'); + }); +}); diff --git a/languages/sr.json b/languages/sr.json new file mode 100644 index 00000000..5c8ba7b4 --- /dev/null +++ b/languages/sr.json @@ -0,0 +1,149 @@ +{ + "global": { + "error": { + "code": "Шифра: ", + "message": "Порука: " + }, + "success": { + "save": "Подаци су успешно сачувани!" + }, + "groups": "Групе", + "details": "Детаљи", + "selectPeopleGroups": "Изаберите Групе људи", + "address": "Адреса", + "progress": "Напредак", + "commentsActivity": "Коментари и активност", + "writeYourCommentNoteHere": "Напишите свој коментар или белешку овде", + "requiredField": "Обавезно поље", + "edit": "Уредити", + "delete": "Избриши", + "cancel": "Поништити, отказати", + "save": "сачувати", + "online": "Онлине", + "offline": "одсутан", + "language": "Језик", + "search": "Претрага", + "membersActivity": "Чланови", + "moreFields": "Још поља", + "nameRequired": "Име (обавезно", + "close": "Близу", + "editComment": "Уредити", + "deleteComment": "Избриши" + }, + "loginScreen": { + "domain": { + "label": "УРЛ", + "placeholder": "На пример. митеам.мидомаин.цом", + "error": "Домен је обавезан", + "errorForgotPass": "Унесите УРЛ да бисте започели преузимање лозинке" + }, + "username": { + "label": "Корисничко име", + "error": "Корисничко име је обавезно" + }, + "password": { + "label": "Лозинка", + "error": "Потребна је шифра" + }, + "logIn": "Пријавите се", + "forgotPassword": "Заборавили сте лозинку?", + "errors": { + "invalidUsername": "Непознато корисничко име. Проверите поново или испробајте своју адресу е-поште.", + "incorrectPassword": "Лозинка коју сте унели за корисничко име је нетачна" + } + }, + "contactsScreen": { + "contacts": "Контакти", + "noContactPlacheHolder": "Тренутно нема доступних контаката.", + "noContactPlacheHolder1": "Можда је дошло до проблема при преузимању са ваше веб локације. Превуците надоле да бисте покушали да освежите и покушајте поново.", + "noContactPlacheHolderOffline": "Тренутно сте ван мреже. Уверите се да сте повезани на ВиФи или мобилне податке како бисте освежили и добили своје контакте." + }, + "settingsScreen": { + "settings": "Подешавања", + "networkUnavailable": "Мрежа није доступна. Сада у ОФФЛИНЕ режиму", + "networkAvailable": "Мрежа је откривена. Повратак у ОНЛИНЕ режим", + "storybook": "Књига прича", + "logout": "Одјава", + "helpSupport": "Помоћ / подршка", + "rememberPassword": "Запамти лозинку", + "rememberPasswordActive": "Останите пријављени", + "rememberPasswordInactive": "Останите пријављени онемогућени", + "remove": "Уклоните", + "set": "Комплет", + "pinCode": "Пин код", + "enterPin": "Унесите ПИН", + "setPin": "Подесите нови ПИН", + "incorrectPin": "Нетачан ПИН", + "savedPinCode": "ПИН код је успешно сачуван", + "removedPinCode": "ПИН код је успешно уклоњен", + "close": "Близу" + }, + "contactDetailScreen": { + "addNewContact": "Додај нови контакт", + "subAssignThisContact": "Пододелите овај контакт", + "mobile": "Телефон", + "selectLocations": "Изаберите Лоцатионс", + "selectSources": "Изаберите изворе", + "connections": "Везе", + "addGroup": "Додај групу", + "addConnection": "Додај везу", + "addBaptizedBy": "Додај крштених од", + "addBaptized": "Додати крштено", + "addCoachedBy": "Додај тренирао", + "addCoaching": "Додајте тренирање", + "phoneNumber": "Број телефона", + "initialComment": "Почетни коментар", + "socialMedia": "Друштвени медији", + "noContactCommentPlacheHolder": "Тренутно нема доступних коментара на контакт.", + "fullName": { + "label": "Пуно име", + "error": "Име је обавезно" + }, + "noContactCommentPlacheHolder1": "Можда је дошло до проблема при преузимању са ваше веб локације. Превуците надоле да бисте покушали да освежите и покушајте поново.", + "noContactCommentPlacheHolderOffline": "Тренутно сте ван мреже. Уверите се да сте повезани на ВиФи или мобилне податке како бисте освежили и добили коментар на контакт." + }, + "groupDetailScreen": { + "addNewGroup": "Додај нову групу", + "selectCoaches": "Изаберите Групни тренер", + "searchGroups": "Претражите групе", + "searchPeerGroups": "Претражите групе вршњака", + "childGroup": "Дечија група", + "searchChildGroups": "Претражите дечје групе", + "addMember": "Додај члана", + "noGroupCommentPlacheHolder": "Тренутно нема доступних коментара групе.", + "noGroupCommentPlacheHolder1": "Можда је дошло до проблема при преузимању са ваше веб локације. Превуците надоле да бисте покушали да освежите и покушајте поново.", + "noGroupCommentPlacheHolderOffline": "Тренутно сте ван мреже. Уверите се да сте повезани на ВиФи или мобилне податке како бисте освежили и добили коментар групе.", + "selectLocations": "Изаберите Лоцатионс", + "noMembersMessage": "Нема чланова. Додирните овде или на Уреди да бисте додали неке чланове у ову групу.", + "groupName": { + "label": "Назив групе", + "error": "Име је обавезно" + }, + "fab": { + "quick_button_meeting_scheduled": "Састанак заказан", + "quick_button_meeting_postponed": "Састанак одложен", + "quick_button_meeting_complete": "Састанак завршен" + } + }, + "notificationsScreen": { + "notifications": "Обавештења", + "loadMore": "Учитај више", + "new": "Нова", + "unRead": "Непрочитано", + "all": "Све", + "markAll": "Означи све", + "dontHaveNotificationsUnread": "Нема непрочитаних обавештења", + "dontHaveNotifications": "Нема обавештења" + }, + "groupsScreen": { + "noGroupPlacheHolder": "Тренутно нема доступних група.", + "noGroupPlacheHolder1": "Можда је дошло до проблема при преузимању са ваше веб локације. Превуците надоле да бисте покушали да освежите и покушајте поново.", + "noGroupPlacheHolderOffline": "Тренутно сте ван мреже. Уверите се да сте повезани на ВиФи или мобилне податке како бисте освежили и прибавили своје групе." + }, + "appRestart": { + "message": "Апликација ће се поново покренути да би применила следећу конфигурацију:", + "button": "Ок", + "textDirection": "Смер текста", + "selectedLanguage": "Одабрани језик" + } +} diff --git a/languages/sr.test.js b/languages/sr.test.js new file mode 100644 index 00000000..a68a5af7 --- /dev/null +++ b/languages/sr.test.js @@ -0,0 +1,14 @@ +import i18n from '.'; +const locale = 'sr'; + +describe('languages/' + locale, () => { + beforeAll(() => i18n.setLocale(locale, false)); + + test('test missing translation', () => { + expect(i18n.t('global.zz')).toEqual('[missing "' + locale + '.global.zz" translation]'); + }); + + test('test global.success.save translation', () => { + expect(i18n.t('global.success.save')).toEqual('Подаци су успешно сачувани!'); + }); +}); diff --git a/languages/sw.json b/languages/sw.json index eaf844b1..937c4d9e 100644 --- a/languages/sw.json +++ b/languages/sw.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Andika maoni yako au daftari hapa", "requiredField": "Sehemu inayohitajika", "edit": "Hariri", + "delete": "Futa", "cancel": "Ghairi", "save": "Okoa", "online": "Mtandaoni", @@ -25,13 +26,9 @@ "membersActivity": "Wajumbe", "moreFields": "Mashamba Zaidi", "nameRequired": "Jina linalohitajika", - "close": "Karibu" - }, - "appRestart": { - "message": "Maombi yataanza tena kutumia usanidi ufuatao:", - "button": "sawa", - "textDirection": "Miongozo ya maandishi", - "selectedLanguage": "Lugha iliyochaguliwa" + "close": "Karibu", + "editComment": "Hariri", + "deleteComment": "Futa" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "Kumbuka Nenosiri", "rememberPasswordActive": "Endelea kuwezeshwa", "rememberPasswordInactive": "Endelea kuwa umelemazwa", - "remove": "Ondoa msimbo wa Pini", - "set": "Weka nambari ya PIN", + "remove": "Ondoa", + "set": "Weka", "pinCode": "Nambari ya siri", - "enterPin": "Ingiza msimbo wa pini", + "enterPin": "Ingiza PIN", "setPin": "Weka PIN mpya", - "incorrectPin": "Nambari ya Pini isiyo sahihi", - "savedPinCode": "Nambari ya pini iliyohifadhiwa kikamilifu!", - "removedPinCode": "Nambari ya pini imeondolewa vyema!", - "close": "karibu" + "incorrectPin": "PIN isiyo sahihi", + "savedPinCode": "Nambari ya siri imehifadhiwa kwa mafanikio", + "removedPinCode": "Nambari ya siri imeondolewa kwa mafanikio", + "close": "Funga" }, "contactDetailScreen": { "addNewContact": "Ongeza Mawasiliano Mpya", @@ -121,6 +118,11 @@ "groupName": { "label": "Jina la Kikundi", "error": "Jina inahitajika" + }, + "fab": { + "quick_button_meeting_scheduled": "Mkutano Umepangwa", + "quick_button_meeting_postponed": "Mkutano Ulihairishwa", + "quick_button_meeting_complete": "Mkutano Umekamilika" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Hakuna vikundi yoyote vinavyopatikana kwa sasa.", "noGroupPlacheHolder1": "Kunaweza kuwa na shida kuzipata kutoka kwa wavuti yako. Buruta chini kujaribu kujaribu kuburudisha na kujaribu tena.", "noGroupPlacheHolderOffline": "Hivi sasa uko nje ya mkondo. Hakikisha umeunganishwa na wifi au data ya rununu ili kuburudisha na upate vikundi vyako." + }, + "appRestart": { + "message": "Programu itaanza tena kutumia usanidi ufuatao:", + "button": "Sawa", + "textDirection": "Mwelekeo wa maandishi", + "selectedLanguage": "Lugha iliyochaguliwa" } } diff --git a/languages/tr.json b/languages/tr.json index 913484a3..d0b9dc25 100644 --- a/languages/tr.json +++ b/languages/tr.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "Yorumunu veya notunu buraya yaz", "requiredField": "Gerekli alan", "edit": "Düzenle", + "delete": "Sil", "cancel": "İptal et", "save": "kaydet", "online": "Çevrimiçi", @@ -25,13 +26,9 @@ "membersActivity": "Üyeler", "moreFields": "Diğer Alanlar", "nameRequired": "İsim (gerekli", - "close": "Kapat" - }, - "appRestart": { - "message": "Aşağıdaki yapılandırmayı uygulamak için uygulama yeniden başlatılır:", - "button": "tamam", - "textDirection": "Metin yönü", - "selectedLanguage": "Seçilen dil" + "close": "Kapat", + "editComment": "Düzenle", + "deleteComment": "Sil" }, "loginScreen": { "domain": { @@ -71,15 +68,15 @@ "rememberPassword": "şifre hatırlamak", "rememberPasswordActive": "Oturum açmayı etkin tut", "rememberPasswordInactive": "Giriş yapmayı devre dışı bırak", - "remove": "PIN kodunu kaldır", - "set": "PIN kodunu ayarla", + "remove": "Kaldırmak", + "set": "Ayarlamak", "pinCode": "PIN kodu", - "enterPin": "PIN kodunu girin", - "setPin": "Yeni PIN ayarla", - "incorrectPin": "Yanlış PIN kodu", - "savedPinCode": "PIN kodu başarıyla kaydedildi!", - "removedPinCode": "PIN kodu başarıyla kaldırıldı!", - "close": "kapat" + "enterPin": "PIN girin", + "setPin": "Yeni PIN belirleyin", + "incorrectPin": "Yanlış PIN", + "savedPinCode": "PIN kodu başarıyla kaydedildi", + "removedPinCode": "PIN kodu başarıyla kaldırıldı", + "close": "Kapat" }, "contactDetailScreen": { "addNewContact": "Yeni bağlantı ekle", @@ -121,6 +118,11 @@ "groupName": { "label": "Grup ismi", "error": "İsim gerekli" + }, + "fab": { + "quick_button_meeting_scheduled": "Toplantı Planlandı", + "quick_button_meeting_postponed": "Toplantı Ertelendi", + "quick_button_meeting_complete": "Toplantı Tamamlandı" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "Şu anda kullanılabilir grup yok.", "noGroupPlacheHolder1": "Bunları sitenizden alırken bir sorun olabilir. Yenilemeyi denemek için aşağı sürükleyin ve tekrar deneyin.", "noGroupPlacheHolderOffline": "Şu anda çevrimdışısınız. Gruplarınızı yenilemek ve almak için kablosuz bağlantıya veya mobil verilere bağlı olduğunuzdan emin olun." + }, + "appRestart": { + "message": "Aşağıdaki yapılandırmayı uygulamak için uygulama yeniden başlatılacak:", + "button": "Tamam", + "textDirection": "Metin yönü", + "selectedLanguage": "Seçilen dil" } } diff --git a/languages/zhCn.json b/languages/zhCn.json index 452180dc..00c2523a 100644 --- a/languages/zhCn.json +++ b/languages/zhCn.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "在这里写下您的评论或注释", "requiredField": "必填项目", "edit": "编辑", + "delete": "删除", "cancel": "取消", "save": "保存", "online": "线上", @@ -25,13 +26,9 @@ "membersActivity": "会员", "moreFields": "更多领域", "nameRequired": "姓名必填", - "close": "关" - }, - "appRestart": { - "message": "该应用程序将重新启动以应用以下配置:", - "button": "好", - "textDirection": "文字方向", - "selectedLanguage": "所选语言" + "close": "关", + "editComment": "编辑", + "deleteComment": "删除" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "记住密码", "rememberPasswordActive": "保持登录状态", "rememberPasswordInactive": "保持登录状态已禁用", - "remove": "删除PIN码", - "set": "设置PIN码", + "remove": "去掉", + "set": "组", "pinCode": "PIN码", - "enterPin": "输入PIN码", + "enterPin": "输入密码", "setPin": "设置新密码", - "incorrectPin": "PIN码不正确", - "savedPinCode": "PIN码已成功保存!", - "removedPinCode": "PIN码已成功删除!", + "incorrectPin": "PIN码错误", + "savedPinCode": "PIN码已成功保存", + "removedPinCode": "PIN码已成功删除", "close": "关" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "组的名字", "error": "名称为必填项" + }, + "fab": { + "quick_button_meeting_scheduled": "会议预定", + "quick_button_meeting_postponed": "会议推迟", + "quick_button_meeting_complete": "会议完成" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "当前没有任何组。", "noGroupPlacheHolder1": "从您的网站获取它们可能存在问题。向下拖动以尝试刷新,然后重试。", "noGroupPlacheHolderOffline": "您目前离线。确保您已连接到wifi或移动数据,以便刷新并分组。" + }, + "appRestart": { + "message": "该应用程序将重新启动以应用以下配置:", + "button": "好", + "textDirection": "文字方向", + "selectedLanguage": "所选语言" } } diff --git a/languages/zhTw.json b/languages/zhTw.json index f79a718e..ee27119b 100644 --- a/languages/zhTw.json +++ b/languages/zhTw.json @@ -16,6 +16,7 @@ "writeYourCommentNoteHere": "在這裡寫下您的評論或註釋", "requiredField": "必填項目", "edit": "編輯", + "delete": "刪除", "cancel": "取消", "save": "保存", "online": "線上", @@ -25,13 +26,9 @@ "membersActivity": "會員", "moreFields": "更多領域", "nameRequired": "姓名必填", - "close": "關" - }, - "appRestart": { - "message": "該應用程序將重新啟動以應用以下配置:", - "button": "好", - "textDirection": "文字方向", - "selectedLanguage": "所選語言" + "close": "關", + "editComment": "編輯", + "deleteComment": "刪除" }, "loginScreen": { "domain": { @@ -71,14 +68,14 @@ "rememberPassword": "記住密碼", "rememberPasswordActive": "保持登錄狀態", "rememberPasswordInactive": "保持登錄狀態已禁用", - "remove": "刪除PIN碼", - "set": "設置PIN碼", + "remove": "去掉", + "set": "組", "pinCode": "PIN碼", - "enterPin": "輸入PIN碼", + "enterPin": "輸入密碼", "setPin": "設置新密碼", - "incorrectPin": "PIN碼不正確", - "savedPinCode": "PIN碼已成功保存!", - "removedPinCode": "PIN碼已成功刪除!", + "incorrectPin": "PIN碼錯誤", + "savedPinCode": "PIN碼已成功保存", + "removedPinCode": "PIN碼已成功刪除", "close": "關" }, "contactDetailScreen": { @@ -121,6 +118,11 @@ "groupName": { "label": "組的名字", "error": "名稱為必填項" + }, + "fab": { + "quick_button_meeting_scheduled": "會議預定", + "quick_button_meeting_postponed": "會議推遲", + "quick_button_meeting_complete": "會議完成" } }, "notificationsScreen": { @@ -137,5 +139,11 @@ "noGroupPlacheHolder": "當前沒有任何組。", "noGroupPlacheHolder1": "從您的網站獲取它們可能存在問題。向下拖動以嘗試刷新,然後重試。", "noGroupPlacheHolderOffline": "您目前離線。確保您已連接到wifi或移動數據,以便刷新並分組。" + }, + "appRestart": { + "message": "該應用程序將重新啟動以應用以下配置:", + "button": "好", + "textDirection": "文字方向", + "selectedLanguage": "所選語言" } } diff --git a/package-lock.json b/package-lock.json index b289181e..47d6ce5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1356,12 +1356,12 @@ } }, "@expo/configure-splash-screen": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@expo/configure-splash-screen/-/configure-splash-screen-0.1.12.tgz", - "integrity": "sha512-XvXZEmRaHmuO4/oJnE4cD2VFwYQec7kXBq0Ki9NtSdNHmGWE8XuXslZpn9anORcTjas/X0p9KtJbRsYQVVqbww==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@expo/configure-splash-screen/-/configure-splash-screen-0.1.15.tgz", + "integrity": "sha512-jKall/3aq3g/dQd0gPijsCxHyPXWh8rq/F2h0IYf1mcixlrMGTTFl/0um48SMJzyEg4HYMe4w8thn79vseI+AA==", "requires": { - "@react-native-community/cli-platform-android": "4.7.0", - "@react-native-community/cli-platform-ios": "4.7.0", + "@react-native-community/cli-platform-android": "^4.10.0", + "@react-native-community/cli-platform-ios": "^4.10.0", "color-string": "^1.5.3", "commander": "^5.1.0", "core-js": "^3.6.5", @@ -1372,6 +1372,119 @@ "xml-js": "^1.6.11" }, "dependencies": { + "@react-native-community/cli-platform-android": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-4.13.0.tgz", + "integrity": "sha512-3i8sX8GklEytUZwPnojuoFbCjIRzMugCdzDIdZ9UNmi/OhD4/8mLGO0dgXfT4sMWjZwu3qjy45sFfk2zOAgHbA==", + "requires": { + "@react-native-community/cli-tools": "^4.13.0", + "chalk": "^3.0.0", + "execa": "^1.0.0", + "fs-extra": "^8.1.0", + "glob": "^7.1.3", + "jetifier": "^1.6.2", + "lodash": "^4.17.15", + "logkitty": "^0.7.1", + "slash": "^3.0.0", + "xmldoc": "^1.1.2" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "@react-native-community/cli-platform-ios": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-4.13.0.tgz", + "integrity": "sha512-6THlTu8zp62efkzimfGr3VIuQJ2514o+vScZERJCV1xgEi8XtV7mb/ZKt9o6Y9WGxKKkc0E0b/aVAtgy+L27CA==", + "requires": { + "@react-native-community/cli-tools": "^4.13.0", + "chalk": "^3.0.0", + "glob": "^7.1.3", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", + "plist": "^3.0.1", + "xcode": "^2.0.0" + }, + "dependencies": { + "xcode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/xcode/-/xcode-2.1.0.tgz", + "integrity": "sha512-uCrmPITrqTEzhn0TtT57fJaNaw8YJs1aCzs+P/QqxsDbvPZSv7XMPPwXrKvHtD6pLjBM/NaVwraWJm8q83Y4iQ==", + "requires": { + "simple-plist": "^1.0.0", + "uuid": "^3.3.2" + } + } + } + }, + "@react-native-community/cli-tools": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-4.13.0.tgz", + "integrity": "sha512-s4f489h5+EJksn4CfheLgv5PGOM0CDmK1UEBLw2t/ncWs3cW2VI7vXzndcd/WJHTv3GntJhXDcJMuL+Z2IAOgg==", + "requires": { + "chalk": "^3.0.0", + "lodash": "^4.17.15", + "mime": "^2.4.1", + "node-fetch": "^2.6.0", + "open": "^6.2.0", + "shell-quote": "1.6.1" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "deep-equal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", @@ -1392,6 +1505,147 @@ "which-collection": "^1.0.1", "which-typed-array": "^1.1.2" } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "logkitty": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", + "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", + "requires": { + "ansi-fragments": "^0.2.1", + "dayjs": "^1.8.15", + "yargs": "^15.1.0" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -3072,7 +3326,6 @@ "glob": "^7.1.3", "jetifier": "^1.6.2", "lodash": "^4.17.15", - "logkitty": "^0.6.0", "slash": "^3.0.0", "xmldoc": "^1.1.2" }, @@ -3307,9 +3560,9 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "supports-color": { "version": "7.1.0", @@ -3452,9 +3705,9 @@ }, "dependencies": { "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" } } }, @@ -3540,8 +3793,7 @@ "opn": "^5.4.0", "r2": "^2.0.1", "read-env": "^1.3.0", - "xcode": "2.0.0", - "yargs": "^12.0.2" + "xcode": "2.0.0" }, "dependencies": { "chardet": { @@ -5324,31 +5576,6 @@ "tiny-emitter": "^2.0.0" } }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -5360,11 +5587,6 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -5657,18 +5879,18 @@ } }, "cross-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.5.tgz", - "integrity": "sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", "dev": true, "requires": { - "node-fetch": "2.6.0" + "node-fetch": "2.6.1" }, "dependencies": { "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true } } @@ -6076,14 +6298,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "requires": { - "iconv-lite": "^0.6.2" - } - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7847,11 +8061,6 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, "get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -8321,14 +8530,6 @@ "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.7.1.tgz", "integrity": "sha512-xrRzCeda5ZC0u0yRN+dMdidtda0N+f7t7Pek0ajWb+iyKqSGdrMmuBtbNpWJWY5N4Th0cxbp/BR57zSPdrM3Rw==" }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -8533,11 +8734,6 @@ "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -8880,7 +9076,6 @@ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { - "node-fetch": "^1.0.1", "whatwg-fetch": ">=0.10.0" } }, @@ -13122,14 +13317,6 @@ "language-subtag-registry": "~0.3.2" } }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -13792,16 +13979,6 @@ } } }, - "logkitty": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.6.1.tgz", - "integrity": "sha512-cHuXN8qUZuzX/7kB6VyS7kB4xyD24e8gyHXIFNhIv+fjW3P+jEXNUhj0o/7qWJtv7UZpbnPgUqzu/AZQ8RAqxQ==", - "requires": { - "ansi-fragments": "^0.2.1", - "dayjs": "^1.8.15", - "yargs": "^12.0.5" - } - }, "lolex": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", @@ -13861,14 +14038,6 @@ "tmpl": "1.0.x" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -13904,16 +14073,6 @@ "buffer-alloc": "^1.1.0" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "memoizerific": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", @@ -14100,9 +14259,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "p-limit": { "version": "2.3.0", @@ -14626,7 +14785,8 @@ "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true }, "min-document": { "version": "2.19.0", @@ -14863,15 +15023,6 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -15005,11 +15156,6 @@ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -15254,26 +15400,11 @@ "wcwidth": "^1.0.1" } }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" - }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -15285,11 +15416,6 @@ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -15990,9 +16116,9 @@ }, "dependencies": { "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" } } }, @@ -17329,11 +17455,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -19151,48 +19272,6 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -19322,74 +19401,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - } - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } } diff --git a/screens/Contact/ContactDetailScreen.js b/screens/Contact/ContactDetailScreen.js index 25c3a193..34583a44 100644 --- a/screens/Contact/ContactDetailScreen.js +++ b/screens/Contact/ContactDetailScreen.js @@ -15,13 +15,13 @@ import { Linking, BackHandler, ActivityIndicator, + KeyboardAvoidingView, } 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'; -import { Label, Input, Icon, Picker, DatePicker, Textarea } from 'native-base'; +import { Label, Input, Icon, Picker, DatePicker, Textarea, Button } from 'native-base'; import Toast from 'react-native-easy-toast'; import { Col, Row, Grid } from 'react-native-easy-grid'; import ProgressBarAnimated from 'react-native-progress-bar-animated'; @@ -31,7 +31,8 @@ import { TabView, TabBar } from 'react-native-tab-view'; import { NavigationActions, StackActions } from 'react-navigation'; import MentionsTextInput from 'react-native-mentions'; import ParsedText from 'react-native-parsed-text'; -import * as Sentry from 'sentry-expo'; +//import * as Sentry from 'sentry-expo'; +import { BlurView } from 'expo-blur'; import moment from '../../languages/moment'; import sharedTools from '../../shared'; @@ -43,8 +44,13 @@ import { getByIdEnd, getActivitiesByContact, saveEnd, + deleteComment, + loadingFalse, + updatePrevious, } from '../../store/actions/contacts.actions'; +import { updatePrevious as updatePreviousGroups } from '../../store/actions/groups.actions'; import Colors from '../../constants/Colors'; +import statusIcon from '../../assets/icons/status.png'; import hasBibleIcon from '../../assets/icons/book-bookmark.png'; import readingBibleIcon from '../../assets/icons/word.png'; import statesBeliefIcon from '../../assets/icons/language.png'; @@ -60,10 +66,11 @@ import { searchLocations } from '../../store/actions/groups.actions'; let toastSuccess; let toastError; -const containerPadding = 35; +const containerPadding = 20; const windowWidth = Dimensions.get('window').width; const progressBarWidth = windowWidth - 100; const milestonesGridSize = windowWidth + 5; +const windowHeight = Dimensions.get('window').height; let keyboardDidShowListener, keyboardDidHideListener, focusListener, hardwareBackPressListener; //const hasNotch = Platform.OS === 'android' && StatusBar.currentHeight > 25; //const extraNotchHeight = hasNotch ? StatusBar.currentHeight : 0; @@ -117,9 +124,17 @@ const styles = StyleSheet.create({ }, tabStyle: { backgroundColor: '#FFFFFF' }, textStyle: { color: 'gray' }, + fieldsIcons: { + height: 22, + width: 22, + }, addRemoveIcons: { fontSize: 30, + marginRight: 0, + color: Colors.addRemoveIcons, }, + addIcons: { color: 'green' }, + removeIcons: { color: 'red' }, // Social Media Field socialMediaNames: { color: Colors.grayDark, @@ -128,22 +143,23 @@ const styles = StyleSheet.create({ // Form formContainer: { paddingTop: 10, - paddingBottom: 80, + paddingBottom: 100, paddingLeft: containerPadding, paddingRight: containerPadding, }, formRow: { - paddingTop: 20, - paddingBottom: 20, + paddingTop: 15, + paddingBottom: 15, width: '100%', }, formIconLabel: { marginLeft: 10, width: 'auto', + marginBottom: 'auto', }, formIcon: { color: Colors.tintColor, - fontSize: 25, + fontSize: 22, marginTop: 'auto', marginBottom: 'auto', }, @@ -159,17 +175,18 @@ const styles = StyleSheet.create({ formDivider: { borderBottomColor: '#CCCCCC', borderBottomWidth: 1, - marginLeft: 10, - marginRight: 10, }, formIconLabelCol: { width: 35, }, formIconLabelView: { alignItems: 'center', + marginTop: 'auto', + marginBottom: 'auto', }, formFieldMargin: { marginTop: 20, + marginBottom: 10, }, // Progress Section progressIcon: { height: '100%', width: '100%' }, @@ -224,9 +241,8 @@ const styles = StyleSheet.create({ }, noCommentsContainer: { padding: 20, - textAlignVertical: 'top', - textAlign: 'center', - height: 300, + height: '90%', + transform: [{ scaleY: -1 }], }, noCommentsImage: { opacity: 0.5, @@ -235,19 +251,10 @@ const styles = StyleSheet.create({ padding: 10, }, noCommentsText: { - textAlignVertical: 'center', textAlign: 'center', fontWeight: 'bold', color: '#A8A8A8', - padding: 5, - }, - noCommentsTextOffilne: { - textAlignVertical: 'center', - textAlign: 'center', - fontWeight: 'bold', - color: '#A8A8A8', - backgroundColor: '#fff2ac', - padding: 5, + marginTop: 10, }, contactTextField: { borderBottomWidth: 1, @@ -320,6 +327,44 @@ const styles = StyleSheet.create({ fontSize: 12, color: 'rgba(0,0,0,0.6)', }, + // Edit/Delete comment dialog + dialogBackground: { + position: 'absolute', + top: 0, + left: 0, + }, + dialogBox: { + backgroundColor: '#FFFFFF', + padding: 20, + marginLeft: 'auto', + marginRight: 'auto', + height: windowHeight - windowHeight * 0.55, + width: windowWidth - windowWidth * 0.1, + marginTop: windowHeight * 0.1, + }, + dialogButton: { + backgroundColor: Colors.tintColor, + borderRadius: 5, + width: 100, + marginTop: 20, + marginLeft: 'auto', + marginRight: 'auto', + }, + dialogContent: { + height: '100%', + width: '100%', + fontSize: 20, + textAlign: 'center', + color: Colors.grayDark, + marginBottom: 5, + }, + dialogComment: { + borderWidth: 1, + borderRadius: 5, + borderStyle: 'solid', + borderColor: '#B4B4B4', + color: Colors.tintColor, + }, }); const initialState = { @@ -331,22 +376,29 @@ const initialState = { peopleGroups: [], geonames: [], loadedLocal: false, - comments: [], + comments: { + data: [], + pagination: { + limit: 10, + offset: 0, + total: 0, + }, + }, loadComments: false, loadingMoreComments: false, - totalComments: 0, - commentsOffset: 0, - commentsLimit: 10, - activities: [], + activities: { + data: [], + pagination: { + limit: 10, + offset: 0, + total: 0, + }, + }, loadActivities: false, loadingMoreActivities: false, - totalActivities: 0, - activitiesOffset: 0, - activitiesLimit: 10, comment: '', progressBarValue: 0, overallStatusBackgroundColor: '#ffffff', - renderFab: true, showAssignedToModal: false, loading: false, moreFields: false, @@ -380,7 +432,12 @@ const initialState = { connectionGroups: [], unmodifiedConnectionGroups: [], assignedToContacts: [], - unmodifedAssignedToContacts: [] + unmodifedAssignedToContacts: [], + commentDialog: { + toggle: false, + data: {}, + delete: false, + }, }; const safeFind = (found, prop) => { @@ -401,14 +458,14 @@ class ContactDetailScreen extends React.Component { : i18n.t('contactDetailScreen.addNewContact'); let headerRight = () => ( - + {i18n.t('global.save')} @@ -420,14 +477,14 @@ class ContactDetailScreen extends React.Component { if (params.onEnableEdit && params.contactId && params.onlyView) { headerRight = () => ( - + {i18n.t('global.edit')} @@ -439,10 +496,8 @@ class ContactDetailScreen extends React.Component { { - params.backButtonTap(); - }} - style={{ paddingLeft: 16, color: '#FFFFFF', paddingRight: 16 }} + onPress={params.backButtonTap} + style={{ paddingLeft: 16, color: Colors.onPressIcons, paddingRight: 16 }} /> ); } else { @@ -452,11 +507,11 @@ class ContactDetailScreen extends React.Component { type="AntDesign" name="close" style={[ - { color: '#FFFFFF', marginTop: 'auto', marginBottom: 'auto' }, + { color: Colors.onPressIcons, marginTop: 'auto', marginBottom: 'auto' }, self && self.props.isRTL ? { paddingRight: 16 } : { paddingLeft: 16 }, ]} /> - + {i18n.t('global.cancel')} @@ -475,13 +530,13 @@ class ContactDetailScreen extends React.Component { fontWeight: 'bold', width: params.onlyView ? Platform.select({ - android: 200, - ios: 180, - }) + android: 200, + ios: 180, + }) : Platform.select({ - android: 180, - ios: 140, - }), + android: 180, + ios: 140, + }), marginLeft: params.onlyView ? undefined : 25, }, }; @@ -494,12 +549,22 @@ class ContactDetailScreen extends React.Component { componentDidMount() { const { navigation } = this.props; this.onLoad(); - navigation.setParams({ - onEnableEdit: this.onEnableEdit, - onDisableEdit: this.onDisableEdit, - onSaveContact: this.onSaveContact, - backButtonTap: this.backButtonTap, - }); + + let params = { + onEnableEdit: this.onEnableEdit.bind(this), + onDisableEdit: this.onDisableEdit.bind(this), + onSaveContact: this.onSaveContact.bind(this), + backButtonTap: this.backButtonTap.bind(this), + }; + // Add afterBack param to execute 'parents' functions (ContactsView, NotificationsView) + if (!navigation.state.params.afterBack) { + params = { + ...params, + afterBack: this.afterBack.bind(this), + }; + } + navigation.setParams(params); + keyboardDidShowListener = Keyboard.addListener( 'keyboardDidShow', this.keyboardDidShow.bind(this), @@ -509,34 +574,21 @@ class ContactDetailScreen extends React.Component { this.keyboardDidHide.bind(this), ); focusListener = navigation.addListener('didFocus', () => { - if ( - typeof this.props.navigation.state.params.contactId !== 'undefined' && - this.state.loadedLocal - ) { - this.setState( - { - loading: false, - }, - () => { - this.onRefresh(this.props.navigation.state.params.contactId); - }, - ); + //Focus on 'detail mode' (going back or open detail view) + if (typeof this.props.navigation.state.params.contactId !== 'undefined') { + this.props.loadingFalse(); + this.onRefresh(this.props.navigation.state.params.contactId, true); } }); + // Android bottom back button listener hardwareBackPressListener = BackHandler.addEventListener('hardwareBackPress', () => { - sharedTools.onlyExecuteLastCall( - null, - () => { - this.backButtonTap(); - }, - 1000, - ); + this.props.navigation.state.params.backButtonTap(); return true; }); } componentDidCatch(error, errorInfo) { - Sentry.captureException(errorInfo); + //Sentry.captureException(errorInfo); } componentWillUnmount() { @@ -551,35 +603,22 @@ class ContactDetailScreen extends React.Component { contact, loading, comments, - totalComments, loadingComments, activities, - totalActivities, loadingActivities, - newComment, foundGeonames, } = nextProps; let newState = { ...prevState, loading, - comments: comments || prevState.comments, - totalComments: totalComments || prevState.totalComments, + comments: prevState.comments, loadComments: loadingComments, - activities: activities || prevState.activities, - totalActivities: totalActivities || prevState.totalActivities, + activities: prevState.activities, loadActivities: loadingActivities, contact: prevState.contact, unmodifiedContact: prevState.unmodifiedContact, }; - // NEW COMMENT - if (newComment) { - newState.comments.unshift(newComment); - newState = { - ...newState, - comments: newState.comments, - }; - } // SAVE / GET BY ID if (contact) { newState = { @@ -591,9 +630,6 @@ class ContactDetailScreen extends React.Component { ...contact, }, }; - if (newState.contact.oldID) { - delete newState.contact.oldID; - } if (newState.contact.overall_status) { newState = { ...newState, @@ -663,15 +699,16 @@ class ContactDetailScreen extends React.Component { }); } if (newState.contact.subassigned) { - // Clear collection newState = { ...newState, - subAssignedContacts: [] + subAssignedContacts: [], }; newState.contact.subassigned.values.forEach((subassignedContact) => { - const foundSubassigned = newState.usersContacts.find((user) => user.value === subassignedContact.value); + const foundSubassigned = newState.usersContacts.find( + (user) => user.value === subassignedContact.value, + ); if (!foundSubassigned) { // Add non existent contact subassigned in subassigned list (user does not have access permission to this contacts) newState = { @@ -680,31 +717,31 @@ class ContactDetailScreen extends React.Component { ...newState.subAssignedContacts, { name: subassignedContact.name, - value: subassignedContact.value - } + value: subassignedContact.value, + }, ], unmodifiedSubAssignedContacts: [ ...newState.unmodifiedSubAssignedContacts, { name: subassignedContact.name, - value: subassignedContact.value - } - ] + value: subassignedContact.value, + }, + ], }; } }); - } if (newState.contact.relation) { - // Clear collection newState = { ...newState, - relationContacts: [] + relationContacts: [], }; newState.contact.relation.values.forEach((relationContact) => { - const foundRelation = newState.usersContacts.find((user) => user.value === relationContact.value); + const foundRelation = newState.usersContacts.find( + (user) => user.value === relationContact.value, + ); if (!foundRelation) { // Add non existent contact relation in relation list (user does not have access permission to this contacts) newState = { @@ -713,31 +750,31 @@ class ContactDetailScreen extends React.Component { ...newState.relationContacts, { name: relationContact.name, - value: relationContact.value - } + value: relationContact.value, + }, ], unmodifiedRelationContacts: [ ...newState.unmodifiedRelationContacts, { name: relationContact.name, - value: relationContact.value - } - ] + value: relationContact.value, + }, + ], }; } }); - } if (newState.contact.baptized_by) { - // Clear collection newState = { ...newState, - baptizedByContacts: [] + baptizedByContacts: [], }; newState.contact.baptized_by.values.forEach((baptizedByContact) => { - const foundBaptized = newState.usersContacts.find((user) => user.value === baptizedByContact.value); + const foundBaptized = newState.usersContacts.find( + (user) => user.value === baptizedByContact.value, + ); if (!foundBaptized) { // Add non existent contact relation in relation list (user does not have access permission to this contacts) newState = { @@ -746,31 +783,31 @@ class ContactDetailScreen extends React.Component { ...newState.baptizedByContacts, { name: baptizedByContact.name, - value: baptizedByContact.value - } + value: baptizedByContact.value, + }, ], unmodifiedBaptizedByContacts: [ ...newState.unmodifiedBaptizedByContacts, { name: baptizedByContact.name, - value: baptizedByContact.value - } - ] + value: baptizedByContact.value, + }, + ], }; } }); - } if (newState.contact.baptized) { - // Clear collection newState = { ...newState, - baptizedContacts: [] + baptizedContacts: [], }; newState.contact.baptized.values.forEach((baptizedContact) => { - const foundBaptized = newState.usersContacts.find((user) => user.value === baptizedContact.value); + const foundBaptized = newState.usersContacts.find( + (user) => user.value === baptizedContact.value, + ); if (!foundBaptized) { // Add non existent contact baptized to list (user does not have access permission to this contacts) newState = { @@ -779,31 +816,31 @@ class ContactDetailScreen extends React.Component { ...newState.baptizedContacts, { name: baptizedContact.name, - value: baptizedContact.value - } + value: baptizedContact.value, + }, ], unmodifiedBaptizedContacts: [ ...newState.unmodifiedBaptizedContacts, { name: baptizedContact.name, - value: baptizedContact.value - } - ] + value: baptizedContact.value, + }, + ], }; } }); - } if (newState.contact.coached_by) { - // Clear collection newState = { ...newState, - coachedByContacts: [] + coachedByContacts: [], }; newState.contact.coached_by.values.forEach((coachedByContact) => { - const foundcoachedBy = newState.usersContacts.find((user) => user.value === coachedByContact.value); + const foundcoachedBy = newState.usersContacts.find( + (user) => user.value === coachedByContact.value, + ); if (!foundcoachedBy) { // Add non existent contact coachedBy to list (user does not have access permission to this contacts) newState = { @@ -812,31 +849,31 @@ class ContactDetailScreen extends React.Component { ...newState.coachedByContacts, { name: coachedByContact.name, - value: coachedByContact.value - } + value: coachedByContact.value, + }, ], unmodifiedCoachedByContacts: [ ...newState.unmodifiedCoachedByContacts, { name: coachedByContact.name, - value: coachedByContact.value - } - ] + value: coachedByContact.value, + }, + ], }; } }); - } if (newState.contact.coaching) { - // Clear collection newState = { ...newState, - coachedContacts: [] + coachedContacts: [], }; newState.contact.coaching.values.forEach((coachedContact) => { - const foundCoached = newState.usersContacts.find((user) => user.value === coachedContact.value); + const foundCoached = newState.usersContacts.find( + (user) => user.value === coachedContact.value, + ); if (!foundCoached) { // Add non existent contact coached to list (user does not have access permission to this contacts) newState = { @@ -845,27 +882,25 @@ class ContactDetailScreen extends React.Component { ...newState.coachedContacts, { name: coachedContact.name, - value: coachedContact.value - } + value: coachedContact.value, + }, ], unmodifiedCoachedContacts: [ ...newState.unmodifiedCoachedContacts, { name: coachedContact.name, - value: coachedContact.value - } - ] + value: coachedContact.value, + }, + ], }; } }); - } if (newState.contact.groups) { - // Clear collection newState = { ...newState, - connectionGroups: [] + connectionGroups: [], }; newState.contact.groups.values.forEach((groupConnection) => { @@ -878,86 +913,99 @@ class ContactDetailScreen extends React.Component { ...newState.connectionGroups, { name: groupConnection.name, - value: groupConnection.value - } + value: groupConnection.value, + }, ], unmodifiedConnectionGroups: [ ...newState.unmodifiedConnectionGroups, { name: groupConnection.name, - value: groupConnection.value - } - ] + value: groupConnection.value, + }, + ], }; } }); - } if (newState.contact.assigned_to) { - // Clear collection newState = { ...newState, - assignedToContacts: [] + assignedToContacts: [], }; - let foundAssigned = newState.users.find((user) => user.key === newState.contact.assigned_to.key); - if (!foundAssigned) { - // Add non existent group to list (user does not have access permission to this groups) - newState = { - ...newState, - assignedToContacts: [ - ...newState.assignedToContacts, - { - label: foundAssigned.label, - key: foundAssigned.key - } - ], - unmodifedAssignedToContacts: [ - ...newState.unmodifedAssignedToContacts, - { - label: foundAssigned.label, - key: foundAssigned.key - } - ] - }; - } - + let foundAssigned = newState.users.find( + (user) => user.key === newState.contact.assigned_to.key, + ); + if (!foundAssigned) { + // Add non existent group to list (user does not have access permission to this groups) + newState = { + ...newState, + assignedToContacts: [ + ...newState.assignedToContacts, + { + label: newState.contact.assigned_to.label, + key: newState.contact.assigned_to.key, + }, + ], + unmodifedAssignedToContacts: [ + ...newState.unmodifedAssignedToContacts, + { + label: newState.contact.assigned_to.label, + key: newState.contact.assigned_to.key, + }, + ], + }; + } } } // GET COMMENTS if (comments) { - // NEW COMMENTS (PAGINATION) - if (prevState.commentsOffset > 0) { + if ( + newState.contact.ID && + Object.prototype.hasOwnProperty.call(comments, newState.contact.ID) + ) { + // NEW COMMENTS (PAGINATION) + if (comments[newState.contact.ID].pagination.offset > 0) { + newState = { + ...newState, + loadingMoreComments: false, + }; + } + // ONLINE MODE: USE STATE PAGINATION - OFFLINE MODE: USE STORE PAGINATION + // UPDATE OFFSET newState = { ...newState, - comments: prevState.comments.concat(comments), - loadingMoreComments: false, + comments: { + ...comments[newState.contact.ID], + }, }; } - newState = { - // UPDATE OFFSET - ...newState, - commentsOffset: prevState.commentsOffset + prevState.commentsLimit, - }; } - // GET ACTIVITITES + // GET ACTIVITIES if (activities) { - // NEW ACTIVITIES (PAGINATION) - if (prevState.activitiesOffset > 0) { + if ( + newState.contact.ID && + Object.prototype.hasOwnProperty.call(activities, newState.contact.ID) + ) { + // NEW ACTIVITIES (PAGINATION) + if (activities[newState.contact.ID].pagination.offset > 0) { + newState = { + ...newState, + loadingMoreActivities: false, + }; + } + // ONLINE MODE: USE STATE PAGINATION - OFFLINE MODE: USE STORE PAGINATION + // UPDATE OFFSET newState = { ...newState, - activities: prevState.activities.concat(activities), - loadingMoreActivities: false, + activities: { + ...activities[newState.contact.ID], + }, }; } - newState = { - // UPDATE OFFSET - ...newState, - activitiesOffset: prevState.activitiesOffset + prevState.activitiesLimit, - }; } // GET FILTERED LOCATIONS @@ -998,7 +1046,7 @@ class ContactDetailScreen extends React.Component { // Same offline contact created in DB (AutoID to DBID) if ( (typeof contact.ID !== 'undefined' && typeof this.state.contact.ID === 'undefined') || - contact.ID.toString() === this.state.contact.ID.toString() || + (contact.ID && contact.ID.toString() === this.state.contact.ID.toString()) || (contact.oldID && contact.oldID === this.state.contact.ID.toString()) ) { // Highlight Updates -> Compare this.state.contact with contact and show differences @@ -1010,6 +1058,21 @@ class ContactDetailScreen extends React.Component { this.onSaveComment(); } this.getContactByIdEnd(); + // Add contact to 'previousContacts' array on creation + if ( + !this.props.previousContacts.find( + (previousContact) => previousContact.contactId === parseInt(contact.ID), + ) + ) { + this.props.updatePrevious([ + ...this.props.previousContacts, + { + contactId: parseInt(contact.ID), + onlyView: true, + contactName: contact.title, + }, + ]); + } } } @@ -1021,10 +1084,11 @@ class ContactDetailScreen extends React.Component { // Same offline contact created in DB (AutoID to DBID) if ( (typeof contact.ID !== 'undefined' && typeof this.state.contact.ID === 'undefined') || - contact.ID.toString() === this.state.contact.ID.toString() || + (contact.ID && contact.ID.toString() === this.state.contact.ID.toString()) || (contact.oldID && contact.oldID === this.state.contact.ID.toString()) ) { - this.onRefreshCommentsActivities(contact.ID); + // Highlight Updates -> Compare this.state.group with group and show differences + this.onRefreshCommentsActivities(contact.ID, true); toastSuccess.show( {i18n.t('global.success.save')} @@ -1056,11 +1120,12 @@ class ContactDetailScreen extends React.Component { 3000, ); } - + // Fix to press back button in comments tab 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); + navigation.goBack(null); + navigation.state.params.afterBack(); }, 1000); } } @@ -1149,15 +1214,42 @@ class ContactDetailScreen extends React.Component { }, ); } else { - this.executeBack(navigation, params); + //Fix to returning using Android back button! -> goBack(null) + navigation.goBack(null); + navigation.state.params.afterBack(); } }; - executeBack = (navigation, params) => { - if (params.previousList.length > 0) { - navigation.goBack(); - params.onBackFromSameScreen(); - } else if (params.fromNotificationView) { + afterBack = () => { + let { navigation } = this.props; + let newPreviousContacts = [...this.props.previousContacts]; + newPreviousContacts.pop(); + this.props.updatePrevious(newPreviousContacts); + if (newPreviousContacts.length > 0) { + this.props.loadingFalse(); + let currentParams = { + ...newPreviousContacts[newPreviousContacts.length - 1], + }; + this.setState({ + contact: { + ID: currentParams.contactId, + title: currentParams.contactName, + sources: { + values: [ + { + value: 'personal', + }, + ], + }, + seeker_path: 'none', + }, + overallStatusBackgroundColor: '#ffffff', + }); + navigation.setParams({ + ...currentParams, + }); + this.onRefresh(currentParams.contactId, true); + } else if (navigation.state.params.fromNotificationView) { const resetAction = StackActions.reset({ index: 0, actions: [NavigationActions.navigate({ routeName: 'ContactList' })], @@ -1165,36 +1257,23 @@ class ContactDetailScreen extends React.Component { navigation.dispatch(resetAction); navigation.navigate('NotificationList'); } else { - navigation.goBack(); // Prevent error when view loaded from GroupDetailScreen.js - if (typeof params.onGoBack === 'function') { - params.onGoBack(); + if (typeof navigation.state.params.onGoBack === 'function') { + navigation.state.params.onGoBack(); } } }; - onRefresh(contactId) { - if (!self.state.loading) { + onRefresh(contactId, forceRefresh = false) { + if (!self.state.loading || forceRefresh) { self.getContactById(contactId); - self.onRefreshCommentsActivities(contactId); + self.onRefreshCommentsActivities(contactId, true); } } - onRefreshCommentsActivities(contactId) { - this.setState( - { - comments: [], - activities: [], - commentsOffset: 0, - activitiesOffset: 0, - }, - () => { - this.getContactComments(contactId); - if (this.props.isConnected) { - this.getContactActivities(contactId); - } - }, - ); + onRefreshCommentsActivities(contactId, resetPagination = false) { + this.getContactComments(contactId, resetPagination); + this.getContactActivities(contactId, resetPagination); } getLists = async (contactId) => { @@ -1207,6 +1286,7 @@ class ContactDetailScreen extends React.Component { users: JSON.parse(users).map((user) => ({ key: user.ID, label: user.name, + contactID: parseInt(user.contact_id), })), }; } @@ -1246,7 +1326,7 @@ class ContactDetailScreen extends React.Component { })), loadedLocal: true, sources: [...sourcesList], - unmodifiedSources: [...sourcesList] + unmodifiedSources: [...sourcesList], }; this.setState(newState, () => { @@ -1265,27 +1345,51 @@ class ContactDetailScreen extends React.Component { this.props.getByIdEnd(); } - getContactComments(contactId) { - if (!this.state.loadComments) { - this.props.getComments( - this.props.userData.domain, - this.props.userData.token, - contactId, - this.state.commentsOffset, - this.state.commentsLimit, - ); + getContactComments(contactId, resetPagination = false) { + if (this.props.isConnected) { + if (resetPagination) { + this.props.getComments(this.props.userData.domain, this.props.userData.token, contactId, { + offset: 0, + limit: 10, + }); + } else { + //ONLY GET DATA IF THERE IS MORE DATA TO GET + if ( + !this.state.loadComments && + this.state.comments.pagination.offset < this.state.comments.pagination.total + ) { + this.props.getComments( + this.props.userData.domain, + this.props.userData.token, + contactId, + this.state.comments.pagination, + ); + } + } } } - getContactActivities(contactId) { - if (!this.state.loadActivities) { - this.props.getActivities( - this.props.userData.domain, - this.props.userData.token, - contactId, - this.state.activitiesOffset, - this.state.activitiesLimit, - ); + getContactActivities(contactId, resetPagination = false) { + if (this.props.isConnected) { + if (resetPagination) { + this.props.getActivities(this.props.userData.domain, this.props.userData.token, contactId, { + offset: 0, + limit: 10, + }); + } else { + //ONLY GET DATA IF THERE IS MORE DATA TO GET + if ( + !this.state.loadActivities && + this.state.activities.pagination.offset < this.state.activities.pagination.total + ) { + this.props.getActivities( + this.props.userData.domain, + this.props.userData.token, + contactId, + this.state.activities.pagination, + ); + } + } } } @@ -1314,7 +1418,8 @@ class ContactDetailScreen extends React.Component { }; onDisableEdit = () => { - const { unmodifiedContact, + const { + unmodifiedContact, unmodifiedSources, unmodifiedSubAssignedContacts, unmodifiedRelationContacts, @@ -1323,11 +1428,14 @@ class ContactDetailScreen extends React.Component { unmodifiedCoachedByContacts, unmodifiedCoachedContacts, unmodifiedConnectionGroups, - unmodifedAssignedToContacts + unmodifedAssignedToContacts, } = this.state; this.setState((state) => { + // Set correct index in Tab position according to view mode and current tab position const indexFix = - state.tabViewConfig.index > 1 ? state.tabViewConfig.index + 1 : state.tabViewConfig.index; + state.tabViewConfig.index > 1 && !state.onlyView + ? state.tabViewConfig.index + 1 + : state.tabViewConfig.index; return { onlyView: true, contact: { @@ -1349,7 +1457,7 @@ class ContactDetailScreen extends React.Component { coachedByContacts: [...unmodifiedCoachedByContacts], coachedContacts: [...unmodifiedCoachedContacts], connectionGroups: [...unmodifiedConnectionGroups], - assignedToContacts: [...unmodifedAssignedToContacts] + assignedToContacts: [...unmodifedAssignedToContacts], }; }); this.props.navigation.setParams({ hideTabBar: false, onlyView: true }); @@ -1528,7 +1636,7 @@ class ContactDetailScreen extends React.Component { ID: this.state.contact.ID, }; } - if(contactToSave.assigned_to) { + if (contactToSave.assigned_to) { contactToSave = { ...contactToSave, assigned_to: `user-${contactToSave.assigned_to.key}`, @@ -1653,7 +1761,7 @@ class ContactDetailScreen extends React.Component { getCommentsAndActivities() { const { comments, activities } = this.state; - const list = comments.concat(activities); + const list = comments.data.concat(activities.data); return list .filter((item, index) => list.indexOf(item) === index) .sort((a, b) => new Date(a.date).getTime() < new Date(b.date).getTime()); @@ -1699,77 +1807,88 @@ class ContactDetailScreen extends React.Component { ); - onBackFromSameScreen = () => { - const { navigation } = this.props; - const { params } = navigation.state; - const newPreviousList = params.previousList; - const previousParams = newPreviousList[newPreviousList.length - 1]; - newPreviousList.pop(); - navigation.setParams({ - ...previousParams, - previousList: newPreviousList, - }); - this.setState(initialState, () => { - this.onLoad(); - }); - }; - - goToContactDetailScreen = (contactID) => { - const { navigation } = this.props; + goToContactDetailScreen = (contactID, name) => { + let { navigation } = this.props; /* eslint-disable */ - const { params } = navigation.state; - const { ID, title } = this.state.contact; - params.previousList.push({ - contactId: ID, - onlyView: true, - contactName: title, - }); + // Save new contact in 'previousContacts' array + if ( + !this.props.previousContacts.find( + (previousContact) => previousContact.contactId === contactID, + ) + ) { + // Add contact to 'previousContacts' array on creation + this.props.updatePrevious([ + ...this.props.previousContacts, + { + contactId: contactID, + onlyView: true, + contactName: name, + }, + ]); + } navigation.push('ContactDetail', { - ...params, // previousList, onGoBack() contactId: contactID, onlyView: true, - contactName: safeFind( - this.state.usersContacts.find((user) => user.value === contactID), - 'name', - ), - backButtonTap: this.backButtonTap.bind(this), - onBackFromSameScreen: this.onBackFromSameScreen.bind(this), + contactName: name, + afterBack: () => this.afterBack(), }); /* eslint-enable */ }; - goToGroupDetailScreen = (groupID) => { + goToGroupDetailScreen = (groupID, name) => { + // Clean 'previousContacts' array + this.props.updatePreviousGroups([ + { + groupId: groupID, + onlyView: true, + groupName: name, + }, + ]); this.props.navigation.navigate('GroupDetail', { groupId: groupID, onlyView: true, - groupName: safeFind( - this.state.groups.find((groupItem) => groupItem.value === groupID), - 'name', - ), - previousList: [], + groupName: name, }); }; noCommentsRender = () => ( - - - - - - {i18n.t('contactDetailScreen.noContactCommentPlacheHolder')} - - - {i18n.t('contactDetailScreen.noContactCommentPlacheHolder1')} - - {!this.props.isConnected && ( - - {i18n.t('contactDetailScreen.noContactCommentPlacheHolderOffline')} - - )} - + this.onRefreshCommentsActivities(this.state.contact.ID, true)} + /> + }> + + + + + + + + {i18n.t('contactDetailScreen.noContactCommentPlacheHolder')} + + + + + {i18n.t('contactDetailScreen.noContactCommentPlacheHolder1')} + + + {!this.props.isConnected && ( + + + {i18n.t('contactDetailScreen.noContactCommentPlacheHolderOffline')} + + + )} + + + ); detailView = () => ( + /*_viewable_*/ {this.state.onlyView ? ( @@ -1780,16 +1899,27 @@ class ContactDetailScreen extends React.Component { onRefresh={() => this.onRefresh(this.state.contact.ID)} /> }> - - + + + + + + + + + - + + + + {this.state.contact.assigned_to + ? this.renderContactLink(this.state.contact.assigned_to) + : null} - {(this.state.contact.assigned_to) ? this.showAssignedUser() : null} @@ -2034,18 +2177,18 @@ class ContactDetailScreen extends React.Component { ]}> {this.state.contact.location_grid ? this.state.contact.location_grid.values - .map( - function (location) { - return safeFind( - this.state.geonames.find( - (geoname) => geoname.value === location.value, - ), - 'name', - ); - }.bind(this), - ) - .filter(String) - .join(', ') + .map( + function (location) { + return safeFind( + this.state.geonames.find( + (geoname) => geoname.value === location.value, + ), + 'name', + ); + }.bind(this), + ) + .filter(String) + .join(', ') : ''} @@ -2068,18 +2211,18 @@ class ContactDetailScreen extends React.Component { ]}> {this.state.contact.people_groups ? this.state.contact.people_groups.values - .map( - function (peopleGroup) { - return safeFind( - this.state.peopleGroups.find( - (person) => person.value === peopleGroup.value, - ), - 'name', - ); - }.bind(this), - ) - .filter(String) - .join(', ') + .map( + function (peopleGroup) { + return safeFind( + this.state.peopleGroups.find( + (person) => person.value === peopleGroup.value, + ), + 'name', + ); + }.bind(this), + ) + .filter(String) + .join(', ') : ''} @@ -2092,7 +2235,11 @@ class ContactDetailScreen extends React.Component { - + @@ -2126,7 +2273,7 @@ class ContactDetailScreen extends React.Component { ]}> {this.state.contact.gender ? this.props.contactSettings.fields.gender.values[this.state.contact.gender] - .label + .label : ''} @@ -2139,7 +2286,11 @@ class ContactDetailScreen extends React.Component { - + {this.state.contact.sources ? `${this.state.contact.sources.values - .map( - (source) => - this.state.sources.find( - (sourceItem) => sourceItem.value === source.value, - ).name, - ) - .join(', ')}` + .map( + (source) => + this.state.sources.find( + (sourceItem) => sourceItem.value === source.value, + ).name, + ) + .join(', ')}` : ''} @@ -2170,145 +2321,125 @@ class ContactDetailScreen extends React.Component { ) : ( - - - - - + + + + + + + + + + - {this.renderStatusPickerItems()} - - - - - - - - - - - - - - - - - - - - - + {this.renderStatusPickerItems()} + + + + + + + + + + + + + + + + + + + + + - - - {this.state.nameRequired ? ( - - {i18n.t('contactDetailScreen.fullName.error')} - - ) : null} + : null + }> + - - { - this.updateShowAssignedToModal(true); - }}> - - - - - - - - - - - - - - - - - - - {this.renderPickerItems([...this.state.users, ...this.state.assignedToContacts])} - - - - + {this.state.nameRequired ? ( + + {i18n.t('contactDetailScreen.fullName.error')} + + ) : null} + + + { + this.updateShowAssignedToModal(true); + }}> - + @@ -2316,573 +2447,630 @@ class ContactDetailScreen extends React.Component { - - { - subAssignedSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.subAssignedContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.subassigned, - [...this.state.subAssignedContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.subAssignThisContact'), - }} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundSubassignedIndex = this.state.subAssignedContacts.findIndex( - (subassigned) => subassigned.value === id - ); - if (foundSubassignedIndex > -1) { - // Remove subassigned from list - const subAssignedContacts = [...this.state.subAssignedContacts]; - subAssignedContacts.splice(foundSubassignedIndex, 1); - this.setState({ - subAssignedContacts: [...subAssignedContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - renderRow={(id, onPress, item) => ( - + + {this.renderPickerItems([ + ...this.state.users, + ...this.state.assignedToContacts, + ])} + + + + + + + + + + + + + + + + + + + + + + { + subAssignedSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.subAssignedContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.subassigned, [ + ...this.state.subAssignedContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.subAssignThisContact'), + }} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundSubassignedIndex = this.state.subAssignedContacts.findIndex( + (subassigned) => subassigned.value === id, + ); + if (foundSubassignedIndex > -1) { + // Remove subassigned from list + const subAssignedContacts = [...this.state.subAssignedContacts]; + subAssignedContacts.splice(foundSubassignedIndex, 1); + this.setState({ + subAssignedContacts: [...subAssignedContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} + /> + )} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - {this.state.contact.contact_phone ? ( - this.state.contact.contact_phone.map((phone, index) => - !phone.delete ? ( - - - - - - - - { - this.onPhoneFieldChange(value, index, phone.key, this); - }} - style={styles.contactTextField} - keyboardType="phone-pad" - /> - - + + + )} + filterOnKey="name" + inputContainerStyle={styles.selectizeField} + /> + + + + + + + + + + + + + + + + {this.state.contact.contact_phone ? ( + this.state.contact.contact_phone.map((phone, index) => + !phone.delete ? ( + + + { - this.onRemovePhoneField(index, this); - }} + type="FontAwesome" + name="phone" + style={[styles.formIcon, { opacity: 0 }]} /> - - - ) : null, - ) - ) : ( - - )} - - - - - - - - - - + + + + { + this.onPhoneFieldChange(value, index, phone.key, this); + }} + style={styles.contactTextField} + keyboardType="phone-pad" + /> + + + { + this.onRemovePhoneField(index, this); + }} + /> + + + ) : null, + ) + ) : ( + + )} + + + - - - {this.state.contact.contact_email ? ( - this.state.contact.contact_email.map((email, index) => - !email.delete ? ( - - - - - - - - { - this.onEmailFieldChange(value, index, email.key, this); - }} - style={styles.contactTextField} - keyboardType="email-address" - /> - - + + + + + + + + + + {this.state.contact.contact_email ? ( + this.state.contact.contact_email.map((email, index) => + !email.delete ? ( + + + { - this.onRemoveEmailField(index, this); - }} + type="FontAwesome" + name="envelope" + style={[styles.formIcon, { opacity: 0 }]} /> - - - ) : null, - ) - ) : ( - - )} - - - - - - - - - - + + + + { + this.onEmailFieldChange(value, index, email.key, this); + }} + style={styles.contactTextField} + keyboardType="email-address" + /> + + + { + this.onRemoveEmailField(index, this); + }} + /> + + + ) : null, + ) + ) : ( + + )} + + + - - - {Object.keys(this.props.contactSettings.channels) - .filter( - (channelName) => - channelName !== 'phone' && channelName !== 'email' && channelName !== 'address', - ) - .map((channelName, channelNameIndex) => { - const propertyName = `contact_${channelName}`; - return ( - - {this.state.contact[propertyName] - ? this.state.contact[propertyName].map((socialMedia, socialMediaIndex) => + + + + + + + + + + {Object.keys(this.props.contactSettings.channels) + .filter( + (channelName) => + channelName !== 'phone' && channelName !== 'email' && channelName !== 'address', + ) + .map((channelName, channelNameIndex) => { + const propertyName = `contact_${channelName}`; + return ( + + {this.state.contact[propertyName] + ? this.state.contact[propertyName].map((socialMedia, socialMediaIndex) => !socialMedia.key ? this.renderSocialMediaField( - socialMediaIndex, - socialMedia, - propertyName, - channelName, - ) + socialMediaIndex, + socialMedia, + propertyName, + channelName, + ) : null, ) - : null} - - ); - })} - {Object.keys(this.props.contactSettings.channels) - .filter( - (channelName) => - channelName !== 'phone' && channelName !== 'email' && channelName !== 'address', - ) - .map((channelName, channelNameIndex) => { - const propertyName = `contact_${channelName}`; - return ( - - {this.state.contact[propertyName] - ? this.state.contact[propertyName].map((socialMedia, socialMediaIndex) => + : null} + + ); + })} + {Object.keys(this.props.contactSettings.channels) + .filter( + (channelName) => + channelName !== 'phone' && channelName !== 'email' && channelName !== 'address', + ) + .map((channelName, channelNameIndex) => { + const propertyName = `contact_${channelName}`; + return ( + + {this.state.contact[propertyName] + ? this.state.contact[propertyName].map((socialMedia, socialMediaIndex) => socialMedia.key && !socialMedia.delete ? this.renderSocialMediaField( - socialMediaIndex, - socialMedia, - propertyName, - channelName, - ) + socialMediaIndex, + socialMedia, + propertyName, + channelName, + ) : null, ) - : null} + : null} + + ); + })} + + + + + + + + + + + + + + {this.state.contact.contact_address ? ( + this.state.contact.contact_address.map((address, index) => + !address.delete ? ( + + + + + - ); - })} - - - - - - - - - - + + { + this.onAddressFieldChange(value, index, address.key, this); + }} + style={styles.contactTextField} + /> + + + { + this.onRemoveAddressField(index, this); + }} + /> + + + ) : null, + ) + ) : ( + + )} + + + + + + + + + + + + + - - - {this.state.contact.contact_address ? ( - this.state.contact.contact_address.map((address, index) => - !address.delete ? ( - - - - - - - - { - this.onAddressFieldChange(value, index, address.key, this); - }} - style={styles.contactTextField} - /> - - - { - this.onRemoveAddressField(index, this); - }} - /> - - - ) : null, - ) - ) : ( - - )} - - - - - - - - - - - - - - + + + { + geonamesSelectizeRef = selectize; + }} + itemId="value" + items={this.state.foundGeonames} + selectedItems={this.getSelectizeItems( + this.state.contact.location_grid, + this.state.geonames, + )} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.selectLocations'), + }} + renderChip={(id, onClose, item, style, iconStyle) => ( + - - - - { - geonamesSelectizeRef = selectize; - }} - itemId="value" - items={this.state.foundGeonames} - selectedItems={this.getSelectizeItems( - this.state.contact.location_grid, - this.state.geonames, - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.selectLocations'), - }} - renderChip={(id, onClose, item, style, iconStyle) => ( - - )} - renderRow={(id, onPress, item) => ( - ( + + - - - {item.name} - - - - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - textInputProps={{ - onChangeText: this.searchLocationsDelayed, - }} - /> - - - - - - - - - - - - - - - - - - - - { - peopleGroupsSelectizeRef = selectize; - }} - itemId="value" - items={this.state.peopleGroups} - selectedItems={this.getSelectizeItems( - this.state.contact.people_groups, - this.state.peopleGroups, - )} - textInputProps={{ - placeholder: i18n.t('global.selectPeopleGroups'), - }} - renderRow={(id, onPress, item) => ( - + + + )} + filterOnKey="name" + inputContainerStyle={styles.selectizeField} + textInputProps={{ + onChangeText: this.searchLocationsDelayed, + }} + /> + + + + + + + + + + + + + + + + + + + + { + peopleGroupsSelectizeRef = selectize; + }} + itemId="value" + items={this.state.peopleGroups} + selectedItems={this.getSelectizeItems( + this.state.contact.people_groups, + this.state.peopleGroups, + )} + textInputProps={{ + placeholder: i18n.t('global.selectPeopleGroups'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - - - - - - {Object.keys(this.props.contactSettings.fields.age.values).map((key) => { - const optionData = this.props.contactSettings.fields.age.values[key]; - return ; - })} - - - - - - - - - - - - - - - - - - - - - - {Object.keys(this.props.contactSettings.fields.gender.values).map((key) => { - const optionData = this.props.contactSettings.fields.gender.values[key]; - return ; - })} - - - - - - - - - - - - - - - - - + + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + - - - - { - sourcesSelectizeRef = selectize; - }} - itemId="value" - items={this.state.sources} - selectedItems={ - this.state.contact.sources - ? // Only add option elements (by contact sources) does exist in source list + )} + filterOnKey="name" + inputContainerStyle={styles.selectizeField} + /> + + + + + + + + + + + + + + + + + + + + + {Object.keys(this.props.contactSettings.fields.age.values).map((key) => { + const optionData = this.props.contactSettings.fields.age.values[key]; + return ; + })} + + + + + + + + + + + + + + + + + + + + + + {Object.keys(this.props.contactSettings.fields.gender.values).map((key) => { + const optionData = this.props.contactSettings.fields.gender.values[key]; + return ; + })} + + + + + + + + + + + + + + + + + + + + + { + sourcesSelectizeRef = selectize; + }} + itemId="value" + items={this.state.sources} + selectedItems={ + this.state.contact.sources + ? // Only add option elements (by contact sources) does exist in source list this.state.contact.sources.values .filter((contactSource) => this.state.sources.find( @@ -2897,73 +3085,74 @@ class ContactDetailScreen extends React.Component { value: contactSource.value, }; }) - : [] - } - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.selectSources'), - }} - renderRow={(id, onPress, item) => ( - ( + + - - - {item.name} - - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - const nonExistingSourcesList = [...this.state.nonExistingSources]; - let foundNonExistingSource = nonExistingSourcesList.findIndex( - (source) => source.value === id, - ); - if (foundNonExistingSource > -1) { - // Remove custom source from select list - const sourceList = [...this.state.sources]; //, - let foundSourceIndex = sourceList.findIndex( - (source) => source.value === id, - ); - sourceList.splice(foundSourceIndex, 1); - this.setState({ - sources: [...sourceList], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - )} + color: 'rgba(0, 0, 0, 0.87)', + fontSize: 14, + lineHeight: 21, + }}> + {item.name} + + + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + const nonExistingSourcesList = [...this.state.nonExistingSources]; + let foundNonExistingSource = nonExistingSourcesList.findIndex( + (source) => source.value === id, + ); + if (foundNonExistingSource > -1) { + // Remove custom source from select list + const sourceList = [...this.state.sources]; //, + let foundSourceIndex = sourceList.findIndex( + (source) => source.value === id, + ); + sourceList.splice(foundSourceIndex, 1); + this.setState({ + sources: [...sourceList], + }); + } + onClose(props); + }} + text={item.name} + style={style} + /> + )} + filterOnKey="name" + inputContainerStyle={styles.selectizeField} + /> + + + + + )} ); progressView = () => ( + /*_viewable_*/ {this.state.onlyView ? ( @@ -2974,8 +3163,8 @@ class ContactDetailScreen extends React.Component { onRefresh={() => this.onRefresh(this.state.contact.ID)} /> }> - - + + {this.state.contact.seeker_path ? this.props.contactSettings.fields.seeker_path.values[ - this.state.contact.seeker_path - ].label + this.state.contact.seeker_path + ].label : ''} @@ -3015,14 +3204,21 @@ class ContactDetailScreen extends React.Component { /> - + + + + + + + + {this.renderfaithMilestones()} {this.renderCustomFaithMilestones()} @@ -3034,7 +3230,9 @@ class ContactDetailScreen extends React.Component { {this.state.contact.baptism_date - ? moment(new Date(this.state.contact.baptism_date * 1000)).format('LL') + ? moment(new Date(this.state.contact.baptism_date * 1000)) + .utc() + .format('LL') : ''} @@ -3049,90 +3247,100 @@ class ContactDetailScreen extends React.Component { ) : ( - - - - - - - - - - - - - - - - - - - - - {Object.keys(this.props.contactSettings.fields.seeker_path.values).map((key) => { - const optionData = this.props.contactSettings.fields.seeker_path.values[key]; - return ; - })} - - - - - {this.renderfaithMilestones()} - {this.renderCustomFaithMilestones()} - - - - - - - - - - - - - - - - - - + + + + + - - - - - )} + + + + + + + + + + + + + + + {Object.keys(this.props.contactSettings.fields.seeker_path.values).map((key) => { + const optionData = this.props.contactSettings.fields.seeker_path.values[key]; + return ; + })} + + + + + + + + + + + + {this.renderfaithMilestones()} + {this.renderCustomFaithMilestones()} + + + + + + + + + + + + + + + + + + + + + + + )} ); @@ -3177,78 +3385,63 @@ class ContactDetailScreen extends React.Component { } commentsView = () => ( + /*_viewable_*/ - {this.state.comments.length == 0 && - this.state.activities.length == 0 && - !this.state.loadComments && - !this.state.loadActivities && - this.noCommentsRender()} - { - commentsFlatList = flatList; - }} - data={this.getCommentsAndActivities()} - extraData={!this.state.loadingMoreComments || !this.state.loadingMoreActivities} - inverted - ItemSeparatorComponent={() => ( - - )} - keyExtractor={(item, index) => String(index)} - renderItem={(item) => { - const commentOrActivity = item.item; - return this.renderActivityOrCommentRow(commentOrActivity); - }} - refreshControl={ - this.onRefreshCommentsActivities(this.state.contact.ID)} - /> - } - onScroll={({ nativeEvent }) => { - const { loadingMoreComments, commentsOffset, activitiesOffset } = this.state; - const flatList = nativeEvent; - const contentOffsetY = flatList.contentOffset.y; - const layoutMeasurementHeight = flatList.layoutMeasurement.height; - const contentSizeHeight = flatList.contentSize.height; - const heightOffsetSum = layoutMeasurementHeight + contentOffsetY; - const distanceToStart = contentSizeHeight - heightOffsetSum; - - if (distanceToStart < 100) { - if (!loadingMoreComments) { - if (commentsOffset < this.state.totalComments) { - this.setState( - { - loadingMoreComments: true, - }, - () => { - this.getContactComments(this.state.contact.ID); - }, - ); - } - } - if (!this.state.loadingMoreActivities) { - if (activitiesOffset < this.state.totalActivities) { - this.setState( - { - loadingMoreActivities: true, - }, - () => { - this.getContactActivities(this.state.contact.ID); - }, - ); - } - } + {this.state.comments.data.length == 0 && + this.state.activities.data.length == 0 && + !this.state.loadComments && + !this.state.loadActivities ? ( + this.noCommentsRender() + ) : ( + { + commentsFlatList = flatList; + }} + data={this.getCommentsAndActivities()} + extraData={!this.state.loadingMoreComments || !this.state.loadingMoreActivities} + inverted + ItemSeparatorComponent={() => ( + + )} + keyExtractor={(item, index) => String(index)} + renderItem={(item) => { + const commentOrActivity = item.item; + return this.renderActivityOrCommentRow(commentOrActivity); + }} + refreshControl={ + this.onRefreshCommentsActivities(this.state.contact.ID, true)} + /> } - }} - /> + onScroll={({ nativeEvent }) => { + sharedTools.onlyExecuteLastCall( + {}, + () => { + const flatList = nativeEvent; + const contentOffsetY = flatList.contentOffset.y; + const layoutMeasurementHeight = flatList.layoutMeasurement.height; + const contentSizeHeight = flatList.contentSize.height; + const heightOffsetSum = layoutMeasurementHeight + contentOffsetY; + const distanceToStart = contentSizeHeight - heightOffsetSum; + if (distanceToStart < 100) { + this.getContactComments(this.state.contact.ID); + this.getContactActivities(this.state.contact.ID); + } + }, + 500, + ); + }} + /> + )} ( + /*_viewable_*/ {this.state.onlyView ? ( @@ -3325,7 +3519,7 @@ class ContactDetailScreen extends React.Component { onRefresh={() => this.onRefresh(this.state.contact.ID)} /> }> - + - + {this.state.contact.groups ? ( - this.state.contact.groups.values.map((group, index) => + this.state.contact.groups.values.map((group, index) => ( this.goToGroupDetailScreen(group.value)}> - + onPress={() => this.goToGroupDetailScreen(group.value, group.name)}> + {group.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3364,27 +3560,33 @@ class ContactDetailScreen extends React.Component { - + - + {this.state.contact.relation ? ( - this.state.contact.relation.values.map((contact, index) => + this.state.contact.relation.values.map((contact, index) => ( this.goToContactDetailScreen(contact.value)}> - + onPress={() => this.goToContactDetailScreen(contact.value, contact.name)}> + {contact.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3399,24 +3601,26 @@ class ContactDetailScreen extends React.Component { - + {this.state.contact.baptized_by ? ( - this.state.contact.baptized_by.values.map((contact, index) => + this.state.contact.baptized_by.values.map((contact, index) => ( this.goToContactDetailScreen(contact.value)}> - + onPress={() => this.goToContactDetailScreen(contact.value, contact.name)}> + {contact.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3435,24 +3639,26 @@ class ContactDetailScreen extends React.Component { /> - + {this.state.contact.baptized ? ( - this.state.contact.baptized.values.map((contact, index) => + this.state.contact.baptized.values.map((contact, index) => ( this.goToContactDetailScreen(contact.value)}> - + onPress={() => this.goToContactDetailScreen(contact.value, contact.name)}> + {contact.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3471,24 +3677,26 @@ class ContactDetailScreen extends React.Component { /> - + {this.state.contact.coached_by ? ( - this.state.contact.coached_by.values.map((contact, index) => + this.state.contact.coached_by.values.map((contact, index) => ( this.goToContactDetailScreen(contact.value)}> - + onPress={() => this.goToContactDetailScreen(contact.value, contact.name)}> + {contact.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3507,24 +3715,26 @@ class ContactDetailScreen extends React.Component { /> - + {this.state.contact.coaching ? ( - this.state.contact.coaching.values.map((contact, index) => + this.state.contact.coaching.values.map((contact, index) => ( this.goToContactDetailScreen(contact.value)}> - + onPress={() => this.goToContactDetailScreen(contact.value, contact.name)}> + {contact.name} - )) : ( - - )} + )) + ) : ( + + )} @@ -3538,598 +3748,598 @@ class ContactDetailScreen extends React.Component { ) : ( - - - - - - - - - - - - - - - - - - - - { - groupsSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.connectionGroups, ...this.state.groups]} - selectedItems={this.getSelectizeItems( - this.state.contact.groups, - [...this.state.connectionGroups, ...this.state.groups], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addGroup'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + { + groupsSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.connectionGroups, ...this.state.groups]} + selectedItems={this.getSelectizeItems(this.state.contact.groups, [ + ...this.state.connectionGroups, + ...this.state.groups, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addGroup'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundGroupIndex = this.state.connectionGroups.findIndex( - (groupConnection) => groupConnection.value === id - ); - if (foundGroupIndex > -1) { - // Remove group from list - const connectionGroups = [...this.state.connectionGroups]; - connectionGroups.splice(foundGroupIndex, 1); - this.setState({ - connectionGroups: [...connectionGroups], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - + + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundGroupIndex = this.state.connectionGroups.findIndex( + (groupConnection) => groupConnection.value === id, + ); + if (foundGroupIndex > -1) { + // Remove group from list + const connectionGroups = [...this.state.connectionGroups]; + connectionGroups.splice(foundGroupIndex, 1); + this.setState({ + connectionGroups: [...connectionGroups], + }); + } + onClose(props); + }} + text={item.name} + style={style} /> - - - - { - connectionsSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.relationContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.relation, - [...this.state.relationContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addConnection'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + + { + connectionsSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.relationContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.relation, [ + ...this.state.relationContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addConnection'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundRelationIndex = this.state.relationContacts.findIndex( - (relation) => relation.value === id - ); - if (foundRelationIndex > -1) { - // Remove relation from list - const relationContacts = [...this.state.relationContacts]; - relationContacts.splice(foundRelationIndex, 1); - this.setState({ - relationContacts: [...relationContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundRelationIndex = this.state.relationContacts.findIndex( + (relation) => relation.value === id, + ); + if (foundRelationIndex > -1) { + // Remove relation from list + const relationContacts = [...this.state.relationContacts]; + relationContacts.splice(foundRelationIndex, 1); + this.setState({ + relationContacts: [...relationContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} /> - - - - { - baptizedBySelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.baptizedByContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.baptized_by, - [...this.state.baptizedByContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addBaptizedBy'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + + { + baptizedBySelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.baptizedByContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.baptized_by, [ + ...this.state.baptizedByContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addBaptizedBy'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundBaptizedByIndex = this.state.baptizedByContacts.findIndex( - (baptized) => baptized.value === id - ); - if (foundBaptizedByIndex > -1) { - // Remove baptized from list - const baptizedByContacts = [...this.state.baptizedByContacts]; - baptizedByContacts.splice(foundBaptizedByIndex, 1); - this.setState({ - baptizedByContacts: [...baptizedByContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundBaptizedByIndex = this.state.baptizedByContacts.findIndex( + (baptized) => baptized.value === id, + ); + if (foundBaptizedByIndex > -1) { + // Remove baptized from list + const baptizedByContacts = [...this.state.baptizedByContacts]; + baptizedByContacts.splice(foundBaptizedByIndex, 1); + this.setState({ + baptizedByContacts: [...baptizedByContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} /> - - - - { - baptizedSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.baptizedContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.baptized, - [...this.state.baptizedContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addBaptized'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + + { + baptizedSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.baptizedContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.baptized, [ + ...this.state.baptizedContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addBaptized'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundBaptizedIndex = this.state.baptizedContacts.findIndex( - (baptized) => baptized.value === id - ); - if (foundBaptizedIndex > -1) { - // Remove baptized from list - const baptizedContacts = [...this.state.baptizedContacts]; - baptizedContacts.splice(foundBaptizedIndex, 1); - this.setState({ - baptizedContacts: [...baptizedContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundBaptizedIndex = this.state.baptizedContacts.findIndex( + (baptized) => baptized.value === id, + ); + if (foundBaptizedIndex > -1) { + // Remove baptized from list + const baptizedContacts = [...this.state.baptizedContacts]; + baptizedContacts.splice(foundBaptizedIndex, 1); + this.setState({ + baptizedContacts: [...baptizedContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} /> - - - - { - coachedSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.coachedByContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.coached_by, - [...this.state.coachedByContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addCoachedBy'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + + { + coachedSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.coachedByContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.coached_by, [ + ...this.state.coachedByContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addCoachedBy'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundCoachedByIndex = this.state.coachedByContacts.findIndex( - (coachedBy) => coachedBy.value === id - ); - if (foundCoachedByIndex > -1) { - // Remove coachedBy from list - const coachedByContacts = [...this.state.coachedByContacts]; - coachedByContacts.splice(foundCoachedByIndex, 1); - this.setState({ - coachedByContacts: [...coachedByContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - - - - - - - - - - - - + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundCoachedByIndex = this.state.coachedByContacts.findIndex( + (coachedBy) => coachedBy.value === id, + ); + if (foundCoachedByIndex > -1) { + // Remove coachedBy from list + const coachedByContacts = [...this.state.coachedByContacts]; + coachedByContacts.splice(foundCoachedByIndex, 1); + this.setState({ + coachedByContacts: [...coachedByContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} /> - - - - { - coachingSelectizeRef = selectize; - }} - itemId="value" - items={[...this.state.coachedContacts, ...this.state.usersContacts]} - selectedItems={this.getSelectizeItems( - this.state.contact.coaching, - [...this.state.coachedContacts, ...this.state.usersContacts], - )} - textInputProps={{ - placeholder: i18n.t('contactDetailScreen.addCoaching'), - }} - renderRow={(id, onPress, item) => ( - + + + + + + + + + + + + + + + + + + + + { + coachingSelectizeRef = selectize; + }} + itemId="value" + items={[...this.state.coachedContacts, ...this.state.usersContacts]} + selectedItems={this.getSelectizeItems(this.state.contact.coaching, [ + ...this.state.coachedContacts, + ...this.state.usersContacts, + ])} + textInputProps={{ + placeholder: i18n.t('contactDetailScreen.addCoaching'), + }} + renderRow={(id, onPress, item) => ( + + - - - {item.name} - - - {' '} + {item.name} + + + {' '} (# {id}) - - - )} - renderChip={(id, onClose, item, style, iconStyle) => ( - { - let foundCoachedIndex = this.state.coachedContacts.findIndex( - (coached) => coached.value === id - ); - if (foundCoachedIndex > -1) { - // Remove coached from list - const coachedContacts = [...this.state.coachedContacts]; - coachedContacts.splice(foundCoachedIndex, 1); - this.setState({ - coachedContacts: [...coachedContacts], - }); - } - onClose(props); - }} - text={item.name} - style={style} - /> - )} - filterOnKey="name" - inputContainerStyle={styles.selectizeField} - /> - - - - - )} + + + )} + renderChip={(id, onClose, item, style, iconStyle) => ( + { + let foundCoachedIndex = this.state.coachedContacts.findIndex( + (coached) => coached.value === id, + ); + if (foundCoachedIndex > -1) { + // Remove coached from list + const coachedContacts = [...this.state.coachedContacts]; + coachedContacts.splice(foundCoachedIndex, 1); + this.setState({ + coachedContacts: [...coachedContacts], + }); + } + onClose(props); + }} + text={item.name} + style={style} + /> + )} + filterOnKey="name" + inputContainerStyle={styles.selectizeField} + /> + + + + + )} ); @@ -4264,6 +4474,16 @@ class ContactDetailScreen extends React.Component { return transformedContact; }; + openCommentDialog = (comment, deleteComment = false) => { + this.setState({ + commentDialog: { + toggle: true, + data: comment, + delete: deleteComment, + }, + }); + }; + renderActivityOrCommentRow = (commentOrActivity) => ( + { + // Comment and its their own comment + Object.prototype.hasOwnProperty.call(commentOrActivity, 'content') && + commentOrActivity.author.toLowerCase() === + this.props.userData.username.toLowerCase() && ( + + + { + this.openCommentDialog(commentOrActivity, true); + }}> + + + {i18n.t('global.delete')} + + + { + this.openCommentDialog(commentOrActivity); + }}> + + + {i18n.t('global.edit')} + + + + + ) + } ); @@ -4394,7 +4675,6 @@ class ContactDetailScreen extends React.Component { ...prevState.tabViewConfig, index, }, - renderFab: !(index === 2), })); }; @@ -4594,11 +4874,11 @@ class ContactDetailScreen extends React.Component { key: value, label: [...this.state.users, ...this.state.assignedToContacts].find( (user) => user.key === value, - ).label + ).label, }, }, showAssignedToModal: false, - assignedToContacts: [] // Clear non existing assigentToContacts list + assignedToContacts: [], // Clear non existing assigentToContacts list })); }; @@ -4608,19 +4888,60 @@ class ContactDetailScreen extends React.Component { }); }; - showAssignedUser = () => { - const foundUser = [...this.state.users, ...this.state.assignedToContacts].find( - (user) => user.key === this.state.contact.assigned_to.key, - ); - return ( - - {foundUser.label} - + renderContactLink = (assignedTo) => { + let foundContact, valueToSearch, nameToShow; + if (assignedTo.key) { + valueToSearch = assignedTo.key; + nameToShow = assignedTo.label; + } else if (assignedTo.value) { + valueToSearch = assignedTo.value; + nameToShow = assignedTo.name; + } + foundContact = this.state.users.find( + (user) => user.key === parseInt(valueToSearch) || user.contactID === parseInt(valueToSearch), ); + if (!foundContact) { + foundContact = this.state.usersContacts.find( + (user) => user.value === valueToSearch.toString(), + ); + } + // User have accesss to this assigned_to user/contact + if (foundContact && foundContact.contactID) { + // Contact exist in 'this.state.users' list + return ( + this.goToContactDetailScreen(foundContact.contactID, nameToShow)}> + + {nameToShow} + + + ); + } else if (foundContact) { + // Contact exist in 'this.state.usersContacts' list + return ( + this.goToContactDetailScreen(valueToSearch, nameToShow)}> + + {nameToShow} + + + ); + } else { + // User does not exist in any list + return ( + + {nameToShow} + + ); + } }; socialMediaKeyIsDB = (key) => key; @@ -4711,6 +5032,36 @@ class ContactDetailScreen extends React.Component { })); }; + onCloseCommentDialog() { + this.setState({ + commentDialog: { + toggle: false, + data: {}, + delete: false, + }, + }); + } + + onUpdateComment(commentData) { + this.props.saveComment( + this.props.userData.domain, + this.props.userData.token, + this.state.contact.ID, + commentData, + ); + this.onCloseCommentDialog(); + } + + onDeleteComment(commentData) { + this.props.deleteComment( + this.props.userData.domain, + this.props.userData.token, + this.state.contact.ID, + commentData.ID, + ); + this.onCloseCommentDialog(); + } + renderfaithMilestones() { return ( { this.onRemoveSocialMediaField(propertyName, socialMediaIndex, this); }} @@ -5204,23 +5555,35 @@ class ContactDetailScreen extends React.Component { this.props.searchLocations(this.props.userData.domain, this.props.userData.token, queryText); }; - onMeetingComplete = (props) => { - /* - if (true) { - this.props.navigation.navigate(NavigationActions.navigate({ - routeName: 'Questionnaire', - action: NavigationActions.navigate({ - routeName: 'Question', params: { - userData: this.state.userData, - contact: this.state.contact, - }, - }) - })); - } else { - this.onSaveQuickAction('quick_button_meeting_complete'); - } - */ + onMeetingComplete = () => { this.onSaveQuickAction('quick_button_meeting_complete'); + var isQuestionnaireEnabled = false; + var q_id = null; + // loop thru all (active) questionnaires, and check whether 'contact'->'meeting_complete' is enabled + this.props.questionnaires.map((questionnaire) => { + if ( + questionnaire.trigger_type == 'contact' && + questionnaire.trigger_value == 'meeting_complete' + ) { + isQuestionnaireEnabled = true; + q_id = questionnaire.id; + } + }); + if (isQuestionnaireEnabled) { + this.props.navigation.navigate( + NavigationActions.navigate({ + routeName: 'Questionnaire', + action: NavigationActions.navigate({ + routeName: 'Question', + params: { + userData: this.props.userData, + contact: this.state.contact, + q_id, + }, + }), + }), + ); + } }; onSaveQuickAction = (quickActionPropertyName) => { @@ -5335,7 +5698,7 @@ class ContactDetailScreen extends React.Component { onIndexChange={this.tabChanged} initialLayout={{ width: windowWidth }} /> - {this.state.renderFab && ( + {this.state.onlyView && this.state.tabViewConfig.index != 2 && ( @@ -5346,31 +5709,30 @@ class ContactDetailScreen extends React.Component { style={{ color: 'white', fontSize: 22 }} /> ) : ( - - ) + + ) } degrees={0} activeOpacity={0} bgColor="rgba(0,0,0,0.5)" nativeFeedbackRippleColor="rgba(0,0,0,0)"> { this.onSaveQuickAction('quick_button_no_answer'); }} size={40} + buttonColor={Colors.colorNo} nativeFeedbackRippleColor="rgba(0,0,0,0)" textStyle={{ color: Colors.tintColor, fontSize: 15 }} textContainerStyle={{ height: 'auto' }}> @@ -5388,13 +5751,13 @@ class ContactDetailScreen extends React.Component { /> { this.onSaveQuickAction('quick_button_meeting_scheduled'); }} + buttonColor={Colors.colorWait} size={40} nativeFeedbackRippleColor="rgba(0,0,0,0)" textStyle={{ color: Colors.tintColor, fontSize: 15 }} @@ -5406,12 +5769,12 @@ class ContactDetailScreen extends React.Component { /> { - this.onMeetingComplete(this.props); + this.onMeetingComplete(); }} size={40} + buttonColor={Colors.colorYes} nativeFeedbackRippleColor="rgba(0,0,0,0)" textStyle={{ color: Colors.tintColor, fontSize: 15 }} textContainerStyle={{ height: 'auto' }}> @@ -5422,12 +5785,12 @@ class ContactDetailScreen extends React.Component { /> { this.onSaveQuickAction('quick_button_no_show'); }} size={40} + buttonColor={Colors.colorNo} nativeFeedbackRippleColor="rgba(0,0,0,0)" textStyle={{ color: Colors.tintColor, fontSize: 15 }} textContainerStyle={{ height: 'auto' }}> @@ -5439,468 +5802,642 @@ class ContactDetailScreen extends React.Component { )} + {this.state.commentDialog.toggle ? ( + + + + + + {this.state.commentDialog.delete ? ( + + + + + + + {this.state.commentDialog.data.content} + + + + ) : ( + + + + + + + { + this.setState((prevState) => ({ + commentDialog: { + ...prevState.commentDialog, + data: { + ...prevState.commentDialog.data, + content: value, + }, + }, + })); + }} + style={[ + styles.contactTextField, + { height: 'auto', minHeight: 50 }, + ]} + /> + + + + )} + + + + {this.state.commentDialog.delete ? ( + + ) : ( + + )} + + + + + + ) : null} ) : ( - - {!this.props.isConnected && this.offlineBarRender()} - - - - + {Object.keys(this.props.contactSettings.channels) + .filter( + (channelName) => + channelName !== 'phone' && + channelName !== 'email' && + channelName !== 'address', + ) + .map((channelName, channelNameIndex) => { + const propertyName = `contact_${channelName}`; + return ( + + {this.state.contact[propertyName] + ? this.state.contact[ propertyName ].map((socialMedia, socialMediaIndex) => !socialMedia.key ? this.renderSocialMediaField( - socialMediaIndex, - socialMedia, - propertyName, - channelName, - ) + socialMediaIndex, + socialMedia, + propertyName, + channelName, + ) : null, ) - : null} - - ); - })} - {Object.keys(this.props.contactSettings.channels) - .filter( - (channelName) => - channelName !== 'phone' && - channelName !== 'email' && - channelName !== 'address', - ) - .map((channelName, channelNameIndex) => { - const propertyName = `contact_${channelName}`; - return ( - - {this.state.contact[propertyName] - ? this.state.contact[ + : null} + + ); + })} + {Object.keys(this.props.contactSettings.channels) + .filter( + (channelName) => + channelName !== 'phone' && + channelName !== 'email' && + channelName !== 'address', + ) + .map((channelName, channelNameIndex) => { + const propertyName = `contact_${channelName}`; + return ( + + {this.state.contact[propertyName] + ? this.state.contact[ propertyName ].map((socialMedia, socialMediaIndex) => socialMedia.key && !socialMedia.delete ? this.renderSocialMediaField( - socialMediaIndex, - socialMedia, - propertyName, - channelName, - ) + socialMediaIndex, + socialMedia, + propertyName, + channelName, + ) : null, ) - : null} - - ); - })} - - - - - - - - - - - - - - - - - - - {Object.keys(this.props.contactSettings.fields.gender.values).map( - (key) => { - const optionData = this.props.contactSettings.fields.gender.values[ - key - ]; - return ; - }, - )} - - - - -