diff --git a/Tella.xcodeproj/project.pbxproj b/Tella.xcodeproj/project.pbxproj index e8f9cdfcb..175817280 100644 --- a/Tella.xcodeproj/project.pbxproj +++ b/Tella.xcodeproj/project.pbxproj @@ -26,8 +26,7 @@ 0E998553266E57FC0019FDF4 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E998552266E57FC0019FDF4 /* HeaderView.swift */; }; 0ECC7817267A533E00A2ACF2 /* PageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC7816267A533E00A2ACF2 /* PageViewCell.swift */; }; 0ECC7819267A53A700A2ACF2 /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC7818267A53A700A2ACF2 /* PageView.swift */; }; - 0ECC781B267A542300A2ACF2 /* Pages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC781A267A542300A2ACF2 /* Pages.swift */; }; - 0ECC781D267AADA500A2ACF2 /* PageViewCellNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC781C267AADA500A2ACF2 /* PageViewCellNotification.swift */; }; + 0ECC781B267A542300A2ACF2 /* UwaziPages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */; }; 1203CDDC298C5B9100D09073 /* ReportActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203CDDB298C5B9100D09073 /* ReportActionType.swift */; }; 1203CDDE298D7C1300D09073 /* FileToUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203CDDD298D7C1300D09073 /* FileToUpload.swift */; }; 1203CDE02991429200D09073 /* FileStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203CDDF2991429200D09073 /* FileStatus.swift */; }; @@ -188,6 +187,7 @@ 126E813B272CAFA300688B64 /* UINavigationControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E813A272CAFA300688B64 /* UINavigationControllerExtension.swift */; }; 126ECFEA27C57FA600ED5161 /* CameraState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126ECFE927C57FA600ED5161 /* CameraState.swift */; }; 126FE4D727A427E200AE4188 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FE4D627A427E200AE4188 /* Constants.swift */; }; + 126FEE0A2AC4993900B99298 /* ReportPages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FEE092AC4993900B99298 /* ReportPages.swift */; }; 1272F25D27C91ACD0054F2E2 /* ImportFileProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F25C27C91ACD0054F2E2 /* ImportFileProgress.swift */; }; 1272F25F27C91BD70054F2E2 /* ImportFilesFromCameraProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F25E27C91BD70054F2E2 /* ImportFilesFromCameraProgress.swift */; }; 1272F26127C91CD90054F2E2 /* ImportFilesProgressModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F26027C91CD90054F2E2 /* ImportFilesProgressModels.swift */; }; @@ -312,20 +312,62 @@ 12FAC0CF2799FF230068EC2B /* ImportProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC0CE2799FF230068EC2B /* ImportProgress.swift */; }; 12FDEDD5272AFE99005C17AC /* LocalizableHome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FDEDD4272AFE99005C17AC /* LocalizableHome.swift */; }; 12FFEFA528BE96E00081DDB2 /* AddServerURLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FFEFA428BE96E00081DDB2 /* AddServerURLView.swift */; }; + 15EE30FE2B5073E20033BBBB /* Pages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15EE30FD2B5073E20033BBBB /* Pages.swift */; }; 1E67B39223FC9067001F3D64 /* ImagePickerSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E67B39123FC9067001F3D64 /* ImagePickerSheet.swift */; }; 1EAE6BF524146C2100114244 /* QuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EAE6BF424146C2100114244 /* QuickLook.swift */; }; 379A13111EE0E1EB59813E7F /* libPods-TellaTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C4F744F7D3402BB64531DB91 /* libPods-TellaTests.a */; }; 89EDF57136D5C3761B8AB12D /* libPods-Tella.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AEB425D8CB3D5E80DA3F19E6 /* libPods-Tella.a */; }; CC0797BE2A8D46CB00AAE63F /* UnlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0797BD2A8D46CB00AAE63F /* UnlockView.swift */; }; + CC0D56ED2AC4B46F00AA5D81 /* DownloadedTemplatesVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0D56EC2AC4B46F00AA5D81 /* DownloadedTemplatesVM.swift */; }; + CC0D56EF2AC4B59B00AA5D81 /* AddTemplateVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0D56EE2AC4B59B00AA5D81 /* AddTemplateVM.swift */; }; + CC0F1DD42B47320F00720010 /* UwaziDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0F1DD32B47320F00720010 /* UwaziDatePicker.swift */; }; CC1313822A5CB0DB0057271C /* DeleteAfterFailOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1313812A5CB0DB0057271C /* DeleteAfterFailOption.swift */; }; CC1313842A5CB3590057271C /* DeleteAfterFailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1313832A5CB3590057271C /* DeleteAfterFailView.swift */; }; CC1313882A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1313872A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift */; }; CC13138A2A5DB68E0057271C /* BottomSheetTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1313892A5DB68E0057271C /* BottomSheetTitleView.swift */; }; CC13138C2A5DB79B0057271C /* BottomButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC13138B2A5DB79B0057271C /* BottomButtonsView.swift */; }; + CC1C734D2AF5439600D736F8 /* UwaziMultipartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1C734C2AF5439600D736F8 /* UwaziMultipartData.swift */; }; + CC43D9E12A72F8260030192E /* UwaziView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC43D9E02A72F8260030192E /* UwaziView.swift */; }; + CC4864E92AF94AF300293F62 /* SubmitEntityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC4864E82AF94AF300293F62 /* SubmitEntityView.swift */; }; + CC5CEB872AF037B600BF238B /* FileDropdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CEB862AF037B600BF238B /* FileDropdown.swift */; }; + CC5CEB892AF0551900BF238B /* UwaziAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */; }; + CC5DF8782B02B06A0068F622 /* UwaziServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5DF8772B02B06A0068F622 /* UwaziServer.swift */; }; CC9C01B829D223FC00FDF341 /* LocalizableReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9C01B729D223FC00FDF341 /* LocalizableReport.swift */; }; + CC9DEE5F2AE80ED400C8ED29 /* UwaziFileSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */; }; CCA66C9C29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA66C9B29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift */; }; + CCAE3A682AC217EC00A38C6D /* UwaziViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCAE3A672AC217EC00A38C6D /* UwaziViewModel.swift */; }; + CCAEA0522A97E038008190E0 /* CreateDraftHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCAEA0512A97E038008190E0 /* CreateDraftHeaderView.swift */; }; CCBC2F6929A93E4900888779 /* SettingCheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBC2F6829A93E4900888779 /* SettingCheckboxItem.swift */; }; CCBEE0482A8E9B780067884E /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBEE0472A8E9B780067884E /* TextView.swift */; }; + CCC1B4252AC759F70075B819 /* UwaziEntityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4242AC759F70075B819 /* UwaziEntityViewModel.swift */; }; + CCC1B4272AC75A380075B819 /* CreateEntityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4262AC75A380075B819 /* CreateEntityView.swift */; }; + CCC1B4292AC75A8E0075B819 /* RenderPropertyComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4282AC75A8E0075B819 /* RenderPropertyComponentView.swift */; }; + CCC1B42B2AC75AAE0075B819 /* GenericEntityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B42A2AC75AAE0075B819 /* GenericEntityWidget.swift */; }; + CCC1B42D2AC75AD20075B819 /* UwaziTextWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B42C2AC75AD20075B819 /* UwaziTextWidget.swift */; }; + CCC1B42F2AC75AF60075B819 /* UwaziDividerWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B42E2AC75AF60075B819 /* UwaziDividerWidget.swift */; }; + CCC1B4312AC75B120075B819 /* UwaziEntityButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4302AC75B120075B819 /* UwaziEntityButtonView.swift */; }; + CCC1B4332AC75B410075B819 /* UwaziEntityMandatoryTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4322AC75B410075B819 /* UwaziEntityMandatoryTextView.swift */; }; + CCC1B4352AC75B550075B819 /* UwaziEntitySubtitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4342AC75B550075B819 /* UwaziEntitySubtitleView.swift */; }; + CCC1B4372AC75B700075B819 /* UwaziEntityTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4362AC75B700075B819 /* UwaziEntityTitleView.swift */; }; + CCC1B4392AC75B8D0075B819 /* UwaziEntityParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B4382AC75B8D0075B819 /* UwaziEntityParser.swift */; }; + CCC1B43B2AC75BED0075B819 /* UwaziEntityParserProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B43A2AC75BED0075B819 /* UwaziEntityParserProtocol.swift */; }; + CCC1B43D2AC75C5D0075B819 /* UwaziConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1B43C2AC75C5D0075B819 /* UwaziConstants.swift */; }; + CCC60CD22B0D2C5B00A3DB8C /* UwaziEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC60CD12B0D2C5B00A3DB8C /* UwaziEmptyView.swift */; }; + CCD586C22A7AB28200014F87 /* TemplateListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586C12A7AB28200014F87 /* TemplateListView.swift */; }; + CCD586C72A7ABDE800014F87 /* TemplateCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586C62A7ABDE800014F87 /* TemplateCardView.swift */; }; + CCD586CD2A7AD94B00014F87 /* ConnectionCardDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586CC2A7AD94B00014F87 /* ConnectionCardDetail.swift */; }; + CCD586CF2A7AD9E400014F87 /* MoreButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586CE2A7AD9E400014F87 /* MoreButtonView.swift */; }; + CCD586D12A7C096C00014F87 /* AddTemplatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586D02A7C096B00014F87 /* AddTemplatesView.swift */; }; + CCD586D42A7C294800014F87 /* TemplateItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD586D32A7C294800014F87 /* TemplateItemView.swift */; }; + CCDC398A2AF2BCE400674735 /* MultipartRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDC39892AF2BCE400674735 /* MultipartRequest.swift */; }; + CCE875B32A96699000C576B9 /* LocalizableUwazi.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE875B22A96699000C576B9 /* LocalizableUwazi.swift */; }; + CCE8CCDD2AF41E410088B61D /* UwaziFileUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE8CCDC2AF41E410088B61D /* UwaziFileUtility.swift */; }; + CCE8CCDF2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */; }; + CCEAE5922AE6F6EE0069204A /* EntityCreationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCEAE5912AE6F6EE0069204A /* EntityCreationResponse.swift */; }; + CCEAE5942AE7188F0069204A /* UwaziSelectWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCEAE5932AE7188F0069204A /* UwaziSelectWidget.swift */; }; + CCF47F442ABE18D7000ABBBC /* TemplateActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF47F432ABE18D7000ABBBC /* TemplateActionType.swift */; }; + CCF8D5C22AE994BF000D27C5 /* SupportingFileWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8D5C12AE994BF000D27C5 /* SupportingFileWidget.swift */; }; + CCF8D5C42AE9AA08000D27C5 /* PrimaryDocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF8D5C32AE9AA08000D27C5 /* PrimaryDocuments.swift */; }; CE02ED7F24E42B3900014176 /* UserDefaultsProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE02ED7E24E42B3900014176 /* UserDefaultsProperty.swift */; }; D50727BF2625561400BBC370 /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50727BE2625561400BBC370 /* KeychainManager.swift */; }; D50727C42625567900BBC370 /* KeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50727C32625567900BBC370 /* KeychainTests.swift */; }; @@ -355,6 +397,47 @@ D7F55FC825348FA50028739E /* RecordState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F55FC725348FA50028739E /* RecordState.swift */; }; D7F55FCD25348FB80028739E /* AudioRecorderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F55FCC25348FB80028739E /* AudioRecorderManager.swift */; }; D7F55FDA253490BE0028739E /* RecordingAudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F55FD9253490BE0028739E /* RecordingAudioManager.swift */; }; + E108B75729FE79340045CCBE /* UwaziServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E108B75629FE79340045CCBE /* UwaziServerViewModel.swift */; }; + E11968B12A1F409D00BA7B56 /* UwaziLanguageDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11968B02A1F409D00BA7B56 /* UwaziLanguageDTO.swift */; }; + E131BA3B2A1B45A30095BC91 /* UwaziServerRepositories.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131BA3A2A1B45A30095BC91 /* UwaziServerRepositories.swift */; }; + E14E0CF629FAD68E004FC4CD /* UwaziSuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14E0CF529FAD68E004FC4CD /* UwaziSuccessView.swift */; }; + E14FD87A2A6AB22200BC7523 /* UwaziLocale.swift in Sources */ = {isa = PBXBuildFile; fileRef = E14FD8792A6AB22200BC7523 /* UwaziLocale.swift */; }; + E16D20CF2ABEAAB1001708CC /* TemplateItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16D20CE2ABEAAB1001708CC /* TemplateItemViewModel.swift */; }; + E16D20D12ABEB5C7001708CC /* TemplateCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16D20D02ABEB5C7001708CC /* TemplateCardViewModel.swift */; }; + E177363C2A83AE4800FE01C0 /* UwaziTemplateDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E177363B2A83AE4800FE01C0 /* UwaziTemplateDTO.swift */; }; + E177363E2A83CCE100FE01C0 /* UwaziSettingDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E177363D2A83CCE100FE01C0 /* UwaziSettingDTO.swift */; }; + E17736402A83FFA200FE01C0 /* UwaziDictionaryDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E177363F2A83FFA200FE01C0 /* UwaziDictionaryDTO.swift */; }; + E17736422A84034A00FE01C0 /* UwaziTranslationRowDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17736412A84034A00FE01C0 /* UwaziTranslationRowDTO.swift */; }; + E18BD4332AA0C39A00EE62E2 /* UwaziLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18BD4322AA0C39A00EE62E2 /* UwaziLanguage.swift */; }; + E1975EE72AB032B10060228F /* UwaziEntryPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1975EE62AB032B10060228F /* UwaziEntryPrompt.swift */; }; + E1CF5A252AB3477C00365036 /* UwaziDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF5A242AB3477C00365036 /* UwaziDatabase.swift */; }; + E1CF5A272AB3488E00365036 /* JSONDecoderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF5A262AB3488E00365036 /* JSONDecoderExtension.swift */; }; + E1CF5A2B2AB3533100365036 /* ServerDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF5A2A2AB3533100365036 /* ServerDatabase.swift */; }; + E1D48B852A1BB8EA00D36270 /* UwaziCheckURLDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D48B842A1BB8EA00D36270 /* UwaziCheckURLDTO.swift */; }; + E1E7C56F2AA9822F00DDB07E /* DeleteServerTexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C56E2AA9822F00DDB07E /* DeleteServerTexts.swift */; }; + E1E7C5712AAB2BAB00DDB07E /* UwaziLanguageRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5702AAB2BAB00DDB07E /* UwaziLanguageRow.swift */; }; + E1E7C5732AAB4DCC00DDB07E /* CollectedTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5722AAB4DCC00DDB07E /* CollectedTemplate.swift */; }; + E1E7C5772AAB512400DDB07E /* UwaziTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5762AAB512400DDB07E /* UwaziTemplate.swift */; }; + E1E7C5792AAB515100DDB07E /* UwaziTemplateRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5782AAB515100DDB07E /* UwaziTemplateRow.swift */; }; + E1E7C57B2AAB54BE00DDB07E /* UwaziCommonProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C57A2AAB54BE00DDB07E /* UwaziCommonProperty.swift */; }; + E1E7C57D2AAB553500DDB07E /* UwaziProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C57C2AAB553500DDB07E /* UwaziProperty.swift */; }; + E1E7C57F2AAB5DB500DDB07E /* UwaziSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C57E2AAB5DB500DDB07E /* UwaziSetting.swift */; }; + E1E7C5822AAB707C00DDB07E /* UwaziDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5812AAB707C00DDB07E /* UwaziDictionary.swift */; }; + E1E7C5842AAB70A500DDB07E /* UwaziDictionaryRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5832AAB70A500DDB07E /* UwaziDictionaryRow.swift */; }; + E1E7C5872AAB7EA200DDB07E /* UwaziTranslation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5862AAB7EA200DDB07E /* UwaziTranslation.swift */; }; + E1E7C5892AAB7ED700DDB07E /* UwaziTranslationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C5882AAB7ED700DDB07E /* UwaziTranslationRow.swift */; }; + E1E7C58B2AAB7F2000DDB07E /* UwaziTranslationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E7C58A2AAB7F2000DDB07E /* UwaziTranslationContext.swift */; }; + E1EAA0552AA74FEC00492078 /* UwaziLanguageContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EAA0542AA74FEC00492078 /* UwaziLanguageContext.swift */; }; + E1EAA0572AA7501800492078 /* UwaziCheckURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EAA0562AA7501800492078 /* UwaziCheckURL.swift */; }; + E1EEE29629E6796E009FE227 /* ServerSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE29529E6796E009FE227 /* ServerSelectionView.swift */; }; + E1EEE29A29EECF25009FE227 /* UwaziAddServerURLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE29929EECF25009FE227 /* UwaziAddServerURLView.swift */; }; + E1EEE29E29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE29D29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift */; }; + E1EEE2A129F6EA2B009FE227 /* UwaziLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE2A029F6EA2B009FE227 /* UwaziLoginView.swift */; }; + E1EEE2A429F7D192009FE227 /* UwaziTwoStepVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE2A329F7D192009FE227 /* UwaziTwoStepVerification.swift */; }; + E1EEE2A729F7E1A8009FE227 /* UwaziLanguageSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EEE2A629F7E1A8009FE227 /* UwaziLanguageSelectionView.swift */; }; + E1F4A8E22AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F4A8E12AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift */; }; + E1F4A8E42AAF3F80001C8E61 /* UwaziTemplateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F4A8E32AAF3F80001C8E61 /* UwaziTemplateProtocol.swift */; }; + E1F6D7832AB17FB5000A9B64 /* UwaziValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F6D7822AB17FB5000A9B64 /* UwaziValue.swift */; }; F66E65B323E32080000F93E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F66E65B223E32080000F93E5 /* Assets.xcassets */; }; F66E65B923E32080000F93E5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F66E65B723E32080000F93E5 /* LaunchScreen.storyboard */; }; F66E65CF23E32080000F93E5 /* TellaUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F66E65CE23E32080000F93E5 /* TellaUITests.swift */; }; @@ -422,8 +505,7 @@ 0E998552266E57FC0019FDF4 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; 0ECC7816267A533E00A2ACF2 /* PageViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageViewCell.swift; sourceTree = ""; }; 0ECC7818267A53A700A2ACF2 /* PageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = ""; }; - 0ECC781A267A542300A2ACF2 /* Pages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pages.swift; sourceTree = ""; }; - 0ECC781C267AADA500A2ACF2 /* PageViewCellNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageViewCellNotification.swift; sourceTree = ""; }; + 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziPages.swift; sourceTree = ""; }; 0F3DC3E42523951C0024B4A0 /* BundleInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleInfo.swift; sourceTree = ""; }; 1203CDDB298C5B9100D09073 /* ReportActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportActionType.swift; sourceTree = ""; }; 1203CDDD298D7C1300D09073 /* FileToUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileToUpload.swift; sourceTree = ""; }; @@ -580,6 +662,7 @@ 126E813A272CAFA300688B64 /* UINavigationControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UINavigationControllerExtension.swift; sourceTree = ""; }; 126ECFE927C57FA600ED5161 /* CameraState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraState.swift; sourceTree = ""; }; 126FE4D627A427E200AE4188 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 126FEE092AC4993900B99298 /* ReportPages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPages.swift; sourceTree = ""; }; 1270EA9E2A4B4B07000851BA /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; 1270EAA02A4B4B07000851BA /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Localizable.strings; sourceTree = ""; }; 1270EAA22A4B4B07000851BA /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Localizable.strings; sourceTree = ""; }; @@ -705,6 +788,7 @@ 12FAC0CE2799FF230068EC2B /* ImportProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportProgress.swift; sourceTree = ""; }; 12FDEDD4272AFE99005C17AC /* LocalizableHome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableHome.swift; sourceTree = ""; }; 12FFEFA428BE96E00081DDB2 /* AddServerURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddServerURLView.swift; sourceTree = ""; }; + 15EE30FD2B5073E20033BBBB /* Pages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pages.swift; sourceTree = ""; }; 1E67B39123FC9067001F3D64 /* ImagePickerSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePickerSheet.swift; sourceTree = ""; }; 1EAE6BF424146C2100114244 /* QuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLook.swift; sourceTree = ""; }; 21FEB79B83E10A1CBE4AA323 /* Pods-TellaTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TellaTests.debug.xcconfig"; path = "Target Support Files/Pods-TellaTests/Pods-TellaTests.debug.xcconfig"; sourceTree = ""; }; @@ -715,15 +799,56 @@ AEB425D8CB3D5E80DA3F19E6 /* libPods-Tella.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tella.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C4F744F7D3402BB64531DB91 /* libPods-TellaTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TellaTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; CC0797BD2A8D46CB00AAE63F /* UnlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnlockView.swift; sourceTree = ""; }; + CC0D56EC2AC4B46F00AA5D81 /* DownloadedTemplatesVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadedTemplatesVM.swift; sourceTree = ""; }; + CC0D56EE2AC4B59B00AA5D81 /* AddTemplateVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTemplateVM.swift; sourceTree = ""; }; + CC0F1DD32B47320F00720010 /* UwaziDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDatePicker.swift; sourceTree = ""; }; CC1313812A5CB0DB0057271C /* DeleteAfterFailOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAfterFailOption.swift; sourceTree = ""; }; CC1313832A5CB3590057271C /* DeleteAfterFailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAfterFailView.swift; sourceTree = ""; }; CC1313872A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAfterFailOptionsStatus.swift; sourceTree = ""; }; CC1313892A5DB68E0057271C /* BottomSheetTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetTitleView.swift; sourceTree = ""; }; CC13138B2A5DB79B0057271C /* BottomButtonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomButtonsView.swift; sourceTree = ""; }; + CC1C734C2AF5439600D736F8 /* UwaziMultipartData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziMultipartData.swift; sourceTree = ""; }; + CC43D9E02A72F8260030192E /* UwaziView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziView.swift; sourceTree = ""; }; + CC4864E82AF94AF300293F62 /* SubmitEntityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitEntityView.swift; sourceTree = ""; }; + CC5CEB862AF037B600BF238B /* FileDropdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDropdown.swift; sourceTree = ""; }; + CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziAttachment.swift; sourceTree = ""; }; + CC5DF8772B02B06A0068F622 /* UwaziServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServer.swift; sourceTree = ""; }; CC9C01B729D223FC00FDF341 /* LocalizableReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableReport.swift; sourceTree = ""; }; + CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziFileSelector.swift; sourceTree = ""; }; CCA66C9B29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationBottomSheet.swift; sourceTree = ""; }; + CCAE3A672AC217EC00A38C6D /* UwaziViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziViewModel.swift; sourceTree = ""; }; + CCAEA0512A97E038008190E0 /* CreateDraftHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDraftHeaderView.swift; sourceTree = ""; }; CCBC2F6829A93E4900888779 /* SettingCheckboxItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingCheckboxItem.swift; sourceTree = ""; }; CCBEE0472A8E9B780067884E /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; + CCC1B4242AC759F70075B819 /* UwaziEntityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityViewModel.swift; sourceTree = ""; }; + CCC1B4262AC75A380075B819 /* CreateEntityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateEntityView.swift; sourceTree = ""; }; + CCC1B4282AC75A8E0075B819 /* RenderPropertyComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderPropertyComponentView.swift; sourceTree = ""; }; + CCC1B42A2AC75AAE0075B819 /* GenericEntityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericEntityWidget.swift; sourceTree = ""; }; + CCC1B42C2AC75AD20075B819 /* UwaziTextWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTextWidget.swift; sourceTree = ""; }; + CCC1B42E2AC75AF60075B819 /* UwaziDividerWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDividerWidget.swift; sourceTree = ""; }; + CCC1B4302AC75B120075B819 /* UwaziEntityButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityButtonView.swift; sourceTree = ""; }; + CCC1B4322AC75B410075B819 /* UwaziEntityMandatoryTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityMandatoryTextView.swift; sourceTree = ""; }; + CCC1B4342AC75B550075B819 /* UwaziEntitySubtitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntitySubtitleView.swift; sourceTree = ""; }; + CCC1B4362AC75B700075B819 /* UwaziEntityTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityTitleView.swift; sourceTree = ""; }; + CCC1B4382AC75B8D0075B819 /* UwaziEntityParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityParser.swift; sourceTree = ""; }; + CCC1B43A2AC75BED0075B819 /* UwaziEntityParserProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityParserProtocol.swift; sourceTree = ""; }; + CCC1B43C2AC75C5D0075B819 /* UwaziConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziConstants.swift; sourceTree = ""; }; + CCC60CD12B0D2C5B00A3DB8C /* UwaziEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEmptyView.swift; sourceTree = ""; }; + CCD586C12A7AB28200014F87 /* TemplateListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateListView.swift; sourceTree = ""; }; + CCD586C62A7ABDE800014F87 /* TemplateCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateCardView.swift; sourceTree = ""; }; + CCD586CC2A7AD94B00014F87 /* ConnectionCardDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionCardDetail.swift; sourceTree = ""; }; + CCD586CE2A7AD9E400014F87 /* MoreButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreButtonView.swift; sourceTree = ""; }; + CCD586D02A7C096B00014F87 /* AddTemplatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTemplatesView.swift; sourceTree = ""; }; + CCD586D32A7C294800014F87 /* TemplateItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateItemView.swift; sourceTree = ""; }; + CCDC39892AF2BCE400674735 /* MultipartRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartRequest.swift; sourceTree = ""; }; + CCE875B22A96699000C576B9 /* LocalizableUwazi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableUwazi.swift; sourceTree = ""; }; + CCE8CCDC2AF41E410088B61D /* UwaziFileUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziFileUtility.swift; sourceTree = ""; }; + CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziAttachmentsActionItems.swift; sourceTree = ""; }; + CCEAE5912AE6F6EE0069204A /* EntityCreationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityCreationResponse.swift; sourceTree = ""; }; + CCEAE5932AE7188F0069204A /* UwaziSelectWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSelectWidget.swift; sourceTree = ""; }; + CCF47F432ABE18D7000ABBBC /* TemplateActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateActionType.swift; sourceTree = ""; }; + CCF8D5C12AE994BF000D27C5 /* SupportingFileWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportingFileWidget.swift; sourceTree = ""; }; + CCF8D5C32AE9AA08000D27C5 /* PrimaryDocuments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryDocuments.swift; sourceTree = ""; }; CE02ED7E24E42B3900014176 /* UserDefaultsProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsProperty.swift; sourceTree = ""; }; CE64FA282505542E00B03CEC /* AppViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewState.swift; sourceTree = ""; }; D47E68E3B1AECB3684E49829 /* Pods-Tella.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tella.release.xcconfig"; path = "Target Support Files/Pods-Tella/Pods-Tella.release.xcconfig"; sourceTree = ""; }; @@ -758,6 +883,47 @@ D7F55FC725348FA50028739E /* RecordState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordState.swift; sourceTree = ""; }; D7F55FCC25348FB80028739E /* AudioRecorderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorderManager.swift; sourceTree = ""; }; D7F55FD9253490BE0028739E /* RecordingAudioManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingAudioManager.swift; sourceTree = ""; }; + E108B75629FE79340045CCBE /* UwaziServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServerViewModel.swift; sourceTree = ""; }; + E11968B02A1F409D00BA7B56 /* UwaziLanguageDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLanguageDTO.swift; sourceTree = ""; }; + E131BA3A2A1B45A30095BC91 /* UwaziServerRepositories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServerRepositories.swift; sourceTree = ""; }; + E14E0CF529FAD68E004FC4CD /* UwaziSuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSuccessView.swift; sourceTree = ""; }; + E14FD8792A6AB22200BC7523 /* UwaziLocale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLocale.swift; sourceTree = ""; }; + E16D20CE2ABEAAB1001708CC /* TemplateItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateItemViewModel.swift; sourceTree = ""; }; + E16D20D02ABEB5C7001708CC /* TemplateCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateCardViewModel.swift; sourceTree = ""; }; + E177363B2A83AE4800FE01C0 /* UwaziTemplateDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTemplateDTO.swift; sourceTree = ""; }; + E177363D2A83CCE100FE01C0 /* UwaziSettingDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSettingDTO.swift; sourceTree = ""; }; + E177363F2A83FFA200FE01C0 /* UwaziDictionaryDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDictionaryDTO.swift; sourceTree = ""; }; + E17736412A84034A00FE01C0 /* UwaziTranslationRowDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTranslationRowDTO.swift; sourceTree = ""; }; + E18BD4322AA0C39A00EE62E2 /* UwaziLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLanguage.swift; sourceTree = ""; }; + E1975EE62AB032B10060228F /* UwaziEntryPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntryPrompt.swift; sourceTree = ""; }; + E1CF5A242AB3477C00365036 /* UwaziDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDatabase.swift; sourceTree = ""; }; + E1CF5A262AB3488E00365036 /* JSONDecoderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDecoderExtension.swift; sourceTree = ""; }; + E1CF5A2A2AB3533100365036 /* ServerDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDatabase.swift; sourceTree = ""; }; + E1D48B842A1BB8EA00D36270 /* UwaziCheckURLDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziCheckURLDTO.swift; sourceTree = ""; }; + E1E7C56E2AA9822F00DDB07E /* DeleteServerTexts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteServerTexts.swift; sourceTree = ""; }; + E1E7C5702AAB2BAB00DDB07E /* UwaziLanguageRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLanguageRow.swift; sourceTree = ""; }; + E1E7C5722AAB4DCC00DDB07E /* CollectedTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectedTemplate.swift; sourceTree = ""; }; + E1E7C5762AAB512400DDB07E /* UwaziTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTemplate.swift; sourceTree = ""; }; + E1E7C5782AAB515100DDB07E /* UwaziTemplateRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTemplateRow.swift; sourceTree = ""; }; + E1E7C57A2AAB54BE00DDB07E /* UwaziCommonProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziCommonProperty.swift; sourceTree = ""; }; + E1E7C57C2AAB553500DDB07E /* UwaziProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziProperty.swift; sourceTree = ""; }; + E1E7C57E2AAB5DB500DDB07E /* UwaziSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSetting.swift; sourceTree = ""; }; + E1E7C5812AAB707C00DDB07E /* UwaziDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDictionary.swift; sourceTree = ""; }; + E1E7C5832AAB70A500DDB07E /* UwaziDictionaryRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziDictionaryRow.swift; sourceTree = ""; }; + E1E7C5862AAB7EA200DDB07E /* UwaziTranslation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTranslation.swift; sourceTree = ""; }; + E1E7C5882AAB7ED700DDB07E /* UwaziTranslationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTranslationRow.swift; sourceTree = ""; }; + E1E7C58A2AAB7F2000DDB07E /* UwaziTranslationContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTranslationContext.swift; sourceTree = ""; }; + E1EAA0542AA74FEC00492078 /* UwaziLanguageContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLanguageContext.swift; sourceTree = ""; }; + E1EAA0562AA7501800492078 /* UwaziCheckURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziCheckURL.swift; sourceTree = ""; }; + E1EEE29529E6796E009FE227 /* ServerSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionView.swift; sourceTree = ""; }; + E1EEE29929EECF25009FE227 /* UwaziAddServerURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziAddServerURLView.swift; sourceTree = ""; }; + E1EEE29D29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServerAccessSelectionView.swift; sourceTree = ""; }; + E1EEE2A029F6EA2B009FE227 /* UwaziLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLoginView.swift; sourceTree = ""; }; + E1EEE2A329F7D192009FE227 /* UwaziTwoStepVerification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTwoStepVerification.swift; sourceTree = ""; }; + E1EEE2A629F7E1A8009FE227 /* UwaziLanguageSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziLanguageSelectionView.swift; sourceTree = ""; }; + E1F4A8E12AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServerLanguageProtocol.swift; sourceTree = ""; }; + E1F4A8E32AAF3F80001C8E61 /* UwaziTemplateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziTemplateProtocol.swift; sourceTree = ""; }; + E1F6D7822AB17FB5000A9B64 /* UwaziValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziValue.swift; sourceTree = ""; }; F16689EAD5E834B9D4331109 /* Pods-TellaTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TellaTests.release.xcconfig"; path = "Target Support Files/Pods-TellaTests/Pods-TellaTests.release.xcconfig"; sourceTree = ""; }; F66E65A923E3207D000F93E5 /* Tella.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tella.app; sourceTree = BUILT_PRODUCTS_DIR; }; F66E65B023E3207D000F93E5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -811,6 +977,7 @@ isa = PBXGroup; children = ( 0D4BC3C1267577F900DCDC30 /* ReportsView.swift */, + 126FEE082AC4992800B99298 /* Models */, 12794E70294CB4E000B13BBF /* ReportList */, 121C945C28FF3D6900570EB4 /* Draft */, 12199C322940936B0041BD38 /* Outbox */, @@ -880,6 +1047,7 @@ isa = PBXGroup; children = ( 0E998552266E57FC0019FDF4 /* HeaderView.swift */, + CCAEA0512A97E038008190E0 /* CreateDraftHeaderView.swift */, ); path = Widgets; sourceTree = ""; @@ -889,8 +1057,7 @@ children = ( 0ECC7816267A533E00A2ACF2 /* PageViewCell.swift */, 0ECC7818267A53A700A2ACF2 /* PageView.swift */, - 0ECC781A267A542300A2ACF2 /* Pages.swift */, - 0ECC781C267AADA500A2ACF2 /* PageViewCellNotification.swift */, + 15EE30FD2B5073E20033BBBB /* Pages.swift */, ); path = Tabs; sourceTree = ""; @@ -898,10 +1065,13 @@ 1203CDE429915C6600D09073 /* Database */ = { isa = PBXGroup; children = ( + 1203CDE62991682800D09073 /* Common */, + E1F4A8E02AAF3F4E001C8E61 /* Protocols */, 12351C1B28D220BA00F3CB7C /* TellaData.swift */, 125A9DFF28CE52C700C0C3C8 /* TellaDataBase.swift */, 125A89602A965CEF009347C3 /* Vault */, - 1203CDE62991682800D09073 /* Common */, + E1CF5A242AB3477C00365036 /* UwaziDatabase.swift */, + E1CF5A2A2AB3533100365036 /* ServerDatabase.swift */, ); path = Database; sourceTree = ""; @@ -948,6 +1118,7 @@ 1203CDEC2992C63C00D09073 /* APICall */ = { isa = PBXGroup; children = ( + CCDC39882AF2BCC600674735 /* Utils */, 1233007728E30C7900B8F7BA /* APIRequest.swift */, 12D7D11B28E3581E008121C8 /* URLRequest.swift */, 1203CDE82992C19D00D09073 /* JSONStringEncoder.swift */, @@ -987,6 +1158,13 @@ 121C5D7629422D6800A64123 /* DataModel.swift */, 1234179329CD2CFC004CFAEA /* URLSessionTaskResponse.swift */, 125AAD352953607D002194D6 /* UploadReportModels.swift */, + E1D48B842A1BB8EA00D36270 /* UwaziCheckURLDTO.swift */, + E11968B02A1F409D00BA7B56 /* UwaziLanguageDTO.swift */, + E177363B2A83AE4800FE01C0 /* UwaziTemplateDTO.swift */, + E177363D2A83CCE100FE01C0 /* UwaziSettingDTO.swift */, + E177363F2A83FFA200FE01C0 /* UwaziDictionaryDTO.swift */, + E17736412A84034A00FE01C0 /* UwaziTranslationRowDTO.swift */, + CCEAE5912AE6F6EE0069204A /* EntityCreationResponse.swift */, 1293F6762AD88A0400F6CFBD /* FeedbackDTO.swift */, ); path = Models; @@ -1058,13 +1236,6 @@ path = Password; sourceTree = ""; }; - 122DBE022A5C21C000D6561A /* New Group */ = { - isa = PBXGroup; - children = ( - ); - path = "New Group"; - sourceTree = ""; - }; 122E4D53291AD99A00562EB1 /* TellaButton */ = { isa = PBXGroup; children = ( @@ -1107,6 +1278,8 @@ 1231A45B294C76990057DA85 /* Entity */ = { isa = PBXGroup; children = ( + E18BD4312AA0C2EB00EE62E2 /* Uwazi */, + E1975EE52AB032350060228F /* UwaziEntity */, 1293F6782AD88B4F00F6CFBD /* Feedback */, 121C5D7829422DF400A64123 /* DomainModel.swift */, 1231A45C294C771E0057DA85 /* Report */, @@ -1135,6 +1308,7 @@ 0DC267A5267AED7C00E55AFA /* VaultFile.swift */, 128FD0B327F774FB00B24915 /* RecentFile.swift */, 12751EA92A13A22100FAD7C6 /* SettingsModel.swift */, + E14FD8792A6AB22200BC7523 /* UwaziLocale.swift */, 125A89592A965BE0009347C3 /* Vault */, ); path = Report; @@ -1250,6 +1424,7 @@ 122389B6293635790058593B /* SetExtension.swift */, 12283F9F295C6FB100580EFE /* DataExtension.swift */, 121BAB7E29A7E30600D7E847 /* URLRequestExtension.swift */, + E1CF5A262AB3488E00365036 /* JSONDecoderExtension.swift */, 125F9B9A2AD9B4770073748C /* DictionaryExtension.swift */, ); path = Extensions; @@ -1310,6 +1485,14 @@ path = FileTypeHelper; sourceTree = ""; }; + 126FEE082AC4992800B99298 /* Models */ = { + isa = PBXGroup; + children = ( + 126FEE092AC4993900B99298 /* ReportPages.swift */, + ); + path = Models; + sourceTree = ""; + }; 1272F25A27C919E40054F2E2 /* ImportFilesProgressView */ = { isa = PBXGroup; children = ( @@ -1410,6 +1593,8 @@ children = ( 12ADF98A28BD130C00FA96EB /* ServersListView.swift */, 120451F928C7525200D572A7 /* EditSettingsServerView.swift */, + E1EEE29529E6796E009FE227 /* ServerSelectionView.swift */, + E1EEE29229E671B8009FE227 /* Uwazi */, 121C945B28FF026200570EB4 /* AddServer */, ); path = Servers; @@ -1670,6 +1855,7 @@ 120451FF28C7AC2800D572A7 /* ServerActionType.swift */, CC1313812A5CB0DB0057271C /* DeleteAfterFailOption.swift */, CC1313872A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift */, + E1E7C56E2AA9822F00DDB07E /* DeleteServerTexts.swift */, ); path = Models; sourceTree = ""; @@ -1685,6 +1871,7 @@ 126AD14927C6D5EB00081CC9 /* LocalizableCamera.swift */, 12E8129927A1570A007DDDC0 /* LocalizableSettings.swift */, CC9C01B729D223FC00FDF341 /* LocalizableReport.swift */, + CCE875B22A96699000C576B9 /* LocalizableUwazi.swift */, ); path = Localizable; sourceTree = ""; @@ -1704,6 +1891,7 @@ children = ( 12D7D11E28E36123008121C8 /* ServerRepository.swift */, 121AD8212943A2F00056AC2A /* ReportRepository.swift */, + E131BA3A2A1B45A30095BC91 /* UwaziServerRepositories.swift */, 1293F6742AD8853500F6CFBD /* FeedbackRepository.swift */, ); path = Repositories; @@ -1712,6 +1900,7 @@ 12D7F5F52743F28700EBA3A2 /* Components */ = { isa = PBXGroup; children = ( + CCF47F3F2ABE11F3000ABBBC /* Connections */, 122E4D53291AD99A00562EB1 /* TellaButton */, 1272F25A27C919E40054F2E2 /* ImportFilesProgressView */, 0E99854E266E56A80019FDF4 /* Widgets */, @@ -1737,7 +1926,6 @@ 12234BDF29DAEC4A00D6F981 /* CustomNavigation.swift */, CCBEE0472A8E9B780067884E /* TextView.swift */, 1293F6672AD7EF3900F6CFBD /* CloseHeaderView.swift */, - 122DBE022A5C21C000D6561A /* New Group */, 12603DAB2A56C72900FB1492 /* ToastView.swift */, 122DBE002A5C1E0500D6561A /* Toast.swift */, ); @@ -1764,6 +1952,7 @@ 129B845028BF6C7800F1B344 /* ServersViewModel.swift */, 123CE84329126B52003C251F /* ServerViewModel.swift */, 12730E68279F420400DC0135 /* SettingsViewModel.swift */, + E108B75629FE79340045CCBE /* UwaziServerViewModel.swift */, 1293F6692AD7F29B00F6CFBD /* FeedbackViewModel.swift */, ); path = ViewModel; @@ -1849,6 +2038,106 @@ path = Pods; sourceTree = ""; }; + CC284CCC2AC76B0300DFD7D6 /* Entity */ = { + isa = PBXGroup; + children = ( + CCC1B4262AC75A380075B819 /* CreateEntityView.swift */, + CCC1B4282AC75A8E0075B819 /* RenderPropertyComponentView.swift */, + E1F4A8E52AAF431F001C8E61 /* Widgets */, + CC4864E82AF94AF300293F62 /* SubmitEntityView.swift */, + ); + path = Entity; + sourceTree = ""; + }; + CC387D5A2A7857C900853F2E /* ViewModel */ = { + isa = PBXGroup; + children = ( + E16D20CE2ABEAAB1001708CC /* TemplateItemViewModel.swift */, + E16D20D02ABEB5C7001708CC /* TemplateCardViewModel.swift */, + CCAE3A672AC217EC00A38C6D /* UwaziViewModel.swift */, + CC0D56EC2AC4B46F00AA5D81 /* DownloadedTemplatesVM.swift */, + CC0D56EE2AC4B59B00AA5D81 /* AddTemplateVM.swift */, + CCC1B4242AC759F70075B819 /* UwaziEntityViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + CC43D9DF2A72F7FB0030192E /* Uwazi */ = { + isa = PBXGroup; + children = ( + E1F6D77A2AB1666D000A9B64 /* Utils */, + CCF47F422ABE18B4000ABBBC /* Models */, + E10412112A9474D9003DCE6A /* Views */, + CC387D5A2A7857C900853F2E /* ViewModel */, + CC43D9E02A72F8260030192E /* UwaziView.swift */, + ); + path = Uwazi; + sourceTree = ""; + }; + CCC60CD02B0D2C3700A3DB8C /* Common */ = { + isa = PBXGroup; + children = ( + CCC60CD12B0D2C5B00A3DB8C /* UwaziEmptyView.swift */, + ); + path = Common; + sourceTree = ""; + }; + CCD586BE2A7AB0E300014F87 /* Template */ = { + isa = PBXGroup; + children = ( + CCD586D02A7C096B00014F87 /* AddTemplatesView.swift */, + CCD586C12A7AB28200014F87 /* TemplateListView.swift */, + CCD586C62A7ABDE800014F87 /* TemplateCardView.swift */, + CCD586D22A7C0EFA00014F87 /* Common */, + ); + path = Template; + sourceTree = ""; + }; + CCD586C92A7AD85400014F87 /* Card */ = { + isa = PBXGroup; + children = ( + CCD586CC2A7AD94B00014F87 /* ConnectionCardDetail.swift */, + CCD586CE2A7AD9E400014F87 /* MoreButtonView.swift */, + ); + path = Card; + sourceTree = ""; + }; + CCD586D22A7C0EFA00014F87 /* Common */ = { + isa = PBXGroup; + children = ( + CCD586D32A7C294800014F87 /* TemplateItemView.swift */, + ); + path = Common; + sourceTree = ""; + }; + CCDC39882AF2BCC600674735 /* Utils */ = { + isa = PBXGroup; + children = ( + CCDC39892AF2BCE400674735 /* MultipartRequest.swift */, + ); + path = Utils; + sourceTree = ""; + }; + CCF47F3F2ABE11F3000ABBBC /* Connections */ = { + isa = PBXGroup; + children = ( + CCD586C92A7AD85400014F87 /* Card */, + ); + path = Connections; + sourceTree = ""; + }; + CCF47F422ABE18B4000ABBBC /* Models */ = { + isa = PBXGroup; + children = ( + CCF47F432ABE18D7000ABBBC /* TemplateActionType.swift */, + 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */, + CCC1B43C2AC75C5D0075B819 /* UwaziConstants.swift */, + CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */, + CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */, + ); + path = Models; + sourceTree = ""; + }; D50FA6812613ABC900D6D63B /* VaultManager */ = { isa = PBXGroup; children = ( @@ -1861,6 +2150,7 @@ D53B76DA2601C9E50001CD34 /* Scenes */ = { isa = PBXGroup; children = ( + CC43D9DF2A72F7FB0030192E /* Uwazi */, D5D5696C262747E100C45416 /* MainView.swift */, 12781F4827EA28D600FE9609 /* ContentView */, 12567FED2716B24700A2D356 /* Authentication */, @@ -1928,6 +2218,197 @@ path = Audio; sourceTree = ""; }; + E10412112A9474D9003DCE6A /* Views */ = { + isa = PBXGroup; + children = ( + CCC60CD02B0D2C3700A3DB8C /* Common */, + CC284CCC2AC76B0300DFD7D6 /* Entity */, + CCD586BE2A7AB0E300014F87 /* Template */, + ); + path = Views; + sourceTree = ""; + }; + E14E0CF429F9ACA7004FC4CD /* Step6 */ = { + isa = PBXGroup; + children = ( + E14E0CF529FAD68E004FC4CD /* UwaziSuccessView.swift */, + ); + path = Step6; + sourceTree = ""; + }; + E18BD4312AA0C2EB00EE62E2 /* Uwazi */ = { + isa = PBXGroup; + children = ( + E1E7C5852AAB7E9000DDB07E /* Translation */, + E1E7C5802AAB623300DDB07E /* Dictionary */, + E1E7C5752AAB510A00DDB07E /* Template */, + E1E7C5742AAB50FB00DDB07E /* Language */, + E1EAA0562AA7501800492078 /* UwaziCheckURL.swift */, + E1E7C5722AAB4DCC00DDB07E /* CollectedTemplate.swift */, + CC5DF8772B02B06A0068F622 /* UwaziServer.swift */, + ); + path = Uwazi; + sourceTree = ""; + }; + E1975EE52AB032350060228F /* UwaziEntity */ = { + isa = PBXGroup; + children = ( + E1F6D7822AB17FB5000A9B64 /* UwaziValue.swift */, + E1975EE62AB032B10060228F /* UwaziEntryPrompt.swift */, + ); + path = UwaziEntity; + sourceTree = ""; + }; + E1E7C5742AAB50FB00DDB07E /* Language */ = { + isa = PBXGroup; + children = ( + E18BD4322AA0C39A00EE62E2 /* UwaziLanguage.swift */, + E1EAA0542AA74FEC00492078 /* UwaziLanguageContext.swift */, + E1E7C5702AAB2BAB00DDB07E /* UwaziLanguageRow.swift */, + ); + path = Language; + sourceTree = ""; + }; + E1E7C5752AAB510A00DDB07E /* Template */ = { + isa = PBXGroup; + children = ( + E1E7C5762AAB512400DDB07E /* UwaziTemplate.swift */, + E1E7C5782AAB515100DDB07E /* UwaziTemplateRow.swift */, + E1E7C57A2AAB54BE00DDB07E /* UwaziCommonProperty.swift */, + E1E7C57C2AAB553500DDB07E /* UwaziProperty.swift */, + E1E7C57E2AAB5DB500DDB07E /* UwaziSetting.swift */, + ); + path = Template; + sourceTree = ""; + }; + E1E7C5802AAB623300DDB07E /* Dictionary */ = { + isa = PBXGroup; + children = ( + E1E7C5812AAB707C00DDB07E /* UwaziDictionary.swift */, + E1E7C5832AAB70A500DDB07E /* UwaziDictionaryRow.swift */, + ); + path = Dictionary; + sourceTree = ""; + }; + E1E7C5852AAB7E9000DDB07E /* Translation */ = { + isa = PBXGroup; + children = ( + E1E7C5862AAB7EA200DDB07E /* UwaziTranslation.swift */, + E1E7C5882AAB7ED700DDB07E /* UwaziTranslationRow.swift */, + E1E7C58A2AAB7F2000DDB07E /* UwaziTranslationContext.swift */, + ); + path = Translation; + sourceTree = ""; + }; + E1EEE29229E671B8009FE227 /* Uwazi */ = { + isa = PBXGroup; + children = ( + E14E0CF429F9ACA7004FC4CD /* Step6 */, + E1EEE2A529F7E194009FE227 /* Step5 */, + E1EEE2A229F7D16C009FE227 /* Step4 */, + E1EEE29F29F6EA0F009FE227 /* Step3 */, + E1EEE29C29EEDA1B009FE227 /* Step2 */, + E1EEE29B29EED94C009FE227 /* Step1 */, + ); + path = Uwazi; + sourceTree = ""; + }; + E1EEE29B29EED94C009FE227 /* Step1 */ = { + isa = PBXGroup; + children = ( + E1EEE29929EECF25009FE227 /* UwaziAddServerURLView.swift */, + ); + path = Step1; + sourceTree = ""; + }; + E1EEE29C29EEDA1B009FE227 /* Step2 */ = { + isa = PBXGroup; + children = ( + E1EEE29D29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift */, + ); + path = Step2; + sourceTree = ""; + }; + E1EEE29F29F6EA0F009FE227 /* Step3 */ = { + isa = PBXGroup; + children = ( + E1EEE2A029F6EA2B009FE227 /* UwaziLoginView.swift */, + ); + path = Step3; + sourceTree = ""; + }; + E1EEE2A229F7D16C009FE227 /* Step4 */ = { + isa = PBXGroup; + children = ( + E1EEE2A329F7D192009FE227 /* UwaziTwoStepVerification.swift */, + ); + path = Step4; + sourceTree = ""; + }; + E1EEE2A529F7E194009FE227 /* Step5 */ = { + isa = PBXGroup; + children = ( + E1EEE2A629F7E1A8009FE227 /* UwaziLanguageSelectionView.swift */, + ); + path = Step5; + sourceTree = ""; + }; + E1F4A8E02AAF3F4E001C8E61 /* Protocols */ = { + isa = PBXGroup; + children = ( + E1F4A8E12AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift */, + E1F4A8E32AAF3F80001C8E61 /* UwaziTemplateProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + E1F4A8E52AAF431F001C8E61 /* Widgets */ = { + isa = PBXGroup; + children = ( + E1F4A8E82AAF7AD1001C8E61 /* Common */, + CCC1B42C2AC75AD20075B819 /* UwaziTextWidget.swift */, + CCC1B42A2AC75AAE0075B819 /* GenericEntityWidget.swift */, + CCEAE5932AE7188F0069204A /* UwaziSelectWidget.swift */, + CCF8D5C12AE994BF000D27C5 /* SupportingFileWidget.swift */, + CCF8D5C32AE9AA08000D27C5 /* PrimaryDocuments.swift */, + CC0F1DD32B47320F00720010 /* UwaziDatePicker.swift */, + ); + path = Widgets; + sourceTree = ""; + }; + E1F4A8E82AAF7AD1001C8E61 /* Common */ = { + isa = PBXGroup; + children = ( + CCC1B4362AC75B700075B819 /* UwaziEntityTitleView.swift */, + CCC1B4342AC75B550075B819 /* UwaziEntitySubtitleView.swift */, + CCC1B4322AC75B410075B819 /* UwaziEntityMandatoryTextView.swift */, + CCC1B4302AC75B120075B819 /* UwaziEntityButtonView.swift */, + CCC1B42E2AC75AF60075B819 /* UwaziDividerWidget.swift */, + CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */, + CC5CEB862AF037B600BF238B /* FileDropdown.swift */, + ); + path = Common; + sourceTree = ""; + }; + E1F6D77A2AB1666D000A9B64 /* Utils */ = { + isa = PBXGroup; + children = ( + E1F6D77D2AB16F70000A9B64 /* Protocol */, + CCC1B4382AC75B8D0075B819 /* UwaziEntityParser.swift */, + CCE8CCDC2AF41E410088B61D /* UwaziFileUtility.swift */, + CC1C734C2AF5439600D736F8 /* UwaziMultipartData.swift */, + ); + path = Utils; + sourceTree = ""; + }; + E1F6D77D2AB16F70000A9B64 /* Protocol */ = { + isa = PBXGroup; + children = ( + CCC1B43A2AC75BED0075B819 /* UwaziEntityParserProtocol.swift */, + ); + path = Protocol; + sourceTree = ""; + }; F66E65A023E3207D000F93E5 = { isa = PBXGroup; children = ( @@ -2088,7 +2569,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1110; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = HORIZONTAL; TargetAttributes = { D50FA67F2613ABC900D6D63B = { @@ -2262,19 +2743,24 @@ buildActionMask = 2147483647; files = ( CC13138A2A5DB68E0057271C /* BottomSheetTitleView.swift in Sources */, + E1E7C5842AAB70A500DDB07E /* UwaziDictionaryRow.swift in Sources */, 1240E6B02907E14400692232 /* ReportCardView.swift in Sources */, 12A1489A2720B53800A1ADDC /* ConfirmPasswordErrorView.swift in Sources */, 128B15CB27DA30C800E1C1E0 /* OnboardingEndView.swift in Sources */, 125D2328271F8B6600250FBB /* LockConfirmPinView.swift in Sources */, 125D231F271F12A000250FBB /* NSRegularExpressionExtension.swift in Sources */, + E1E7C5872AAB7EA200DDB07E /* UwaziTranslation.swift in Sources */, + CC0F1DD42B47320F00720010 /* UwaziDatePicker.swift in Sources */, 1209FD3027AD8AC400D8C17E /* AudioPlayerManager.swift in Sources */, 12BAFA7728B3B8D300930451 /* UIImageOrientation.swift in Sources */, + CCE875B32A96699000C576B9 /* LocalizableUwazi.swift in Sources */, 123CE84429126B52003C251F /* ServerViewModel.swift in Sources */, 122DA88F29CB1F21003801CD /* AutoUpload.swift in Sources */, CC1313882A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift in Sources */, 1293F67A2AD88B8400F6CFBD /* FeedbackAPI.swift in Sources */, D7F55FDA253490BE0028739E /* RecordingAudioManager.swift in Sources */, 0E78B3A82656B03600F9BDBC /* ConfirmBottomSheet.swift in Sources */, + E1E7C5772AAB512400DDB07E /* UwaziTemplate.swift in Sources */, 12776A9527BA761C00CDC5DC /* CustomCameraController.swift in Sources */, 12E333C328D0A8D300E1DDB7 /* CryptoManager.swift in Sources */, 120451FA28C7525200D572A7 /* EditSettingsServerView.swift in Sources */, @@ -2285,24 +2771,35 @@ 128D6E182804531B0082AB18 /* AddPhotoVideoItems.swift in Sources */, 12730E65279EAB9200DC0135 /* SettingsMainView.swift in Sources */, 12E0882427AD31F600CEB198 /* LocalizableAudio.swift in Sources */, + CC0D56EF2AC4B59B00AA5D81 /* AddTemplateVM.swift in Sources */, + CCF8D5C22AE994BF000D27C5 /* SupportingFileWidget.swift in Sources */, 12E6772F27A9719F00DC1E1C /* UIImageExtension.swift in Sources */, D5B823EB262774E00008E04B /* MainView.swift in Sources */, 1220A72E292EB11B000BC24C /* ReportFileGridView.swift in Sources */, 125FA6D6278DE49A004B9D06 /* URLExtension.swift in Sources */, 127369E327B3C4DA00CF7BE5 /* UIDeviceExtension.swift in Sources */, 12A148922720A32000A1ADDC /* LockPinData.swift in Sources */, + CCC1B43B2AC75BED0075B819 /* UwaziEntityParserProtocol.swift in Sources */, D57F5E9026152A4800C1DA47 /* Datable.swift in Sources */, CC1313822A5CB0DB0057271C /* DeleteAfterFailOption.swift in Sources */, 1203CDEB2992C1CB00D09073 /* HTTPCodes.swift in Sources */, 122389B7293635790058593B /* SetExtension.swift in Sources */, + CCD586CD2A7AD94B00014F87 /* ConnectionCardDetail.swift in Sources */, 12607D5E27904F5300E2B8CC /* PlayerView.swift in Sources */, 125AAD34295348EC002194D6 /* UploadService.swift in Sources */, 1237A71E278F5F8B00D7BC0F /* LanguageManager.swift in Sources */, + E1CF5A2B2AB3533100365036 /* ServerDatabase.swift in Sources */, + CCC1B4372AC75B700075B819 /* UwaziEntityTitleView.swift in Sources */, CC9C01B829D223FC00FDF341 /* LocalizableReport.swift in Sources */, D5DAFDCF265384C4002F3942 /* MainAppModel.swift in Sources */, + CCDC398A2AF2BCE400674735 /* MultipartRequest.swift in Sources */, + E108B75729FE79340045CCBE /* UwaziServerViewModel.swift in Sources */, 1293F66A2AD7F29B00F6CFBD /* FeedbackViewModel.swift in Sources */, 1289B98B27C53C2400315FCE /* CameraTypeItemView.swift in Sources */, 126AD14A27C6D5EB00081CC9 /* LocalizableCamera.swift in Sources */, + E1E7C58B2AAB7F2000DDB07E /* UwaziTranslationContext.swift in Sources */, + E1E7C5732AAB4DCC00DDB07E /* CollectedTemplate.swift in Sources */, + E1CF5A252AB3477C00365036 /* UwaziDatabase.swift in Sources */, 125680002717907900A2D356 /* LockPasswordView.swift in Sources */, 1224DAF2271A3393001C0ED0 /* AppViewState.swift in Sources */, D7F55FCD25348FB80028739E /* AudioRecorderManager.swift in Sources */, @@ -2320,11 +2817,13 @@ 1293F66C2AD80DB100F6CFBD /* CardFrameView.swift in Sources */, 12E9DD9B27DF7DA50002FD00 /* CreateNewFolderView.swift in Sources */, 12ADF98D28BD16A400FA96EB /* SettingsCardView.swift in Sources */, + E177363C2A83AE4800FE01C0 /* UwaziTemplateDTO.swift in Sources */, 125D231C271F119D00250FBB /* Validator.swift in Sources */, D51639802631130E003A11B5 /* HomeView.swift in Sources */, 1293F6702AD8295400F6CFBD /* BorderedTextEditorView.swift in Sources */, 129B845928BFED9700F1B344 /* TellaButtonView.swift in Sources */, 123048D7295F95BB0015CD96 /* SubmittedDetailsItemView.swift in Sources */, + E1CF5A272AB3488E00365036 /* JSONDecoderExtension.swift in Sources */, 1291F4F227B52BF9006A34D3 /* QueuePlayer.swift in Sources */, 1221CB7128DB1B5A0042F15F /* KeyValue.swift in Sources */, 1234179229CD19E6004CFAEA /* BaseUploadOperation.swift in Sources */, @@ -2337,6 +2836,7 @@ 1244141927E35D120037D469 /* VaultFileImages.swift in Sources */, 1289B98D27C53C8400315FCE /* ImageExtension.swift in Sources */, D538C8AC264CE8D300D457B0 /* CryptoFileManager.swift in Sources */, + E17736422A84034A00FE01C0 /* UwaziTranslationRowDTO.swift in Sources */, 12ADF98B28BD130C00FA96EB /* ServersListView.swift in Sources */, 12568002271790AF00A2D356 /* LockPinView.swift in Sources */, 1209FD3227ADBDDE00D8C17E /* TimeIntervalExtension.swift in Sources */, @@ -2350,13 +2850,17 @@ 12732F87273C219700BCE0FB /* LeadingTitleToolbar.swift in Sources */, 128B929927D796E800E92ACF /* FolderListView.swift in Sources */, 126E813B272CAFA300688B64 /* UINavigationControllerExtension.swift in Sources */, + E1EAA0572AA7501800492078 /* UwaziCheckURL.swift in Sources */, 0E64C6A6265E7625005A7934 /* ActionListBottomSheet.swift in Sources */, + CCC1B4352AC75B550075B819 /* UwaziEntitySubtitleView.swift in Sources */, D5077A40260D300600D72947 /* DebugLogging.swift in Sources */, 1234179429CD2CFC004CFAEA /* URLSessionTaskResponse.swift in Sources */, - 0ECC781D267AADA500A2ACF2 /* PageViewCellNotification.swift in Sources */, + E1F6D7832AB17FB5000A9B64 /* UwaziValue.swift in Sources */, + CCE8CCDF2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift in Sources */, 122DA89729CB2709003801CD /* SettingsBottomView.swift in Sources */, 1216124028DE144200E6D7B6 /* JoinCondition.swift in Sources */, 12AE8D1E278CD4900007C781 /* IntExtension.swift in Sources */, + E14FD87A2A6AB22200BC7523 /* UwaziLocale.swift in Sources */, 12730E67279F11C300DC0135 /* LanguageListView.swift in Sources */, 0ECC7817267A533E00A2ACF2 /* PageViewCell.swift in Sources */, 128B929727D7920300E92ACF /* SelectingFilesHeaderView.swift in Sources */, @@ -2373,7 +2877,9 @@ CCBC2F6929A93E4900888779 /* SettingCheckboxItem.swift in Sources */, 1293547B294A454900735ED0 /* AppDelegate.swift in Sources */, 12B52580284903AE00B3D1C0 /* AboutAndHelpItem.swift in Sources */, + CCC1B4332AC75B410075B819 /* UwaziEntityMandatoryTextView.swift in Sources */, 12E9DD9927DF65E80002FD00 /* MoveFilesView.swift in Sources */, + E1975EE72AB032B10060228F /* UwaziEntryPrompt.swift in Sources */, 1291A83628BE3948001A6055 /* SettingsLinkItemView.swift in Sources */, 0ECC7819267A53A700A2ACF2 /* PageView.swift in Sources */, 12FFEFA528BE96E00081DDB2 /* AddServerURLView.swift in Sources */, @@ -2390,62 +2896,85 @@ 129B844F28BF5E4700F1B344 /* TextfieldView.swift in Sources */, 1203CDE92992C19D00D09073 /* JSONStringEncoder.swift in Sources */, 125FA6D8278E2828004B9D06 /* DoubleExtension.swift in Sources */, + CCEAE5942AE7188F0069204A /* UwaziSelectWidget.swift in Sources */, 129533C42739D5BC0053FA4B /* TextFieldBottomSheetView.swift in Sources */, + E1EEE2A729F7E1A8009FE227 /* UwaziLanguageSelectionView.swift in Sources */, + CCAEA0522A97E038008190E0 /* CreateDraftHeaderView.swift in Sources */, 1224DAF02719C71B001C0ED0 /* LockPasswordData.swift in Sources */, - D572EACB263A43C100CE191A /* FileDetailView.swift in Sources */, + E14E0CF629FAD68E004FC4CD /* UwaziSuccessView.swift in Sources */, 12A1488F27209F2800A1ADDC /* ViewExtension.swift in Sources */, - 0D4BC3CE267668D600DCDC30 /* AddFileYellowButton.swift in Sources */, 12199C34294094430041BD38 /* OutboxDetailsView.swift in Sources */, + CC5CEB892AF0551900BF238B /* UwaziAttachment.swift in Sources */, + CCC1B4312AC75B120075B819 /* UwaziEntityButtonView.swift in Sources */, 1272F26127C91CD90054F2E2 /* ImportFilesProgressModels.swift in Sources */, 1291A83228BE0086001A6055 /* SettingsAddServerCardView.swift in Sources */, + E1E7C56F2AA9822F00DDB07E /* DeleteServerTexts.swift in Sources */, 122B61362AE72D8D00631403 /* FeedbackService.swift in Sources */, 0E87C875268BDDFB00EB5E34 /* ImageViewer.swift in Sources */, 1233076729F15A290019DF7C /* NetworkMonitor.swift in Sources */, 1249B3832AD43AA7009A7023 /* VaultFilesManagerInterface.swift in Sources */, 0DA213CB268028A200526995 /* FileSortOptions.swift in Sources */, + CCEAE5922AE6F6EE0069204A /* EntityCreationResponse.swift in Sources */, 1289B98927C53A9C00315FCE /* CameraType.swift in Sources */, 0E78B3A72656B03600F9BDBC /* DragView.swift in Sources */, 1231A45E294C779D0057DA85 /* ReportStatus.swift in Sources */, + E16D20D12ABEB5C7001708CC /* TemplateCardViewModel.swift in Sources */, 12607D562790416400E2B8CC /* PlayerViewModel.swift in Sources */, 128B929B27D798E200E92ACF /* ManageFileView.swift in Sources */, + E1E7C57D2AAB553500DDB07E /* UwaziProperty.swift in Sources */, 1237A71A278F18C000D7BC0F /* WebView.swift in Sources */, 1244141727E357E80037D469 /* LoadMoreCell.swift in Sources */, + CCC1B4292AC75A8E0075B819 /* RenderPropertyComponentView.swift in Sources */, 125BC1DE2B21F08200A117D0 /* VaultFileDetailsToMerge.swift in Sources */, 12E0882027AD29E400CEB198 /* SaveAudioConfirmationView.swift in Sources */, 125A895F2A965C33009347C3 /* VaultFileType.swift in Sources */, 123AE5A129CFAD7F00814CC7 /* OutboxReportVM.swift in Sources */, 12FDEDD5272AFE99005C17AC /* LocalizableHome.swift in Sources */, D572EAC7263A383900CE191A /* RecentFileCell.swift in Sources */, + CCF8D5C42AE9AA08000D27C5 /* PrimaryDocuments.swift in Sources */, 1268554027D2994500385E18 /* VaultFileStatus.swift in Sources */, 0D4BC3C5267577F900DCDC30 /* ReportsView.swift in Sources */, 12776A9027BA742700CDC5DC /* CameraControlsView.swift in Sources */, 128D6E1A28045C090082AB18 /* ManageFileType.swift in Sources */, + CC5DF8782B02B06A0068F622 /* UwaziServer.swift in Sources */, 12C2BDE82847676100488060 /* LockTimeoutView.swift in Sources */, 1216123E28DE142D00E6D7B6 /* JoinItem.swift in Sources */, + 126FEE0A2AC4993900B99298 /* ReportPages.swift in Sources */, 1218EDFC2AFED7F700F672F7 /* FeedbackConstants.swift in Sources */, 124190462A43230000E177F3 /* SQLiteStatementBuilderExtension.swift in Sources */, 12EC94D828ABCCED0070E72B /* SettingsItemView.swift in Sources */, 1249B3812AD43A8D009A7023 /* VaultManagerInterface.swift in Sources */, 125A89582A965BA4009347C3 /* VaultFileDB.swift in Sources */, 126ECFEA27C57FA600ED5161 /* CameraState.swift in Sources */, + CCC1B42F2AC75AF60075B819 /* UwaziDividerWidget.swift in Sources */, 12AB30D3272C36500020FFBF /* HomeData.swift in Sources */, + CC5CEB872AF037B600BF238B /* FileDropdown.swift in Sources */, 128BEB202B1E4B3D00420923 /* FeedbackOperation.swift in Sources */, 128EDB92280980DB00747B98 /* SequenceExtension.swift in Sources */, 1289B98727C5397200315FCE /* ScreenExtension.swift in Sources */, 1227FC8F2922BDBC002FD6FB /* CameraModel.swift in Sources */, + E1EEE29629E6796E009FE227 /* ServerSelectionView.swift in Sources */, + E131BA3B2A1B45A30095BC91 /* UwaziServerRepositories.swift in Sources */, 12730E69279F420400DC0135 /* SettingsViewModel.swift in Sources */, 12567FF9271742C500A2D356 /* Localizable.swift in Sources */, + CC9DEE5F2AE80ED400C8ED29 /* UwaziFileSelector.swift in Sources */, 122DBE012A5C1E0500D6561A /* Toast.swift in Sources */, 1236EE8228E1E1D700973EEA /* DatabaseExtension.swift in Sources */, 121F87AA2964383D00E6BA0D /* SubmittedReportVM.swift in Sources */, 0DD99E052692D24D009327B3 /* SwipeToDeleteView.swift in Sources */, 121C5D7529422C0800A64123 /* ProjectDetailsResult.swift in Sources */, + 15EE30FE2B5073E20033BBBB /* Pages.swift in Sources */, + CCC1B4272AC75A380075B819 /* CreateEntityView.swift in Sources */, 121C945E28FF3DCC00570EB4 /* EmptyReportView.swift in Sources */, + CCF47F442ABE18D7000ABBBC /* TemplateActionType.swift in Sources */, CC0797BE2A8D46CB00AAE63F /* UnlockView.swift in Sources */, 1216123C28DE13AC00E6D7B6 /* DatabaseUtilities.swift in Sources */, + CCC1B4392AC75B8D0075B819 /* UwaziEntityParser.swift in Sources */, 128BEB222B1E4B7600420923 /* OfflineFeedbackToast.swift in Sources */, 12D7D11C28E3581E008121C8 /* URLRequest.swift in Sources */, + CC0D56ED2AC4B46F00AA5D81 /* DownloadedTemplatesVM.swift in Sources */, 128C7BC42745AB0100A680B3 /* FileListViewModel.swift in Sources */, + E1EAA0552AA74FEC00492078 /* UwaziLanguageContext.swift in Sources */, 12CCDC5428D8E05300C3BE4E /* Project.swift in Sources */, 126818C12A38B0C2004606BD /* FileInformation.swift in Sources */, 129354772949F04700735ED0 /* EmptyResult.swift in Sources */, @@ -2455,12 +2984,15 @@ 12EF3D692B136D6F007A2910 /* UpdateQuery.swift in Sources */, 125A9E0028CE52C700C0C3C8 /* TellaDataBase.swift in Sources */, 12A2DB03273576D50094B856 /* AddFileView.swift in Sources */, + E1E7C5892AAB7ED700DDB07E /* UwaziTranslationRow.swift in Sources */, 128D6E1C280480500082AB18 /* FileActionType.swift in Sources */, D75F9E9C25342539006F6212 /* RecordViewModel.swift in Sources */, + E1EEE2A129F6EA2B009FE227 /* UwaziLoginView.swift in Sources */, 12FAC0CF2799FF230068EC2B /* ImportProgress.swift in Sources */, 1293F6662AD7E52800F6CFBD /* FeedbackView.swift in Sources */, 1291A83428BE0D51001A6055 /* SettingsServerItemView.swift in Sources */, 12607D5A2790457600E2B8CC /* VideoViewer.swift in Sources */, + CCD586C22A7AB28200014F87 /* TemplateListView.swift in Sources */, 12A8B09D2AFC0CEE007C838F /* LocalizableCommon.swift in Sources */, 1242135529B774A40002402D /* DeleteReportConfirmationView.swift in Sources */, 12B5257E2849037E00B3D1C0 /* LockTimeoutOptionsStatus.swift in Sources */, @@ -2471,37 +3003,48 @@ 1EAE6BF524146C2100114244 /* QuickLook.swift in Sources */, 1268554127D2ACB200385E18 /* RecentFilesListView.swift in Sources */, 12D4E8322B45964E00E7FCE7 /* FileWalker.swift in Sources */, + E1E7C57F2AAB5DB500DDB07E /* UwaziSetting.swift in Sources */, + E1E7C57B2AAB54BE00DDB07E /* UwaziCommonProperty.swift in Sources */, 127C155827DA486A009EC15B /* TransitionView.swift in Sources */, 1204520028C7AC2800D572A7 /* ServerActionType.swift in Sources */, 125F9B9B2AD9B4770073748C /* DictionaryExtension.swift in Sources */, 12776A9827BAC87A00CDC5DC /* CameraViewModel.swift in Sources */, + CC43D9E12A72F8260030192E /* UwaziView.swift in Sources */, 1256A33929490A7A00FE2B5C /* FileInfos.swift in Sources */, 121C5D7929422DF400A64123 /* DomainModel.swift in Sources */, 125F9B932AD92FC00073748C /* Feedback.swift in Sources */, 1289B45628B62A99005DA687 /* DeviceOrientationHelper.swift in Sources */, + E1D48B852A1BB8EA00D36270 /* UwaziCheckURLDTO.swift in Sources */, 125DD9C92796CB090053619D /* ImportFilesProgressView.swift in Sources */, 1E67B39223FC9067001F3D64 /* ImagePickerSheet.swift in Sources */, 12B3AD8A28C0DD0200AC9AF9 /* ServerLoginView.swift in Sources */, 1285BF402A3C5D63005D5D23 /* OpenXmlFormats.swift in Sources */, + CC4864E92AF94AF300293F62 /* SubmitEntityView.swift in Sources */, 128FD0B427F774FB00B24915 /* RecentFile.swift in Sources */, 12D37A962721A49D00E43BDB /* NavigationContainerView.swift in Sources */, + E1EEE29E29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift in Sources */, + E17736402A83FFA200FE01C0 /* UwaziDictionaryDTO.swift in Sources */, + CCC60CD22B0D2C5B00A3DB8C /* UwaziEmptyView.swift in Sources */, + E11968B12A1F409D00BA7B56 /* UwaziLanguageDTO.swift in Sources */, 12607D5327903E9E00E2B8CC /* CustomVideoControlsView.swift in Sources */, 1216F8742901823100F5AEA5 /* UnderlinedTextEditorView.swift in Sources */, 12BA188628343BE5002A11D7 /* MoreFileActionButton.swift in Sources */, + CCC1B43D2AC75C5D0075B819 /* UwaziConstants.swift in Sources */, 1268553E27D2572100385E18 /* FileInfoView.swift in Sources */, 1289B45828B63187005DA687 /* RotationViewModifier.swift in Sources */, 12751EAC2A13B6A100FAD7C6 /* VaultFileInfo.swift in Sources */, 126818BF2A376A0E004606BD /* OOXMLContentTypeParser.swift in Sources */, 125D2326271F896400250FBB /* CustomPinView.swift in Sources */, + E1EEE29A29EECF25009FE227 /* UwaziAddServerURLView.swift in Sources */, CC13138C2A5DB79B0057271C /* BottomButtonsView.swift in Sources */, 129B845128BF6C7800F1B344 /* ServersViewModel.swift in Sources */, 1203CDDC298C5B9100D09073 /* ReportActionType.swift in Sources */, + E16D20CF2ABEAAB1001708CC /* TemplateItemViewModel.swift in Sources */, 12476E1F2A5B7D2F00069048 /* TrailingButtonToolbar.swift in Sources */, 12199C3A2941260D0041BD38 /* OutboxDetailsItemView.swift in Sources */, 128D6E1E2804DA2A0082AB18 /* ListActionSheetItem.swift in Sources */, 12623DCC299EA31400E033ED /* ReportVaultFile.swift in Sources */, 125AAD3129534259002194D6 /* UploadProgressInfo.swift in Sources */, - 1E67B39223FC9067001F3D64 /* ImagePickerSheet.swift in Sources */, D572EAC9263A384B00CE191A /* FileGroupsView.swift in Sources */, 127C4AAF27E4A74B00B1A1D3 /* ShareFileView.swift in Sources */, 12567FEF2716B38F00A2D356 /* LockChoiceView.swift in Sources */, @@ -2516,7 +3059,9 @@ 120451FC28C795DC00D572A7 /* EditServerDisplayItemView.swift in Sources */, 125F9B952AD933AB0073748C /* FeedbackStatus.swift in Sources */, 122389B929367ABA0058593B /* ServerType.swift in Sources */, + CCD586D42A7C294800014F87 /* TemplateItemView.swift in Sources */, CCA66C9C29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift in Sources */, + E18BD4332AA0C39A00EE62E2 /* UwaziLanguage.swift in Sources */, 1217FCA328F99A7D00DD2D70 /* CircularLoader.swift in Sources */, 12D37A982721EDB700E43BDB /* PinKeyboardModel.swift in Sources */, 122C54BE28C6351E003B2DBC /* AdvancedServerSettingsView.swift in Sources */, @@ -2524,6 +3069,7 @@ 12619B8B27A01F6600999FDA /* BundleExtension.swift in Sources */, 12283FA0295C6FB100580EFE /* DataExtension.swift in Sources */, 1209FD2E27AD89C800D8C17E /* AudioPlayerViewModel.swift in Sources */, + E177363E2A83CCE100FE01C0 /* UwaziSettingDTO.swift in Sources */, 1233007828E30C7900B8F7BA /* APIRequest.swift in Sources */, 12567FF72717426000A2D356 /* LocalizableLock.swift in Sources */, 1224DAEC271975D3001C0ED0 /* PasswordView.swift in Sources */, @@ -2531,7 +3077,10 @@ 125EDDAD271DBD5200E2789A /* ContentView.swift in Sources */, 12351C1E28D267EF00F3CB7C /* DatabaseConstants.swift in Sources */, 12A148962720B2B400A1ADDC /* PasswordTextFieldView.swift in Sources */, + E1F4A8E42AAF3F80001C8E61 /* UwaziTemplateProtocol.swift in Sources */, + CCD586D12A7C096C00014F87 /* AddTemplatesView.swift in Sources */, 1236644F28FFECEF00FA839B /* DraftReportView.swift in Sources */, + CCC1B42D2AC75AD20075B819 /* UwaziTextWidget.swift in Sources */, 12603DAC2A56C72900FB1492 /* ToastView.swift in Sources */, D50727BF2625561400BBC370 /* KeychainManager.swift in Sources */, 1203CDE129914FA300D09073 /* Value.swift in Sources */, @@ -2539,15 +3088,19 @@ D7CFC37F254260D300CCB352 /* RecordView.swift in Sources */, D7F55FC825348FA50028739E /* RecordState.swift in Sources */, 12AA5A3528576E9C00134A9B /* ArrayExtension.swift in Sources */, - 0ECC781B267A542300A2ACF2 /* Pages.swift in Sources */, + 0ECC781B267A542300A2ACF2 /* UwaziPages.swift in Sources */, 120451FE28C7AB8100D572A7 /* AdvancedSettingsViews.swift in Sources */, 12A43B4F29941574003EF33E /* ReportFile.swift in Sources */, 0E998553266E57FC0019FDF4 /* HeaderView.swift in Sources */, 12935479294A3BFC00735ED0 /* MIMEType.swift in Sources */, + E1E7C5822AAB707C00DDB07E /* UwaziDictionary.swift in Sources */, 126FE4D727A427E200AE4188 /* Constants.swift in Sources */, 122389BB293683930058593B /* ServerDataItem.swift in Sources */, + CCC1B4252AC759F70075B819 /* UwaziEntityViewModel.swift in Sources */, + CCC1B42B2AC75AAE0075B819 /* GenericEntityWidget.swift in Sources */, 123AE5A329CFB7F000814CC7 /* UploadReportOperation.swift in Sources */, 12EC94CF28AA566C0070E72B /* GeneralView.swift in Sources */, + CCD586CF2A7AD9E400014F87 /* MoreButtonView.swift in Sources */, 1240E6B22908123900692232 /* ReportListView.swift in Sources */, 1218E86528E48AEC00FE2E68 /* LoginResult.swift in Sources */, 1204F64327D928C9006E40EE /* FileTypeExtension.swift in Sources */, @@ -2556,14 +3109,21 @@ CE02ED7F24E42B3900014176 /* UserDefaultsProperty.swift in Sources */, 122389B3293630DA0058593B /* SaveSuccessView.swift in Sources */, 12B6AF7D283E4C7200D697F6 /* LocalizableVault.swift in Sources */, + CCAE3A682AC217EC00A38C6D /* UwaziViewModel.swift in Sources */, + E1EEE2A429F7D192009FE227 /* UwaziTwoStepVerification.swift in Sources */, + CCE8CCDD2AF41E410088B61D /* UwaziFileUtility.swift in Sources */, 122E6A5529A396C400BDACAD /* ServerFileSize.swift in Sources */, 125EFACD274A447100D93DF6 /* FileSortMenu.swift in Sources */, + CCD586C72A7ABDE800014F87 /* TemplateCardView.swift in Sources */, 12607D58279042CE00E2B8CC /* VideoPlayer.swift in Sources */, 121AD8222943A2F00056AC2A /* ReportRepository.swift in Sources */, + E1E7C5792AAB515100DDB07E /* UwaziTemplateRow.swift in Sources */, 12567FFC27178EE200A2D356 /* LockButton.swift in Sources */, 127C155E27DA6C2E009EC15B /* EmptyFileListView.swift in Sources */, + CC1C734D2AF5439600D736F8 /* UwaziMultipartData.swift in Sources */, 12781F4427E9FCE200FE9609 /* PasswordTypeEnum.swift in Sources */, D5DA7F9B2648D05400B00FDA /* FileListView.swift in Sources */, + E1E7C5712AAB2BAB00DDB07E /* UwaziLanguageRow.swift in Sources */, 1237A71C278F5B2E00D7BC0F /* DateExtension.swift in Sources */, 122E4D55291ADEB300562EB1 /* AddFilesToDraftView.swift in Sources */, 125299DD2AD02E6F00191CAB /* ImportVaultFileResult.swift in Sources */, @@ -2585,6 +3145,7 @@ 128EDB942809897C00747B98 /* HomeViewModel.swift in Sources */, 122389BE293698010058593B /* ConnectionsView.swift in Sources */, 122C54BC28C614C2003B2DBC /* TellaButtonStyleProtocol.swift in Sources */, + E1F4A8E22AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift in Sources */, 0DC267A8267AEDAD00E55AFA /* DefaultFileManager.swift in Sources */, 123125422930241500D3BCD5 /* PhotoVideoViewModel.swift in Sources */, 0D4D7518266470A700D7A635 /* SettingToggleItem.swift in Sources */, @@ -2859,8 +3420,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella; + MARKETING_VERSION = 1.5.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella.internal; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -2879,7 +3440,6 @@ CURRENT_PROJECT_VERSION = 59; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 6ZG9T42688; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 6ZG9T42688; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "Tella/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -2887,11 +3447,10 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.4.0; - PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella; + MARKETING_VERSION = 1.5.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella.internal; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "Tella (AppStore)"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Tella (AppStore)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; diff --git a/Tella/Application/TellaApp.swift b/Tella/Application/TellaApp.swift index 4df8d8d01..dc284b036 100644 --- a/Tella/Application/TellaApp.swift +++ b/Tella/Application/TellaApp.swift @@ -14,6 +14,7 @@ struct TellaApp: App { private var appViewState = AppViewState() @Environment(\.scenePhase) var scenePhase @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + let delayTimeInSecond = 1.0 var body: some Scene { WindowGroup { @@ -40,7 +41,7 @@ struct TellaApp: App { } } } - + func saveData(lockApptype:LockApptype) { @@ -56,14 +57,13 @@ struct TellaApp: App { if shouldResetApp && !hasFileOnBackground { appViewState.homeViewModel.shouldSaveCurrentData = true - DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { + DispatchQueue.main.asyncAfter(deadline: .now() + delayTimeInSecond, execute: { appViewState.homeViewModel.vaultManager.clearTmpDirectory() // TO FIX for server doesn't allow upload in Background appViewState.resetApp() }) appViewState.homeViewModel.shouldSaveCurrentData = false } } - func resetApp() { let hasFileOnBackground = UploadService.shared.hasFilesToUploadOnBackground let appEnterInBackground = appViewState.homeViewModel.appEnterInBackground diff --git a/Tella/Components/BottomSheet/ConfirmBottomSheet.swift b/Tella/Components/BottomSheet/ConfirmBottomSheet.swift index a1ca1ee4c..844525abc 100644 --- a/Tella/Components/BottomSheet/ConfirmBottomSheet.swift +++ b/Tella/Components/BottomSheet/ConfirmBottomSheet.swift @@ -24,13 +24,12 @@ struct ConfirmBottomSheet : View { } var contentView: some View { - VStack(alignment: .leading, spacing: 9) { - Text(self.titleText) .foregroundColor(.white) .font(Font.custom(Styles.Fonts.semiBoldFontName, size: 17)) - + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) Text(self.msgText) .foregroundColor(.white) .font(Font.custom(Styles.Fonts.regularFontName, size: 14)) @@ -38,9 +37,7 @@ struct ConfirmBottomSheet : View { .fixedSize(horizontal: false, vertical: true) Spacer() HStack(alignment: .lastTextBaseline ){ - Spacer() - Button(action: { didCancelAction?() sheetManager.hide() @@ -48,8 +45,6 @@ struct ConfirmBottomSheet : View { }){ Text(self.cancelText) }.buttonStyle(ButtonSheetStyle()) - - if let discardText = discardText { Spacer() .frame(width: 10) @@ -63,7 +58,6 @@ struct ConfirmBottomSheet : View { }.buttonStyle(ButtonSheetStyle()) } - Spacer() .frame(width: 10) diff --git a/Tella/Components/ConfirmationBottomSheet.swift b/Tella/Components/ConfirmationBottomSheet.swift index bbd711df0..5b2dd0202 100644 --- a/Tella/Components/ConfirmationBottomSheet.swift +++ b/Tella/Components/ConfirmationBottomSheet.swift @@ -3,7 +3,7 @@ // Tella // // Created by Gustavo on 16/03/2023. -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import SwiftUI diff --git a/Tella/Components/Connections/Card/ConnectionCardDetail.swift b/Tella/Components/Connections/Card/ConnectionCardDetail.swift new file mode 100644 index 000000000..de8726d1e --- /dev/null +++ b/Tella/Components/Connections/Card/ConnectionCardDetail.swift @@ -0,0 +1,33 @@ +// +// ConnectionCardDetail.swift +// Tella +// +// Created by Gustavo on 02/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct ConnectionCardDetail: View { + var title : String + var subtitle: String + var body: some View { + VStack(alignment: .leading, spacing: 6) { + + Text(title) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .foregroundColor(.white) + .lineLimit(1) + + Text(subtitle) + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(.white) + } + } +} + +struct ReportCardDetail_Previews: PreviewProvider { + static var previews: some View { + ConnectionCardDetail(title: "", subtitle: "") + } +} diff --git a/Tella/Components/Connections/Card/MoreButtonView.swift b/Tella/Components/Connections/Card/MoreButtonView.swift new file mode 100644 index 000000000..c703b2e2e --- /dev/null +++ b/Tella/Components/Connections/Card/MoreButtonView.swift @@ -0,0 +1,28 @@ +// +// MoreButtonView.swift +// Tella +// +// Created by Gustavo on 02/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct MoreButtonView: View { + var imageName : String + var action : () -> Void + var body: some View { + Button { + action() + } label: { + Image(imageName) + .padding() + } + } +} + +struct MoreButtonView_Previews: PreviewProvider { + static var previews: some View { + MoreButtonView(imageName: "", action: {}) + } +} diff --git a/Tella/Components/CustomNavigation.swift b/Tella/Components/CustomNavigation.swift index 7a6d39fab..4c69d59c2 100644 --- a/Tella/Components/CustomNavigation.swift +++ b/Tella/Components/CustomNavigation.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import SwiftUI diff --git a/Tella/Components/Tabs/PageViewCellNotification.swift b/Tella/Components/Tabs/PageViewCellNotification.swift deleted file mode 100644 index 8f82eb0bc..000000000 --- a/Tella/Components/Tabs/PageViewCellNotification.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright © 2021 INTERNEWS. All rights reserved. -// - -import SwiftUI - -struct PageViewCellNotification: View { - - let title: String - let width: CGFloat - let page: Pages - - @Binding var selectedOption: Pages - @Binding var outBoxCount: Int - - public var body: some View { - VStack { - let selected: Bool = page == selectedOption - HStack(spacing : 2){ - Text(title) - .font(Font.system(size: 15)) - .bold() - .foregroundColor(selected ? .white : .gray) - - Text("("+String(outBoxCount)+")") - .foregroundColor(.yellow) - .font(Font.system(size: 12)) - .bold() - - }.padding(.bottom, 1) - Rectangle() - .fill(selected ? Color.white : Color.clear) - .frame(width: width, height: 4, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) - } - } -} - diff --git a/Tella/Components/Tabs/Pages.swift b/Tella/Components/Tabs/Pages.swift index d703fc6a6..7417a9ab8 100644 --- a/Tella/Components/Tabs/Pages.swift +++ b/Tella/Components/Tabs/Pages.swift @@ -1,5 +1,9 @@ // -// Copyright © 2021 INTERNEWS. All rights reserved. +// Pages.swift +// Tella +// +// Created by gus valbuena on 1/11/24. +// Copyright © 2024 HORIZONTAL. All rights reserved. // import Foundation @@ -9,6 +13,7 @@ public enum Pages: Hashable { case draft case outbox case submitted + case template var title: String { switch self { @@ -19,6 +24,8 @@ public enum Pages: Hashable { return "Outbox" case .submitted: return "Submitted" + case .template: + return "Template" } } @@ -30,6 +37,8 @@ public enum Pages: Hashable { return 1 case .submitted: return 2 + case .template: + return 3 } } @@ -41,6 +50,8 @@ public enum Pages: Hashable { return .outbox case 2: return .submitted + case 3: + return .template default: return .draft } diff --git a/Tella/Components/TellaButton/TellaButtonView.swift b/Tella/Components/TellaButton/TellaButtonView.swift index 2d1865f83..14be31ca3 100644 --- a/Tella/Components/TellaButton/TellaButtonView.swift +++ b/Tella/Components/TellaButton/TellaButtonView.swift @@ -10,6 +10,7 @@ struct TellaButtonView : View { var title : String var nextButtonAction : NextButtonAction var buttonType : ButtonType = .clear + var isOverlay: Bool = false var destination : Destination? @Binding var isValid : Bool @@ -18,7 +19,6 @@ struct TellaButtonView : View { var buttonStyle : TellaButtonStyleProtocol { - switch buttonType { case .yellow: return YellowButtonStyle() @@ -47,6 +47,9 @@ struct TellaButtonView : View { }.cornerRadius(20) .buttonStyle(TellaButtonStyle(buttonStyle: buttonStyle, isValid: isValid)) .disabled(isValid == false) + .overlay(self.isOverlay ? + RoundedRectangle(cornerRadius: 20) + .stroke(.white, lineWidth: 4) : nil) } } diff --git a/Tella/Components/TextfieldView.swift b/Tella/Components/TextfieldView.swift index e6286cf78..1d2ce806d 100644 --- a/Tella/Components/TextfieldView.swift +++ b/Tella/Components/TextfieldView.swift @@ -5,7 +5,13 @@ import SwiftUI import Combine - +enum FieldType { + case url + case username + case text + case password + case code +} struct TextfieldView : View { @Binding var fieldContent : String @@ -19,6 +25,7 @@ struct TextfieldView : View { var placeholder : String = "" var shouldShowTitle : Bool = false var onCommit : (() -> Void)? = ({}) + var keyboardType: UIKeyboardType = .default @State private var shouldShowPassword : Bool = false @@ -35,6 +42,7 @@ struct TextfieldView : View { .font(.custom(Styles.Fonts.regularFontName, size: 14)) .frame(maxWidth: .infinity,alignment: .leading) .contentShape(Rectangle()) + .foregroundColor(fieldContent.isEmpty ? .white : .white.opacity(0.8)) .scaleEffect(fieldContent.isEmpty ? 1 : 0.88, anchor: .leading) .transaction { transaction in @@ -46,7 +54,7 @@ struct TextfieldView : View { } // Textfield - if fieldType == .password { + if fieldType == .password || fieldType == .code { passwordTextfieldView } else { textfieldView @@ -67,9 +75,13 @@ struct TextfieldView : View { var textfieldView : some View { - TextField("", text: $fieldContent,onCommit: { + TextField("", + text: $fieldContent, + onCommit: { self.onCommit?() - }).onChange(of: fieldContent, perform: { value in + }) + .keyboardType(keyboardType) + .onChange(of: fieldContent, perform: { value in validateField(value: value) self.pfieldContent = value }) @@ -91,6 +103,7 @@ struct TextfieldView : View { self.onCommit?() }) }} + .keyboardType(keyboardType) .textFieldStyle(TextfieldStyle(shouldShowError: shouldShowError)) .onChange(of: fieldContent, perform: { value in validateField(value: value) @@ -142,6 +155,8 @@ struct TextfieldView : View { case .password: self.isValid = value.passwordValidator() + case .code: + self.isValid = value.codeValidator() } self.shouldShowError = false } @@ -171,10 +186,3 @@ struct TextfieldView_Previews: PreviewProvider { } } - -enum FieldType { - case url - case username - case text - case password -} diff --git a/Tella/Components/Widgets/CreateDraftHeaderView.swift b/Tella/Components/Widgets/CreateDraftHeaderView.swift new file mode 100644 index 000000000..06d39d0ce --- /dev/null +++ b/Tella/Components/Widgets/CreateDraftHeaderView.swift @@ -0,0 +1,58 @@ +// +// CreateDraftHeaderView.swift +// Tella +// +// Created by Gustavo on 24/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct CreateDraftHeaderView: View { + var title: String + var isDraft: Bool + var hideSaveButton: Bool? = false + var closeAction: () -> Void + var saveAction: () -> Void + + var body: some View { + HStack(spacing: 0) { + closeButton() + titleText() + Spacer() + if(!hideSaveButton!) { + saveButton() + } + }.frame(height: 56) + } + fileprivate func closeButton() -> Button { + return Button { + closeAction() + } label: { + Image("close") + .padding(EdgeInsets(top: 10, leading: 12, bottom: 5, trailing: 16)) + } + } + + fileprivate func saveButton() -> some View { + return Button { + saveAction() + } label: { + Image("reports.save") + .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16)) + .opacity(isDraft ? 1 : 0.4) + }.disabled(!isDraft) + } + + fileprivate func titleText() -> Text { + return Text(title) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 18)) + .foregroundColor(Color.white) + } +} + +struct CreateDraftHeaderView_Previews: PreviewProvider { + static var previews: some View { + CreateDraftHeaderView(title: "new draft", isDraft: true, closeAction: {}, saveAction: {}) + } +} diff --git a/Tella/Data/Database/Common/DatabaseConstants.swift b/Tella/Data/Database/Common/DatabaseConstants.swift index 2a70c9809..048a2b725 100644 --- a/Tella/Data/Database/Common/DatabaseConstants.swift +++ b/Tella/Data/Database/Common/DatabaseConstants.swift @@ -8,13 +8,15 @@ import SQLite3 struct D { /* DATABASE */ + // MARK: - DATABASE static let databaseName = "tella_vault.db" /* DATABASE VERSION */ - static let databaseVersion = 2 + static let databaseVersion = 3 /* DEFAULT TYPES FOR DATABASE */ + // MARK: - DEFAULT TYPES FOR DATABASE static let integer = " INTEGER " static let text = " TEXT "; static let blob = " BLOB "; @@ -22,14 +24,16 @@ struct D { /* DATABASE TABLES */ - + // MARK: - DATABASE TABLES static let tServer = "t_server" static let tReport = "t_report" static let tReportInstanceVaultFile = "t_report_instance_vault_file"; + static let tUwaziTemplate = "t_uwazi_template" + static let tUwaziServer = "t_uwazi_server" static let tFeedback = "t_feedback" /* DATABASE COLUMNS */ - + // MARK: - DATABASE COLUMNS static let cId = "c_id" static let cName = "c_name" static let cServerURL = "c_server_url" @@ -48,7 +52,6 @@ struct D { static let cApiProjectId = "c_api_project_id" static let cSlug = "c_slug" - static let cTitle = "c_title" static let cDescription = "c_description" static let cDate = "c_date" @@ -65,6 +68,15 @@ struct D { static let cCreatedDate = "c_created_date" static let cUpatedDate = "c_upated_date" static let cUpdatedDate = "c_updated_date" + // MARK: Uwazi Locale + static let cLocale = "c_locale" + + static let cTemplateId = "c_template_id" + static let cEntity = "c_entity" + static let cDownloaded = "c_downloaded" + static let cUpdated = "c_updated" + static let cFavorite = "c_favorite" + static let ctext = "c_text" diff --git a/Tella/Data/Database/Common/DatabaseUtilities.swift b/Tella/Data/Database/Common/DatabaseUtilities.swift index a85ad8a2f..3fea2110c 100644 --- a/Tella/Data/Database/Common/DatabaseUtilities.swift +++ b/Tella/Data/Database/Common/DatabaseUtilities.swift @@ -15,6 +15,9 @@ extension DataBase { func cddl(_ columnName: String, _ columnType: String) -> String { return columnName + " " + columnType; } + func cddl(_ columnName: String, _ columnType: String, defaultValue: String) -> String { + return columnName + " " + columnType //+ "DEFAULT" + defaultValue + } func cddl(_ columnName: String, _ columnType: String, _ notNull: Bool) -> String { return (columnName) + " " + columnType + (notNull ? " NOT NULL" : "") diff --git a/Tella/Data/Database/Common/SQLStatementBuilder.swift b/Tella/Data/Database/Common/SQLStatementBuilder.swift index bd78c1230..d5d39fd00 100644 --- a/Tella/Data/Database/Common/SQLStatementBuilder.swift +++ b/Tella/Data/Database/Common/SQLStatementBuilder.swift @@ -30,7 +30,15 @@ class SQLiteStatementBuilder { return userVersion } - + + func addColumnToExistingTable(tableName: String, column: String) { + let sqlExpression = "ALTER TABLE " + tableName + " ADD COLUMN " + column + let ret = sqlite3_exec(dbPointer, sqlExpression, nil, nil, nil) + + if (ret != SQLITE_OK) { // corrupt database. + logDbErr("Error altering db table - \(tableName)") + } + } func setNewDatabaseVersion(version:Int) throws { let sql = ("PRAGMA user_version = \(version)") @@ -248,8 +256,7 @@ class SQLiteStatementBuilder { if (ret != SQLITE_OK) { // corrupt database. logDbErr("Error creating db table - \(tableName)") } - } - + } @discardableResult func insertInto(tableName:String, keyValue: [KeyValue?]) throws -> Int { let keyValue = keyValue.compactMap({$0}) diff --git a/Tella/Data/Database/Protocols/UwaziServerLanguageProtocol.swift b/Tella/Data/Database/Protocols/UwaziServerLanguageProtocol.swift new file mode 100644 index 000000000..4fee6b2cc --- /dev/null +++ b/Tella/Data/Database/Protocols/UwaziServerLanguageProtocol.swift @@ -0,0 +1,19 @@ +// +// UwaziServerLanguageProtocol.swift +// Tella +// +// Created by Robert Shrestha on 9/11/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +protocol UwaziServerLanguageProtocol { + func createUwaziServerTable() + + func addUwaziServer(server: UwaziServer) -> Int? + + func getUwaziServer(serverId: Int) throws -> UwaziServer? + + func parseUwaziServer(dictionary : [String:Any] ) -> UwaziServer +} diff --git a/Tella/Data/Database/Protocols/UwaziTemplateProtocol.swift b/Tella/Data/Database/Protocols/UwaziTemplateProtocol.swift new file mode 100644 index 000000000..c06678105 --- /dev/null +++ b/Tella/Data/Database/Protocols/UwaziTemplateProtocol.swift @@ -0,0 +1,20 @@ +// +// UwaziTemplateProtocol.swift +// Tella +// +// Created by Robert Shrestha on 9/11/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +protocol UwaziTemplateProtocol { + func createTemplateTableForUwazi() + func getUwaziTemplate(serverId: Int) throws -> CollectedTemplate? + func getUwaziTemplate(templateId: Int) throws -> CollectedTemplate? + func getAllUwaziTemplate() throws -> [CollectedTemplate] + func addUwaziTemplate(template: CollectedTemplate) throws -> CollectedTemplate? + func deleteAllUwaziTemplate() throws + func deleteUwaziTemplate(templateId: String) throws + func deleteUwaziTemplate(id: Int) throws +} diff --git a/Tella/Data/Database/ServerDatabase.swift b/Tella/Data/Database/ServerDatabase.swift new file mode 100644 index 000000000..f898df079 --- /dev/null +++ b/Tella/Data/Database/ServerDatabase.swift @@ -0,0 +1,187 @@ +// +// UwaziServerDatabase.swift +// Tella +// +// Created by Robert Shrestha on 9/14/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +// MARK: - Methods related to Uwazi Server +extension TellaDataBase { + func createServerTable() { + let columns = [ + cddl(D.cServerId, D.integer, primaryKey: true, autoIncrement: true), + cddl(D.cName, D.text), + cddl(D.cURL, D.text), + cddl(D.cUsername, D.text), + cddl(D.cPassword, D.text), + cddl(D.cAccessToken, D.text), + cddl(D.cActivatedMetadata, D.integer), + cddl(D.cBackgroundUpload, D.integer), + cddl(D.cApiProjectId, D.text), + cddl(D.cSlug, D.text), + cddl(D.cAutoUpload, D.integer), + cddl(D.cAutoDelete, D.integer) + ] + statementBuilder.createTable(tableName: D.tServer, columns: columns) + } + + func addServer(server : TellaServer) -> Result { + do { + let valuesToAdd = [KeyValue(key: D.cName, value: server.name), + KeyValue(key: D.cURL, value: server.url), + KeyValue(key: D.cUsername, value: server.username), + KeyValue(key: D.cPassword, value: server.password ), + KeyValue(key: D.cAccessToken, value: server.accessToken), + KeyValue(key: D.cActivatedMetadata, value: server.activatedMetadata == false ? 0 : 1), + KeyValue(key: D.cBackgroundUpload, value: server.backgroundUpload == false ? 0 : 1), + KeyValue(key: D.cApiProjectId, value: server.projectId), + KeyValue(key: D.cSlug, value: server.slug), + KeyValue(key: D.cAutoUpload, value:server.autoUpload == false ? 0 : 1), + KeyValue(key: D.cAutoDelete, value:server.autoDelete == false ? 0 : 1)] + + let serverId = try statementBuilder.insertInto(tableName: D.tServer, + keyValue: valuesToAdd) + return .success(serverId) + } catch let error { + debugLog(error) + return .failure(error) + } + } + + func getAutoUploadServer() -> TellaServer? { + do { + let serverCondition = [KeyValue(key: D.cAutoUpload, value: 1)] + let serversDict = try statementBuilder.selectQuery(tableName: D.tServer, + andCondition:serverCondition) + if !serversDict.isEmpty, let dict = serversDict.first { + return getTellaServer(dictionnary: dict) + } + return nil + } catch { + return nil + } + } + + func getTellaServers() -> [TellaServer] { + var servers: [TellaServer] = [] + + do { + let serversDict = try statementBuilder.selectQuery(tableName: D.tServer, andCondition: []) + serversDict.forEach { dict in + servers.append(getTellaServer(dictionnary: dict)) + } + } catch { + debugLog("Error while fetching servers from \(D.tServer): \(error)") + } + + return servers + } + + func getTellaServerById(id: Int) throws -> TellaServer? { + let response = try statementBuilder.selectQuery(tableName: D.tServer, andCondition: [KeyValue(key: D.cServerId, value: id)]) + + guard let tellaServerDict = response.first else { return nil } + return getTellaServer(dictionnary: tellaServerDict) + } + + func getTellaServer(dictionnary : [String:Any]) -> TellaServer { + let id = dictionnary[D.cServerId] as? Int + let name = dictionnary[D.cName] as? String + let url = dictionnary[D.cURL] as? String + let username = dictionnary[D.cUsername] as? String + let password = dictionnary[D.cPassword] as? String + let token = dictionnary[D.cAccessToken] as? String + let activatedMetadata = dictionnary[D.cActivatedMetadata] as? Int + let backgroundUpload = dictionnary[D.cBackgroundUpload] as? Int + let apiProjectId = dictionnary[D.cApiProjectId] as? String + let slug = dictionnary[D.cSlug] as? String + let autoUpload = dictionnary[D.cAutoUpload] as? Int + let autoDelete = dictionnary[D.cAutoDelete] as? Int + + return TellaServer(id:id, + name: name, + serverURL: url, + username: username, + password: password, + accessToken: token, + activatedMetadata: activatedMetadata == 0 ? false : true , + backgroundUpload: backgroundUpload == 0 ? false : true, + projectId: apiProjectId, + slug:slug, + autoUpload: autoUpload == 0 ? false : true, + autoDelete: autoDelete == 0 ? false : true + ) + } + + func updateServer(server : TellaServer) -> Result { + do { + + let valuesToUpdate = [KeyValue(key: D.cName, value: server.name), + KeyValue(key: D.cURL, value: server.url), + KeyValue(key: D.cUsername, value: server.username), + KeyValue(key: D.cPassword, value: server.password), + KeyValue(key: D.cAccessToken, value: server.accessToken), + KeyValue(key: D.cActivatedMetadata, value: server.activatedMetadata == false ? 0 : 1), + KeyValue(key: D.cBackgroundUpload, value: server.backgroundUpload == false ? 0 : 1), + KeyValue(key: D.cApiProjectId, value: server.projectId), + KeyValue(key: D.cSlug, value: server.slug), + KeyValue(key: D.cAutoUpload, value:server.autoUpload == false ? 0 : 1 ), + KeyValue(key: D.cAutoDelete, value:server.autoDelete == false ? 0 : 1 )] + + let serverCondition = [KeyValue(key: D.cServerId, value: server.id)] + try statementBuilder.update(tableName: D.tServer, + valuesToUpdate: valuesToUpdate, + equalCondition: serverCondition) + return .success(true) + } catch let error { + debugLog(error) + return .failure(error) + } + } + + func deleteServer(serverId : Int) -> Result { + do { + var reportIDs : [Int] = [] + let serverCondition = [KeyValue(key: D.cServerId, value: serverId)] + + + let responseDict = try statementBuilder.selectQuery(tableName: D.tReport, + andCondition: serverCondition) + + responseDict.forEach { dict in + if let id = dict[D.cReportId] as? Int { + reportIDs.append(id) + } + } + + try statementBuilder.delete(tableName: D.tServer, + primarykeyValue: serverCondition) + + try statementBuilder.delete(tableName: D.tReport, + primarykeyValue: serverCondition) + + if !reportIDs.isEmpty { + let reportCondition = [KeyValues(key: D.cReportInstanceId, value: reportIDs)] + try statementBuilder.delete(tableName: D.tReportInstanceVaultFile, + inCondition: reportCondition) + } + return .success(true) + } catch let error { + debugLog(error) + return .failure(error) + } + } + + func deleteAllServers() -> Result { + do { + try statementBuilder.deleteAll(tableNames: [D.tServer, D.tReport, D.tReportInstanceVaultFile, D.tUwaziServer, D.tUwaziTemplate]) + return .success(true) + } catch let error { + debugLog(error) + return .failure(error) + } + + } +} diff --git a/Tella/Data/Database/TellaData.swift b/Tella/Data/Database/TellaData.swift index b3a662d4f..6c10d8424 100644 --- a/Tella/Data/Database/TellaData.swift +++ b/Tella/Data/Database/TellaData.swift @@ -12,6 +12,8 @@ class TellaData : ObservableObject { // Servers var servers = CurrentValueSubject<[Server], Error>([]) + var tellaServers = CurrentValueSubject<[TellaServer], Error>([]) + var uwaziServers = CurrentValueSubject<[UwaziServer], Error>([]) // Reports var draftReports = CurrentValueSubject<[Report], Error>([]) @@ -24,19 +26,31 @@ class TellaData : ObservableObject { getReports() } - func addServer(server : Server) -> Result { + func addServer(server : TellaServer) -> Result { let addServerResult = database.addServer(server: server) getServers() return addServerResult } + func addUwaziServer(server: UwaziServer) -> Int? { + let id = database.addUwaziServer(server: server) + getServers() + return id + } + @discardableResult - func updateServer(server : Server) -> Result { + func updateServer(server : TellaServer) -> Result { let updateServerResult = database.updateServer(server: server) getServers() return updateServerResult } + + func updateUwaziServer(server: UwaziServer) -> Int? { + let id = database.updateUwaziServer(server: server) + getServers() + return id + } @discardableResult func deleteServer(serverId : Int) -> Result { @@ -53,14 +67,41 @@ class TellaData : ObservableObject { getReports() return deleteAllServersResult } + + func deleteUwaziServer(serverId: Int) { + database.deleteUwaziServer(serverId: serverId) + getServers() + } func getServers(){ DispatchQueue.main.async { - self.servers.value = self.database.getServer() + self.tellaServers.value = self.database.getTellaServers() + self.uwaziServers.value = self.database.getUwaziServers() + + self.servers.value = self.tellaServers.value + self.uwaziServers.value + } + } + + func getTellaServer(serverId: Int) -> TellaServer? { + do { + return try database.getTellaServerById(id: serverId) + } catch { + debugLog(error) + return nil } } - func getAutoUploadServer() -> Server? { + func getUwaziServer(serverId: Int) -> UwaziServer? { + do { + return try database.getUwaziServer(serverId: serverId) + + }catch { + debugLog(error) + return nil + } + } + + func getAutoUploadServer() -> TellaServer? { return database.getAutoUploadServer() } @@ -192,4 +233,35 @@ class TellaData : ObservableObject { } } +// MARK: - Extension for Uwazi Template methods +extension TellaData { + func addUwaziTemplate(template: CollectedTemplate) -> CollectedTemplate? { + return database.addUwaziTemplate(template: template) + } + + func deleteAllUwaziTemplate(templateId: String) { + return database.deleteUwaziTemplate(templateId: templateId) + } + func getAllUwaziTemplate() -> [CollectedTemplate] { + do { + return try database.getAllUwaziTemplate() + } catch let error { + debugLog(error) + return [] + } + + } + func getUwaziTemplateById(id: Int) -> CollectedTemplate? { + do { + return try database.getUwaziTemplate(templateId: id) + } catch let error { + debugLog(error) + return nil + } + } + func deleteAllUwaziTemplate(id: Int) { + database.deleteUwaziTemplate(id: id) + } +} + diff --git a/Tella/Data/Database/TellaDataBase.swift b/Tella/Data/Database/TellaDataBase.swift index 8edf570d3..9fd5ae89f 100644 --- a/Tella/Data/Database/TellaDataBase.swift +++ b/Tella/Data/Database/TellaDataBase.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 INTERNEWS. All rights reserved. +// Copyright © 2022 HORIZONTAL. All rights reserved. // import Foundation @@ -28,12 +28,13 @@ class TellaDataBase : DataBase { case 1: createFeedbackTable() renameUpdatedDateColumn() + case 2: + createTemplateTableForUwazi() + createUwaziServerTable() default : break } - try statementBuilder.setNewDatabaseVersion(version: D.databaseVersion) - } catch let error { debugLog(error) } @@ -44,25 +45,8 @@ class TellaDataBase : DataBase { createReportTable() createReportFilesTable() createFeedbackTable() - } - - func createServerTable() { - // c_id | c_name | c_url | c_username | c_password | cAccessToken | cActivatedMetadata | cBackgroundUpload - let columns = [ - cddl(D.cServerId, D.integer, primaryKey: true, autoIncrement: true), - cddl(D.cName, D.text), - cddl(D.cURL, D.text), - cddl(D.cUsername, D.text), - cddl(D.cPassword, D.text), - cddl(D.cAccessToken, D.text), - cddl(D.cActivatedMetadata, D.integer), - cddl(D.cBackgroundUpload, D.integer), - cddl(D.cApiProjectId, D.text), - cddl(D.cSlug, D.text), - cddl(D.cAutoUpload, D.integer), - cddl(D.cAutoDelete, D.integer) ] - - statementBuilder.createTable(tableName: D.tServer, columns: columns) + createTemplateTableForUwazi() + createUwaziServerTable() } func createReportTable() { @@ -114,131 +98,6 @@ class TellaDataBase : DataBase { } } - func addServer(server : Server) -> Result { - do { - let valuesToAdd = [KeyValue(key: D.cName, value: server.name), - KeyValue(key: D.cURL, value: server.url), - KeyValue(key: D.cUsername, value: server.username), - KeyValue(key: D.cPassword, value: server.password ), - KeyValue(key: D.cAccessToken, value: server.accessToken), - KeyValue(key: D.cActivatedMetadata, value: server.activatedMetadata == false ? 0 : 1), - KeyValue(key: D.cBackgroundUpload, value: server.backgroundUpload == false ? 0 : 1), - KeyValue(key: D.cApiProjectId, value: server.projectId), - KeyValue(key: D.cSlug, value: server.slug), - KeyValue(key: D.cAutoUpload, value:server.autoUpload == false ? 0 : 1), - KeyValue(key: D.cAutoDelete, value:server.autoDelete == false ? 0 : 1)] - - let serverId = try statementBuilder.insertInto(tableName: D.tServer, - keyValue: valuesToAdd) - return .success(serverId) - } catch let error { - debugLog(error) - return .failure(error) - } - } - - func getServer() -> [Server] { - var servers : [Server] = [] - do { - let serversDict = try statementBuilder.selectQuery(tableName: D.tServer, andCondition: []) - - serversDict.forEach { dict in - servers.append(getServer(dictionnary: dict)) - } - - return servers - - } catch { - return [] - } - } - - func getAutoUploadServer() -> Server? { - - do { - let serverCondition = [KeyValue(key: D.cAutoUpload, value: 1)] - let serversDict = try statementBuilder.selectQuery(tableName: D.tServer, - andCondition:serverCondition ) - - if !serversDict.isEmpty, let dict = serversDict.first { - return getServer(dictionnary: dict) - } - return nil - } catch { - return nil - } - } - - func updateServer(server : Server) -> Result { - do { - - let valuesToUpdate = [KeyValue(key: D.cName, value: server.name), - KeyValue(key: D.cURL, value: server.url), - KeyValue(key: D.cUsername, value: server.username), - KeyValue(key: D.cPassword, value: server.password), - KeyValue(key: D.cAccessToken, value: server.accessToken), - KeyValue(key: D.cActivatedMetadata, value: server.activatedMetadata == false ? 0 : 1), - KeyValue(key: D.cBackgroundUpload, value: server.backgroundUpload == false ? 0 : 1), - KeyValue(key: D.cApiProjectId, value: server.projectId), - KeyValue(key: D.cSlug, value: server.slug), - KeyValue(key: D.cAutoUpload, value:server.autoUpload == false ? 0 : 1 ), - KeyValue(key: D.cAutoDelete, value:server.autoDelete == false ? 0 : 1 )] - - let serverCondition = [KeyValue(key: D.cServerId, value: server.id)] - try statementBuilder.update(tableName: D.tServer, - valuesToUpdate: valuesToUpdate, - equalCondition: serverCondition) - return .success(true) - } catch let error { - debugLog(error) - return .failure(error) - } - } - - func deleteServer(serverId : Int) -> Result { - do { - var reportIDs : [Int] = [] - let serverCondition = [KeyValue(key: D.cServerId, value: serverId)] - - - let responseDict = try statementBuilder.selectQuery(tableName: D.tReport, - andCondition: serverCondition) - - responseDict.forEach { dict in - if let id = dict[D.cReportId] as? Int { - reportIDs.append(id) - } - } - - try statementBuilder.delete(tableName: D.tServer, - primarykeyValue: serverCondition) - - try statementBuilder.delete(tableName: D.tReport, - primarykeyValue: serverCondition) - - if !reportIDs.isEmpty { - let reportCondition = [KeyValues(key: D.cReportInstanceId, value: reportIDs)] - try statementBuilder.delete(tableName: D.tReportInstanceVaultFile, - inCondition: reportCondition) - } - return .success(true) - } catch let error { - debugLog(error) - return .failure(error) - } - } - - func deleteAllServers() -> Result { - do { - try statementBuilder.deleteAll(tableNames: [D.tServer, D.tReport, D.tReportInstanceVaultFile]) - return .success(true) - } catch let error { - debugLog(error) - return .failure(error) - } - - } - func getReports(reportStatus:[ReportStatus]) -> [Report] { var reports : [Report] = [] @@ -665,36 +524,6 @@ class TellaDataBase : DataBase { } - private func getServer(dictionnary : [String:Any] ) -> Server { - - let id = dictionnary[D.cServerId] as? Int - let name = dictionnary[D.cName] as? String - let url = dictionnary[D.cURL] as? String - let username = dictionnary[D.cUsername] as? String - let password = dictionnary[D.cPassword] as? String - let token = dictionnary[D.cAccessToken] as? String - let activatedMetadata = dictionnary[D.cActivatedMetadata] as? Int - let backgroundUpload = dictionnary[D.cBackgroundUpload] as? Int - let apiProjectId = dictionnary[D.cApiProjectId] as? String - let slug = dictionnary[D.cSlug] as? String - let autoUpload = dictionnary[D.cAutoUpload] as? Int - let autoDelete = dictionnary[D.cAutoDelete] as? Int - - return Server(id:id, - name: name, - serverURL: url, - username: username, - password: password, - accessToken: token, - activatedMetadata: activatedMetadata == 0 ? false : true , - backgroundUpload: backgroundUpload == 0 ? false : true, - projectId: apiProjectId, - slug:slug, - autoUpload: autoUpload == 0 ? false : true, - autoDelete: autoDelete == 0 ? false : true) - - } - private func getReport(dictionnary : [String:Any] ) -> Report { let reportID = dictionnary[D.cReportId] as? Int @@ -712,7 +541,7 @@ class TellaDataBase : DataBase { createdDate: createdDate?.getDate() ?? Date(), updatedDate: updatedDate?.getDate() ?? Date(), status: ReportStatus(rawValue: status ?? 0) ?? .draft, - server: getServer(dictionnary: dictionnary), + server: getTellaServer(dictionnary: dictionnary), vaultFiles: getVaultFiles(reportID: reportID), apiID: apiReportId, currentUpload: currentUpload == 0 ? false : true) diff --git a/Tella/Data/Database/UwaziDatabase.swift b/Tella/Data/Database/UwaziDatabase.swift new file mode 100644 index 000000000..4e39c17a8 --- /dev/null +++ b/Tella/Data/Database/UwaziDatabase.swift @@ -0,0 +1,197 @@ +// +// TellaUwaziTemplateDatabase.swift +// Tella +// +// Created by Robert Shrestha on 9/14/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +// MARK: - Methods related to UwaziTemplateProtocol +extension TellaDataBase: UwaziTemplateProtocol { + func createTemplateTableForUwazi() { + let columns = [ + cddl(D.cId, D.integer, primaryKey: true, autoIncrement: true), + cddl(D.cTemplateId, D.text), + cddl(D.cEntity, D.text), + cddl(D.cDownloaded, D.integer), + cddl(D.cUpdated, D.integer), + cddl(D.cFavorite, D.integer), + cddl(D.cServerId, D.integer, tableName: D.tServer, referenceKey: D.cServerId) + + ] + statementBuilder.createTable(tableName: D.tUwaziTemplate, columns: columns) + } + func getUwaziTemplate(serverId: Int) throws -> CollectedTemplate? { + let serversDict = try statementBuilder.selectQuery(tableName: D.tUwaziTemplate, + andCondition: [KeyValue(key: D.cServerId, value: serverId)]) + guard let template = serversDict.first else { return nil } + return try JSONDecoder().decode(CollectedTemplate.self, from: template) + } + func getUwaziTemplate(templateId: Int) throws -> CollectedTemplate? { + let serversDict = try statementBuilder.selectQuery(tableName: D.tUwaziTemplate, + andCondition: [KeyValue(key: D.cId, value: templateId)]) + guard let template = serversDict.first else { return nil } + return try JSONDecoder().decode(CollectedTemplate.self, from: template) + } + func getAllUwaziTemplate() throws -> [CollectedTemplate] { + let serversDict = try statementBuilder.selectQuery(tableName: D.tUwaziTemplate, + andCondition: []) + if !serversDict.isEmpty { + return try JSONDecoder().decode([CollectedTemplate].self, from: serversDict) + } + return [] + } + func addUwaziTemplate(template: CollectedTemplate) -> CollectedTemplate? { + do { + let id = try statementBuilder.insertInto(tableName: D.tUwaziTemplate, keyValue: [ + KeyValue(key: D.cTemplateId, value: template.templateId), + KeyValue(key: D.cDownloaded, value: 1), + KeyValue(key: D.cUpdated, value: 1), + KeyValue(key: D.cFavorite, value: 0), + KeyValue(key: D.cServerId, value: template.serverId), + KeyValue(key: D.cEntity, value: template.entityRowString), + ]) + template.id = id + template.isUpdated = true + template.isDownloaded = true + return template + } catch let error { + debugLog(error) + return nil + } + } + func deleteAllUwaziTemplate() { + do { + try statementBuilder.deleteAll(tableNames: [D.tUwaziTemplate]) + } catch let error { + debugLog(error) + } + } + func deleteUwaziTemplate(templateId: String) { + do{ + try statementBuilder.delete(tableName: D.tUwaziTemplate, + primarykeyValue: [KeyValue(key: D.cTemplateId, value: templateId)]) + } catch let error { + debugLog(error) + } + } + func deleteUwaziTemplate(id: Int) { + do { + try statementBuilder.delete(tableName: D.tUwaziTemplate, + primarykeyValue: [KeyValue(key: D.cId, value: id)]) + } catch let error { + debugLog(error) + } + } +} +// MARK: - Methods related to UwaziServerLanguageProtocol +extension TellaDataBase: UwaziServerLanguageProtocol { + func createUwaziServerTable() { + let columns = [ + cddl(D.cId, D.integer, primaryKey: true, autoIncrement: true), + cddl(D.cName, D.text), + cddl(D.cURL, D.text), + cddl(D.cUsername, D.text), + cddl(D.cPassword, D.text), + cddl(D.cAccessToken, D.text), + cddl(D.cLocale, D.text), + ] + + statementBuilder.createTable(tableName: D.tUwaziServer, columns: columns) + } + + func addUwaziServer(server: UwaziServer) -> Int? { + do { + let valuesToAdd = [KeyValue(key: D.cName, value: server.name), + KeyValue(key: D.cURL, value: server.url), + KeyValue(key: D.cUsername, value: server.username), + KeyValue(key: D.cPassword, value: server.password ), + KeyValue(key: D.cAccessToken, value: server.accessToken), + KeyValue(key: D.cLocale, value: server.locale) + ] + + + return try statementBuilder.insertInto(tableName: D.tUwaziServer, keyValue: valuesToAdd) + } catch let error { + debugLog(error) + return nil + } + } + + func getUwaziServers() -> [UwaziServer] { + var servers: [UwaziServer] = [] + do { + let serversDict = try statementBuilder.selectQuery(tableName: D.tUwaziServer, andCondition: []) + serversDict.forEach { dict in + servers.append(parseUwaziServer(dictionary: dict)) + } + } catch { + debugLog("Error while fetching servers from \(D.tUwaziServer): \(error)") + } + + return servers + } + + func getUwaziServer(serverId: Int) throws -> UwaziServer? { + let response = try statementBuilder.selectQuery(tableName: D.tUwaziServer, + andCondition: [KeyValue(key: D.cId, value: serverId)]) + guard let uwaziServerDict = response.first else { return nil } + + return parseUwaziServer(dictionary: uwaziServerDict) + + } + + func parseUwaziServer(dictionary : [String:Any] ) -> UwaziServer { + let id = dictionary[D.cId] as? Int + let name = dictionary[D.cName] as? String + let url = dictionary[D.cURL] as? String + let username = dictionary[D.cUsername] as? String + let password = dictionary[D.cPassword] as? String + let token = dictionary[D.cAccessToken] as? String + let locale = dictionary[D.cLocale] as? String + return UwaziServer(id:id, + name: name, + serverURL: url, + username: username, + password: password, + accessToken: token, + locale: locale + ) + } + + func updateUwaziServer(server: UwaziServer) -> Int? { + do { + let valuesToUpdate = [KeyValue(key: D.cName, value: server.name), + KeyValue(key: D.cURL, value: server.url), + KeyValue(key: D.cUsername, value: server.username), + KeyValue(key: D.cPassword, value: server.password), + KeyValue(key: D.cAccessToken, value: server.accessToken), + KeyValue(key: D.cLocale, value: server.locale)] + + let serverCondition = [KeyValue(key: D.cId, value: server.id)] + return try statementBuilder.update(tableName: D.tUwaziServer, + valuesToUpdate: valuesToUpdate, + equalCondition: serverCondition) + } catch let error { + debugLog(error) + return nil + } + } + + func deleteUwaziServer(serverId : Int) { + do { + let serverCondition = [KeyValue(key: D.cId, value: serverId)] + + try statementBuilder.delete(tableName: D.tUwaziServer, + primarykeyValue: serverCondition) + + try statementBuilder.delete(tableName: D.tUwaziTemplate, + primarykeyValue: serverCondition) + } catch let error { + debugLog(error) + } + + } +} diff --git a/Tella/Data/MainAppModel.swift b/Tella/Data/MainAppModel.swift index 26d64d8e4..06c5488aa 100644 --- a/Tella/Data/MainAppModel.swift +++ b/Tella/Data/MainAppModel.swift @@ -62,7 +62,7 @@ class MainAppModel: ObservableObject { self.initDataSource() - if settings.shouldMergeVaultFilesToDb ?? true { + if self.settings.shouldMergeVaultFilesToDb ?? true { self.mergeFileToDatabase(promise: promise) } else { self.sendReports() @@ -148,17 +148,7 @@ extension MainAppModel { } func resetSettings() { - settings.unlockAttempts = 0 - settings.deleteAfterFail = .off - settings.quickDelete = false - settings.offlineMode = false - settings.deleteServerSettings = false - settings.showRecentFiles = false - settings.lockTimeout = .immediately - settings.screenSecurity = true - settings.deleteVault = false - settings.showUnlockAttempts = false - + settings = SettingsModel() saveSettings() } diff --git a/Tella/Data/Networking/APICall/APIError.swift b/Tella/Data/Networking/APICall/APIError.swift index b49bca2f1..3b8fbafd9 100644 --- a/Tella/Data/Networking/APICall/APIError.swift +++ b/Tella/Data/Networking/APICall/APIError.swift @@ -10,9 +10,11 @@ enum APIError: Swift.Error { case httpCode(HTTPCode) case unexpectedResponse case noInternetConnection + case badServer } extension APIError: LocalizedError { + var errorDescription: String? { switch self { case .invalidURL: @@ -22,14 +24,22 @@ extension APIError: LocalizedError { case .unexpectedResponse: return "Unexpected response from the server" case .noInternetConnection: - return "No Internet connection" + return LocalizableSettings.settServerNoInternetConnection.localized + case .badServer: + return LocalizableSettings.settServerServerURLIncorrect.localized } } - - func customErrorMessage(errorCode : Int) -> String { - switch errorCode { + private func customErrorMessage(errorCode : Int) -> String { + let httpErrorCode = HTTPErrorCodes(rawValue: errorCode) + switch httpErrorCode{ + case .unauthorized: + return "Invalid username or password" + case .forbidden: + return "Account locked due to too many unsuccessful attempts." + case .notFound: + return LocalizableSettings.settServerServerURLIncorrect.localized default: - return "Custom Error" + return "Unexpected response from the server" } } } diff --git a/Tella/Data/Networking/APICall/APIRequest.swift b/Tella/Data/Networking/APICall/APIRequest.swift index 72591cfe1..f46638df8 100644 --- a/Tella/Data/Networking/APICall/APIRequest.swift +++ b/Tella/Data/Networking/APICall/APIRequest.swift @@ -6,7 +6,7 @@ import Foundation public protocol APIRequest { - + associatedtype Value var keyValues : [Key : Value?]? { get } var urlQueryParameters : [String : String?]? { get } @@ -21,7 +21,8 @@ public protocol APIRequest { var url: URL? { get } var uploadsSession: URLSession? { get } var apiSession: URLSession? { get } - + var multipartBody: Data? { get } + var multipartHeader: String? {get} } public extension APIRequest { @@ -45,6 +46,9 @@ public extension APIRequest { var url: URL? { return URL(string: baseURL + path) } var uploadsSession: URLSession? { return nil } var apiSession: URLSession? { return nil } + var multipartBody: Data? { nil } + var multipartHeader: String? { nil } + } extension APIRequest { @@ -66,7 +70,14 @@ extension APIRequest { request.addValue(HTTPHeaderField.bearer.rawValue + token, forHTTPHeaderField: HTTPHeaderField.authorization.rawValue) } request.httpMethod = httpMethod.rawValue - request.httpBody = try body() + if encoding == .form { + request.setValue(multipartHeader, forHTTPHeaderField: "Content-Type") + + request.httpBody = multipartBody + } else { + request.httpBody = try body() + } + request.timeoutInterval = TimeInterval(30) return request } @@ -74,26 +85,25 @@ extension APIRequest { extension APIRequest { - func body() throws -> Data? { + func body(boundary: String? = nil) throws -> Data? { let keyValues = keyValues?.compactMapValues { $0 } ?? [:] let queryItemsDictionary = keyValues .reduce(into: [:]) { result, tuple in result[tuple.key.apiString] = tuple.value } - if !queryItemsDictionary.isEmpty, encoding == .json { return try JSONSerialization.data(withJSONObject: queryItemsDictionary, options: .prettyPrinted ) } - // if let fileToUpload { // return getHttpBody(fieldInfo: fileToUpload) // } return nil } + private func addURLQueryParameters(toURL url: URL) -> URL { guard let urlQueryParameters else { return url } diff --git a/Tella/Data/Networking/APICall/HTTPCodes.swift b/Tella/Data/Networking/APICall/HTTPCodes.swift index 26f14b077..143a5c110 100644 --- a/Tella/Data/Networking/APICall/HTTPCodes.swift +++ b/Tella/Data/Networking/APICall/HTTPCodes.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation @@ -11,4 +11,15 @@ typealias HTTPCodes = Range extension HTTPCodes { static let success = 200 ..< 300 } +enum HTTPErrorCodes: Int { + case badRequest = 400 + case unauthorized = 401 + case forbidden = 403 + case notFound = 404 + case methodNotAllowed = 405 + case requestTimeout = 408 + case need2FA = 409 + case internalServerError = 500 + case unknown +} diff --git a/Tella/Data/Networking/APICall/JSONStringEncoder.swift b/Tella/Data/Networking/APICall/JSONStringEncoder.swift index 28f7bc2bb..0a8d04181 100644 --- a/Tella/Data/Networking/APICall/JSONStringEncoder.swift +++ b/Tella/Data/Networking/APICall/JSONStringEncoder.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Data/Networking/APICall/Request.swift b/Tella/Data/Networking/APICall/Request.swift index 1c729e42f..3e41e338b 100644 --- a/Tella/Data/Networking/APICall/Request.swift +++ b/Tella/Data/Networking/APICall/Request.swift @@ -14,9 +14,15 @@ public enum HTTPHeaderField: String { case contentType = "Content-Type" case authorization = "Authorization" case bearer = "Bearer " + case cookie = "Cookie" + case xRequestedWith = "X-Requested-With" case tellaPlatform = "X-Tella-Platform" + case ByPassCaptchaHeader = "Bypass-Captcha" } +public enum XRequestedWithValue: String { + case xmlHttp = "XMLHttpRequest" +} public enum Encoding { case json case form diff --git a/Tella/Data/Networking/APICall/URLRequest.swift b/Tella/Data/Networking/APICall/URLRequest.swift index a90ab8752..523c1cfa8 100644 --- a/Tella/Data/Networking/APICall/URLRequest.swift +++ b/Tella/Data/Networking/APICall/URLRequest.swift @@ -33,9 +33,7 @@ extension WebRepository { } } } - // MARK: - Helpers - extension Publisher where Output == URLSession.DataTaskPublisher.Output { func requestJSON() -> APIResponse where Value: Decodable { return requestData() @@ -43,7 +41,14 @@ extension Publisher where Output == URLSession.DataTaskPublisher.Output { let decodedData : Value = try data.decoded() return (decodedData, allHeaderFields) }) - .mapError{ _ in APIError.unexpectedResponse } + .mapError{ + if let error = $0 as? APIError { + return error + } else { + return APIError.unexpectedResponse + } + + } .eraseToAnyPublisher() } } @@ -54,14 +59,25 @@ extension Publisher where Output == URLSession.DataTaskPublisher.Output { guard let code = ($0.1 as? HTTPURLResponse)?.statusCode else { throw APIError.unexpectedResponse } + guard HTTPCodes.success.contains(code) else { debugLog("Error code: \(code)") throw APIError.httpCode(code) } return ($0.0, ($0.1 as? HTTPURLResponse)?.allHeaderFields) } - .mapError{ error in - return APIError.unexpectedResponse + .mapError{ error in + if let error = error as? APIError { + return error + } else { + let error = error as NSError + if error.domain == NSURLErrorDomain { + return APIError.badServer + } else { + return APIError.httpCode(error._code) + } + + } } .eraseToAnyPublisher() } diff --git a/Tella/Data/Networking/APICall/Utils/MultipartRequest.swift b/Tella/Data/Networking/APICall/Utils/MultipartRequest.swift new file mode 100644 index 000000000..2388ae024 --- /dev/null +++ b/Tella/Data/Networking/APICall/Utils/MultipartRequest.swift @@ -0,0 +1,74 @@ +// +// MultipartRequest.swift +// Tella +// +// Created by Gustavo on 01/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +extension Data { + mutating func append(_ string: String) { + if let data = string.data(using: .utf8) { + append(data) + } + } +} + +public struct MultipartRequest { + public let boundary: String + + private let separator: String = "\r\n" + private var data: Data + + public init(boundary: String = UUID().uuidString) { + self.boundary = boundary + self.data = .init() + } + + private mutating func appendBoundarySeparator() { + data.append("--\(boundary)\(separator)") + } + + private mutating func appendSeparator() { + data.append(separator) + } + + private func disposition(_ key: String) -> String { + "Content-Disposition: form-data; name=\"\(key)\"" + } + //DATA + public mutating func add( + key: String, + value: String + ) { + appendBoundarySeparator() + data.append(disposition(key) + separator) + appendSeparator() + data.append(value + separator) + } + + public mutating func add( + key: String, + fileName: String, + fileMimeType: String, + fileData: Data + ) { + appendBoundarySeparator() + data.append(disposition(key) + "; filename=\"\(fileName)\"" + separator) + data.append("Content-Type: \(fileMimeType)" + separator + separator) + data.append(fileData) + appendSeparator() + } + + public var httpContentTypeHeadeValue: String { + "multipart/form-data; boundary=\(boundary)" + } + + public var httpBody: Data { + var bodyData = data + bodyData.append("--\(boundary)--") + return bodyData + } +} diff --git a/Tella/Data/Networking/Models/DataModel.swift b/Tella/Data/Networking/Models/DataModel.swift index a79720fa5..09d913dec 100644 --- a/Tella/Data/Networking/Models/DataModel.swift +++ b/Tella/Data/Networking/Models/DataModel.swift @@ -4,6 +4,6 @@ import Foundation -protocol DataModel:Codable { +protocol DataModel: Codable { func toDomain() -> DomainModel? } diff --git a/Tella/Data/Networking/Models/EntityCreationResponse.swift b/Tella/Data/Networking/Models/EntityCreationResponse.swift new file mode 100644 index 000000000..e9394315e --- /dev/null +++ b/Tella/Data/Networking/Models/EntityCreationResponse.swift @@ -0,0 +1,121 @@ +// +// EntityCreationResponse.swift +// Tella +// +// Created by Gustavo on 23/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +enum EntityResult { + case publicEntity(Entity) + case authorizedEntity(EntityCreationResponse) +} + +struct EntityCreationResponse: Decodable { + let entity: Entity + let errors: [UwaziError]? +} + + +struct Entity: Decodable { + let id: String + let language: String + let sharedId: String + let title: String + let template: String + let published: Bool + let creationDate: Int64 + let editDate: Int64 + let metadata: [String: [MetaDataItem]] + let user: String? + let permissions: [Permission]? + let __v: Int + let documents: [Attachment]? + let attachments: [Attachment]? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case language + case sharedId + case title + case template + case published + case creationDate + case editDate + case metadata + case user + case permissions + case __v + case documents + case attachments + } +} + +struct MetaDataItem: Decodable { + let value: MetaDataType + let label: String? + + enum MetaDataType { + case stringValue(String) + case intValue(Int) + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if let intValue = try? container.decode(Int.self, forKey: .value) { + self.value = .intValue(intValue) + } else if let stringValue = try? container.decode(String.self, forKey: .value) { + self.value = .stringValue(stringValue) + } else { + throw DecodingError.typeMismatch( + MetaDataType.self, + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected value to be either Int or String" + ) + ) + } + label = try container.decodeIfPresent(String.self, forKey: .label) + } + + private enum CodingKeys: String, CodingKey { + case value, label + } +} + +struct Permission: Decodable { + let refId: String + let type: String + let level: String +} + +struct Attachment: Decodable { + let id: String + let entity: String + let type: String + let filename: String + let originalname: String + let mimetype: String + let size: Int64 + let creationDate: Int64 + + enum CodingKeys: String, CodingKey { + case id = "_id" + case entity + case type + case filename + case originalname + case mimetype + case size + case creationDate + } +} + +struct UwaziError: Codable { + let error: String? + let prettyMessage: String? +} + + diff --git a/Tella/Data/Networking/Models/LoginResult.swift b/Tella/Data/Networking/Models/LoginResult.swift index 2a4f044d8..add5e6090 100644 --- a/Tella/Data/Networking/Models/LoginResult.swift +++ b/Tella/Data/Networking/Models/LoginResult.swift @@ -6,7 +6,6 @@ import Foundation // MARK: - LoginResult - struct LoginResult: Codable { let accessToken: String let user: User diff --git a/Tella/Data/Networking/Models/URLSessionTaskResponse.swift b/Tella/Data/Networking/Models/URLSessionTaskResponse.swift index 6d73cc3f5..7bb054309 100644 --- a/Tella/Data/Networking/Models/URLSessionTaskResponse.swift +++ b/Tella/Data/Networking/Models/URLSessionTaskResponse.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Data/Networking/Models/UwaziCheckURLDTO.swift b/Tella/Data/Networking/Models/UwaziCheckURLDTO.swift new file mode 100644 index 000000000..792b7a3d2 --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziCheckURLDTO.swift @@ -0,0 +1,22 @@ +// +// UwaziCheckURLResult.swift +// Tella +// +// Created by Robert Shrestha on 5/22/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +struct UwaziCheckURLDTO: Codable, DataModel { + let id: String + let siteName: String + + enum CodingKeys: String, CodingKey { + case id = "_id" + case siteName = "site_name" + } + func toDomain() -> DomainModel? { + UwaziCheckURL(id: id, siteName: siteName) + } +} diff --git a/Tella/Data/Networking/Models/UwaziDictionaryDTO.swift b/Tella/Data/Networking/Models/UwaziDictionaryDTO.swift new file mode 100644 index 000000000..1c375679b --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziDictionaryDTO.swift @@ -0,0 +1,53 @@ +// +// UwaziDictionaryResult.swift +// Tella +// +// Created by Robert Shrestha on 8/9/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +struct UwaziDictionaryDTO: Codable { + let rows: [UwaziDictionaryRowDTO]? +} + +class UwaziDictionaryRowDTO: Codable { + let id, name: String? + var values: [SelectValue]? = [] + let version: Int? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case name, values + case version = "__v" + } +} + +class SelectValue: Codable, Equatable, Hashable { + var label, id: String? + var translatedLabel: String? = "" + var values : [NestedSelectValue]? = [] + + init(label: String, id: String?, translatedLabel: String?, values: [NestedSelectValue]) { + self.label = label + self.id = id + self.translatedLabel = translatedLabel + self.values = values + } + + static func == (lhs: SelectValue, rhs: SelectValue) -> Bool { + return lhs.id == rhs.id && lhs.label == rhs.label + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} +class NestedSelectValue: Codable { + var id : String? + var label :String? + var translatedLabel : String? = "" +} + + diff --git a/Tella/Data/Networking/Models/UwaziLanguageDTO.swift b/Tella/Data/Networking/Models/UwaziLanguageDTO.swift new file mode 100644 index 000000000..5e3f45579 --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziLanguageDTO.swift @@ -0,0 +1,60 @@ +// +// UwaziLanguageResult.swift +// Tella +// +// Created by Robert Shrestha on 5/25/23. +// Copyright © 2023 INTERNEWS. All rights reserved. +// + +import Foundation + +// MARK: - Welcome +struct UwaziLanguageDTO: Codable, DataModel { + let rows: [UwaziLanguageRowDTO]? + func toDomain() -> DomainModel? { + let rows = rows?.compactMap{ $0.toDomain() as? UwaziLanguageRow} + return UwaziLanguage(rows: rows) + } +} +// MARK: - Row +struct UwaziLanguageRowDTO: Codable, DataModel { + let id: String? + let locale: String? + let contexts: [UwaziLanguageContextDTO]? + func toDomain() -> DomainModel? { + var languageName = "English" + let currentLocale: Locale = .current + if let locale = self.locale { + languageName = currentLocale.localizedString(forLanguageCode: "\(locale)_\(locale.uppercased())") ?? "English" + } + return UwaziLanguageRow(id: id, locale: locale, languageName: languageName) + } + enum CodingKeys: String, CodingKey { + case id = "_id" + case locale, contexts + } +} +// MARK: - Context +struct UwaziLanguageContextDTO: Codable, DataModel { + let contextID, label: String? + let type: UwaziLanguageTypeEnum? + let values: [String: String]? + let id: String? + + enum CodingKeys: String, CodingKey { + case contextID = "id" + case label + case type, values + case id = "_id" + } + func toDomain() -> DomainModel? { + return UwaziLanguageContext(contextID: contextID, label: label, id: id) + } +} + +enum UwaziLanguageTypeEnum: String, Codable { + case entity = "Entity" + case relationshipType = "Relationship Type" + case thesaurus = "Thesaurus" + case uwaziUI = "Uwazi UI" +} diff --git a/Tella/Data/Networking/Models/UwaziSettingDTO.swift b/Tella/Data/Networking/Models/UwaziSettingDTO.swift new file mode 100644 index 000000000..b15165412 --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziSettingDTO.swift @@ -0,0 +1,29 @@ +// +// UwaziSettingsResult.swift +// Tella +// +// Created by Robert Shrestha on 8/9/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +// MARK: - Welcome +class UwaziSettingDTO: Codable { + let id, siteName: String? + let languages: [UwaziLanguageRowDTO] + let version: Int? + let isPrivate: Bool? + let allowedPublicTemplates: [String] + let mapAPIKey: String? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case siteName = "site_name" + case languages + case version = "__v" + case isPrivate = "private" + case allowedPublicTemplates + case mapAPIKey = "mapApiKey" + } +} diff --git a/Tella/Data/Networking/Models/UwaziTemplateDTO.swift b/Tella/Data/Networking/Models/UwaziTemplateDTO.swift new file mode 100644 index 000000000..097b91d30 --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziTemplateDTO.swift @@ -0,0 +1,92 @@ +// +// UwaziTemplateResult.swift +// Tella +// +// Created by Robert Shrestha on 8/9/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + + +struct UwaziTemplateDTO: Codable { + let rows: [UwaziTemplateRowDTO] +} + +class UwaziTemplateRowDTO: Codable, DataModel { + let id, name: String? + var translatedName: String? = "" + var properties: [PropertyDTO] + var commonProperties: [CommonPropertyDTO] + let version: Int? + let rowDefault: Bool? + let color, entityViewPage: String? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case name, properties, commonProperties + case version = "__v" + case rowDefault = "default" + case color, entityViewPage, translatedName + } + func toDomain() -> DomainModel? { + UwaziTemplateRow(id: id, + name: name, + translatedName: translatedName, + properties: properties.compactMap{$0.toDomain() as? Property}, + commonProperties: commonProperties.compactMap{$0.toDomain() as? CommonProperty}, + version: version, + rowDefault: rowDefault, + entityViewPage: entityViewPage) + } +} + +// MARK: - CommonProperty +class CommonPropertyDTO: Codable, DataModel { + let id, label, name: String? + let isCommonProperty: Bool? + let type: String? + var translatedLabel: String? = "" + let prioritySorting, generatedID: Bool? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case label, name, isCommonProperty, type, prioritySorting, translatedLabel + case generatedID = "generatedId" + } + func toDomain() -> DomainModel? { + CommonProperty(id: id, label: label, name: name, isCommonProperty: isCommonProperty, type: type, translatedLabel: translatedLabel, prioritySorting: prioritySorting, generatedID: generatedID) + } +} + +// MARK: - Property +class PropertyDTO: Codable, DataModel { + let content, id, label, type: String? + let propertyRequired: Bool? + let name: String? + var translatedLabel : String? = "" + let filter, showInCard: Bool? + let relationType: String? + var values : [SelectValue]? + + enum CodingKeys: String, CodingKey { + case content + case id = "_id" + case label, type + case propertyRequired = "required" + case name, filter, showInCard, relationType, translatedLabel + } + func toDomain() -> DomainModel? { + Property(content: content, + id: id, + label: label, + type: type, + propertyRequired: propertyRequired, + name: name, + translatedLabel: translatedLabel, + filter: filter, + showInCard: showInCard, + relationType: relationType, + values: values) + } +} diff --git a/Tella/Data/Networking/Models/UwaziTranslationRowDTO.swift b/Tella/Data/Networking/Models/UwaziTranslationRowDTO.swift new file mode 100644 index 000000000..bd0826cfc --- /dev/null +++ b/Tella/Data/Networking/Models/UwaziTranslationRowDTO.swift @@ -0,0 +1,39 @@ +// +// UwaziTranslationResult.swift +// Tella +// +// Created by Robert Shrestha on 8/9/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +struct UwaziTranslationDTO: Codable { + let rows: [UwaziTranslationRowDTO]? +} + +struct UwaziTranslationRowDTO: Codable { + let id, locale: String? + let contexts: [UwaziTranslationContextDTO] + let version: Int? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case locale, contexts + case version = "__v" + } +} + +// MARK: - Context +struct UwaziTranslationContextDTO: Codable { + let contextID, label: String? + let type: String? + let values: [String: String]? + let id: String? + + enum CodingKeys: String, CodingKey { + case contextID = "id" + case label, type, values + case id = "_id" + } +} diff --git a/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift b/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift new file mode 100644 index 000000000..08cf4b0b9 --- /dev/null +++ b/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift @@ -0,0 +1,413 @@ +// +// UwaziServerRepositories.swift +// Tella +// +// Created by Robert Shrestha on 5/22/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Combine +import Foundation + +class UwaziServerRepository: WebRepository { + var cancellable: AnyCancellable? + func login(username: String, + password: String, + serverURL: String) -> AnyPublisher { + + let apiResponse : APIResponse = getAPIResponse(endpoint: API.login((username: username, password: password, serverURL: serverURL))) + return apiResponse + .tryMap({ (response, allHeaderFields) in + let token = self.handleToken(response: response, allHeaderFields: allHeaderFields) + return token + }) + .mapError {$0 as! APIError} + .eraseToAnyPublisher() + } + + func twoFactorAuthentication(username: String, + password: String, + token: String, + serverURL: String) -> AnyPublisher { + + let apiResponse : APIResponse = getAPIResponse(endpoint: API.twoFactorAuthentication((username: username, password: password,token: token, serverURL: serverURL))) + return apiResponse + .tryMap({ (response, allHeaderFields) in + return self.handleToken(response: response, allHeaderFields: allHeaderFields) + }) + .mapError {$0 as! APIError} + .eraseToAnyPublisher() + } + + private func handleToken(response: BoolResponse, allHeaderFields: [AnyHashable: Any]?) -> String? { + if response.success ?? false { + guard let token = getTokenFromHeader(httpResponse: allHeaderFields) else { return nil } + return token + } else { + return nil + } + } + + private func getTokenFromHeader(httpResponse: [AnyHashable: Any]?) -> String? { + if let token = httpResponse?["Set-Cookie"] as? String { + let filteredToken = token.split(separator: ";") + return filteredToken.first?.replacingOccurrences(of: "connect.sid=", with: "") + } + return nil + } + + func checkServerURL(serverURL: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.checkURL(serverURL: serverURL)) + return apiResponse + .compactMap{$0.0.toDomain() as? UwaziCheckURL} + .eraseToAnyPublisher() + } + + func getLanguage(serverURL: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.getLanguage(serverURL: serverURL)) + return apiResponse + .compactMap{$0.0.toDomain() as? UwaziLanguage} + .eraseToAnyPublisher() + } + + /// Get all the templetes related to the Uwazi server + /// - Parameters: + /// - serverURL: the URL of the server + /// - cookieList: string which consist of token and the locale of the selected uwazi server if public instance then the array is empty + /// - Returns: AnyPublisher with UwaziTemplateResult or APIError if any + func getTemplate(serverURL: String, cookieList: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.getTemplate(serverURL: serverURL, cookieList: cookieList)) + return apiResponse + .compactMap{$0.0} + .eraseToAnyPublisher() + } + /// Get all the setting related to the Uwazi server to determine the whitelisted templates + /// - Parameters: + /// - serverURL: the URL of the server + /// - cookieList: string which consist of token and the locale of the selected uwazi server if public instance then the array is empty + /// - Returns: AnyPublisher with UwaziSettingResult or APIError if any + func getSettings(serverURL: String, cookieList: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.getSetting(serverURL: serverURL, cookieList: cookieList)) + return apiResponse + .compactMap{$0.0} + .eraseToAnyPublisher() + } + /// Get all the options that are related to properties of the template related to the selected Uwazi server + /// - Parameters: + /// - serverURL: the URL of the server + /// - cookieList: string which consist of token and the locale of the selected uwazi server if public instance then the array is empty + /// - Returns: AnyPublisher with UwaziDictionaryResult or APIError if any + func getDictionaries(serverURL: String, cookieList: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.getDictionary(serverURL: serverURL, cookieList: cookieList)) + return apiResponse + .compactMap{$0.0} + .eraseToAnyPublisher() + } + /// Get all the translation of the text related to the selected Uwazi server + /// - Parameters: + /// - serverURL: the URL of the server + /// - cookieList: string which consist of token and the locale of the selected uwazi server if public instance then the array is empty + /// - Returns: AnyPublisher with UwaziTranslationResult or APIError if any + func getTranslations(serverURL: String, cookieList: String) -> AnyPublisher { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.getTranslations(serverURL: serverURL, cookieList: cookieList)) + return apiResponse + .compactMap{$0.0} + .eraseToAnyPublisher() + } + + func getProjetDetails(projectURL: String,token: String) -> AnyPublisher { + let apiResponse : APIResponse = getAPIResponse(endpoint: API.getProjetDetails((projectURL: projectURL, token: token))) + return apiResponse + .compactMap{$0.0.toDomain() as? ProjectAPI } + .eraseToAnyPublisher() + } + + func submitEntity(serverURL: String, cookie: String, multipartHeader: String, multipartBody: Data, isPublic: Bool) -> AnyPublisher { + if isPublic { + let apiResponse: APIResponse = getAPIResponse(endpoint: API.submitPublicEntity(serverURL: serverURL, cookie: cookie, multipartHeader: multipartHeader, multipartBody: multipartBody)) + return apiResponse + .compactMap{ response in + EntityResult.publicEntity(response.0) + } + .eraseToAnyPublisher() + } + + let apiResponse: APIResponse = getAPIResponse(endpoint: API.submitEntity(serverURL: serverURL, cookie: cookie, multipartHeader: multipartHeader, multipartBody: multipartBody)) + return apiResponse + .compactMap{response in + EntityResult.authorizedEntity(response.0) + } + .eraseToAnyPublisher() + } +} +extension UwaziServerRepository { + /// Return a collection of CollectedTemplate which is used to created after all the manpulation is done to prepare the template data for storage + /// - Parameters: + /// - server: Server Object to get the information about the Uwazi server + /// - Returns: Collection of CollectedTemplate + func handleTemplate(server: UwaziServer) -> AnyPublisher<[UwaziTemplateRow], APIError> { + guard let serverURL = server.url else { + return Fail(error: APIError.unexpectedResponse).eraseToAnyPublisher() + } + let cookie = server.cookie ?? "" + let getTemplate = UwaziServerRepository().getTemplate(serverURL: serverURL, cookieList: cookie) + let getSetting = UwaziServerRepository().getSettings(serverURL: serverURL, cookieList: cookie) + let getDictionary = UwaziServerRepository().getDictionaries(serverURL: serverURL, cookieList: cookie) + let getTranslation = UwaziServerRepository().getTranslations(serverURL: serverURL, cookieList: cookie) + + return Publishers.Zip4( + getTemplate, + getSetting, + getDictionary, + getTranslation + ) + .tryMap({ (templateResult, settings, dictionary, translationResult) in + let templates = templateResult.rows + let translations = translationResult.rows + let dictionary = dictionary.rows + let settings: UwaziSettingDTO = settings + + // Maps the options to the property of the template + self.handleMapping(templates, dictionary) + // Check whether the server instance is public and if public then only use the whitelisted templates are added to resultTemplates + let resultTemplates = self.getAllowedTemplates(server: server, settings: settings, templates: templates) + self.translate(locale: server.locale ?? "", resultTemplates: resultTemplates, translations: translations) + return resultTemplates.compactMap({$0.toDomain() as? UwaziTemplateRow}) + }) + .mapError{$0 as! APIError} + .eraseToAnyPublisher() + } + + fileprivate func getAllowedTemplates(server: UwaziServer, settings: UwaziSettingDTO, templates: [UwaziTemplateRowDTO]?) -> [UwaziTemplateRowDTO] { + // Check whether the server instance is public and if public then only use the whitelisted templates are added to resultTemplates + var tempTemplates: [UwaziTemplateRowDTO] = [] + if !self.isPublic(server: server) { + tempTemplates = templates ?? [] + } else { + if !settings.allowedPublicTemplates.isEmpty { + templates?.forEach { row in + settings.allowedPublicTemplates.forEach { id in + if row.id == id { + tempTemplates.append(row) + } + } + } + } + } + return tempTemplates + } + + fileprivate func isPublic(server: UwaziServer) -> Bool { + return (server.username?.isEmpty ?? true) || (server.password?.isEmpty ?? true) + } + + // Maps the options to the property of the template + fileprivate func handleMapping(_ templates: [UwaziTemplateRowDTO]?, _ dictionary: [UwaziDictionaryRowDTO]?) { + templates?.forEach { template in + template.properties.forEach { property in + if let dictionaryItem = dictionary?.first(where: { $0.id == property.content }) { + property.values = dictionaryItem.values + } + } + } + } + + fileprivate func translate(locale: String, resultTemplates: [UwaziTemplateRowDTO], translations: [UwaziTranslationRowDTO]?) { + resultTemplates.forEach { template in + // Get only the translations based on the language that user selected + let filteredTranslations = translations?.filter{$0.locale == locale} + filteredTranslations?.first?.contexts.forEach{ context in + // Compare context id and template id to determine appropiate translations + if context.contextID == template.id { + handleTranslationForTemplate(template: template, context: context) + } else { + handleTranslationForOtherProperties(template: template, context: context) + } + } + } + } + + fileprivate func handleTranslationForTemplate(template: UwaziTemplateRowDTO, context: UwaziTranslationContextDTO) { + // Translation for the template name + template.translatedName = context.values?[template.name ?? ""] ?? "" + // Translation for the template's property texts or labels + template.properties.forEach { property in + property.translatedLabel = context.values?[property.label ?? ""] ?? "" + } + // Translation for the template's common properties texts or labels + template.commonProperties.forEach { property in + property.translatedLabel = context.values?[property.label ?? ""] ?? "" + } + } + + fileprivate func handleTranslationForOtherProperties(template: UwaziTemplateRowDTO, context: UwaziTranslationContextDTO) { + // Translation for the template's property options texts or labels if there is any + template.properties.forEach { property in + property.values?.forEach { selectValue in + if context.contextID == property.content { + selectValue.translatedLabel = context.values?[selectValue.label ?? ""] ?? selectValue.label + } + // Translation for the template's property option's options texts or labels if there is any + selectValue.values?.forEach { nestedSelectValue in + if context.id == property.content { + nestedSelectValue.translatedLabel = context.values?[nestedSelectValue.label ?? ""] ?? nestedSelectValue.label + } + } + } + } + } +} + +extension UwaziServerRepository { + enum API { + case login((username: String, + password: String, + serverURL: String)) + + case getProjetDetails((projectURL:String, token: String)) + case checkURL(serverURL: String) + case getLanguage(serverURL: String) + case twoFactorAuthentication((username: String, + password: String, + token: String, + serverURL: String)) + case getTemplate(serverURL: String, cookieList:String) + case getSetting(serverURL: String, cookieList:String) + case getDictionary(serverURL: String, cookieList:String) + case getTranslations(serverURL: String, cookieList:String) + case submitEntity(serverURL: String, cookie: String, multipartHeader: String, multipartBody: Data) + case submitPublicEntity(serverURL: String, cookie: String, multipartHeader: String, multipartBody: Data) + } +} + +extension UwaziServerRepository.API: APIRequest { + typealias Value = Any + var token: String? { + switch self { + case .login, .twoFactorAuthentication: + return nil + case .getProjetDetails((_, let token)): + return token + case .checkURL, .getLanguage: + return nil + case .getTemplate,.getSetting, .getDictionary, .getTranslations, .submitEntity, .submitPublicEntity: + return nil + } + } + + var headers: [String: String]? { + switch self { + case .login, .getProjetDetails, .checkURL, .getLanguage, .twoFactorAuthentication: + return [HTTPHeaderField.contentType.rawValue : ContentType.json.rawValue] + case .getTemplate(_,let cookieList), .getSetting(_,let cookieList), .getDictionary(_,let cookieList), .getTranslations(_,let cookieList): + return [HTTPHeaderField.cookie.rawValue: cookieList, + HTTPHeaderField.contentType.rawValue : ContentType.json.rawValue] + case .submitEntity(_, let cookie, _, _): + return [HTTPHeaderField.cookie.rawValue: cookie, + HTTPHeaderField.xRequestedWith.rawValue: XRequestedWithValue.xmlHttp.rawValue, + HTTPHeaderField.contentType.rawValue : ContentType.data.rawValue ] + case .submitPublicEntity(_, let cookie, _, _): + return [HTTPHeaderField.cookie.rawValue: cookie, + HTTPHeaderField.xRequestedWith.rawValue: XRequestedWithValue.xmlHttp.rawValue, + HTTPHeaderField.contentType.rawValue: ContentType.data.rawValue, + HTTPHeaderField.ByPassCaptchaHeader.rawValue: "true" + ] + } + } + + var encoding: Encoding { + switch self { + case .submitEntity, .submitPublicEntity: + return Encoding.form + default: + return Encoding.json + } + } + + var keyValues: [Key : Value?]? { + + switch self { + case .login((let username, let password, _ )): + return [ + "username": username, + "password": password + ] + case .getProjetDetails(_): + return nil + case .twoFactorAuthentication((let username, let password, let token, _)): + return [ + "username": username, + "password": password, + "token": token + ] + case .checkURL, .getLanguage, .getTemplate, .getSetting,.getDictionary,.getTranslations, .submitEntity(_, _, _, _), .submitPublicEntity: + return nil + } + } + + var multipartBody: Data? { + switch self { + case .submitEntity(_, _, _, let multipartBody), .submitPublicEntity(_, _, _, let multipartBody): + return multipartBody + default: + return nil + } + } + + var multipartHeader: String? { + switch self { + case .submitEntity(_, _, let multipartHeader, _), .submitPublicEntity(_, _, let multipartHeader, _): + return multipartHeader + default: + return nil + } + } + var baseURL: String { + switch self { + case .login((_, _, let serverURL)), .twoFactorAuthentication((_,_,_, let serverURL)): + return serverURL + case .getProjetDetails((let projectURL, _)): + return projectURL + case .checkURL(serverURL: let serverURL) ,.getLanguage(serverURL: let serverURL): + return serverURL + case .getTemplate(serverURL: let serverURL, cookieList: _): + return serverURL + case .getSetting(serverURL: let serverURL, cookieList: _), .getDictionary(serverURL: let serverURL, cookieList: _),.getTranslations(serverURL: let serverURL, cookieList: _), .submitEntity(serverURL: let serverURL, _, _, _), .submitPublicEntity(let serverURL, _, _, _): + return serverURL + } + } + + var path: String { + switch self { + case .login, .twoFactorAuthentication: + return "/api/login" + case .getProjetDetails(_): + return "" + case .checkURL,.getSetting: + return "/api/settings" + case .getLanguage: + return "/api/translations" + case .getTemplate: + return "/api/templates" + case .getDictionary: + return "/api/dictionaries" + case .getTranslations: + return "/api/translations" + case .submitEntity: + return "/api/entities" + case .submitPublicEntity: + return "/api/public" + } + } + + var httpMethod: HTTPMethod { + switch self { + case .login, .twoFactorAuthentication, .submitEntity, .submitPublicEntity: + return HTTPMethod.post + case .getProjetDetails(_): + return HTTPMethod.get + case .checkURL, .getLanguage, .getSetting, .getTemplate,.getDictionary,.getTranslations: + return HTTPMethod.get + } + } +} diff --git a/Tella/Data/Networking/Upload/AutoUpload.swift b/Tella/Data/Networking/Upload/AutoUpload.swift index 1c4e3944d..04c614cb1 100644 --- a/Tella/Data/Networking/Upload/AutoUpload.swift +++ b/Tella/Data/Networking/Upload/AutoUpload.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Data/Networking/Upload/BaseUploadOperation.swift b/Tella/Data/Networking/Upload/BaseUploadOperation.swift index 57a8572e1..f964c55a0 100644 --- a/Tella/Data/Networking/Upload/BaseUploadOperation.swift +++ b/Tella/Data/Networking/Upload/BaseUploadOperation.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Data/Networking/Upload/UploadReportOperation.swift b/Tella/Data/Networking/Upload/UploadReportOperation.swift index 68ab18c69..e66207be2 100644 --- a/Tella/Data/Networking/Upload/UploadReportOperation.swift +++ b/Tella/Data/Networking/Upload/UploadReportOperation.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Data/VaultManager/VaultFilesManager.swift b/Tella/Data/VaultManager/VaultFilesManager.swift index 6f41bca38..8032e3273 100644 --- a/Tella/Data/VaultManager/VaultFilesManager.swift +++ b/Tella/Data/VaultManager/VaultFilesManager.swift @@ -196,11 +196,11 @@ class VaultFilesManager : VaultFilesManagerInterface { func deleteVaultFile(vaultFiles : [VaultFileDB]) -> Result? { var resultFiles : [VaultFileDB] = [] - let fileWalker = FileWalker(vaultDatabase: self.vaultDataBase) + let fileWalker = FileWalker(vaultDatabase: self.vaultDataBase) vaultFiles.forEach { file in if file.type == .directory { - resultFiles.append(contentsOf: fileWalker.walkWithDirectories(root: file)) + resultFiles.append(contentsOf: fileWalker.walkWithDirectories(root: file)) } resultFiles.append(file) } diff --git a/Tella/Domain/Entity/Report/FileAPI.swift b/Tella/Domain/Entity/Report/FileAPI.swift index d23f297bb..56ab8205a 100644 --- a/Tella/Domain/Entity/Report/FileAPI.swift +++ b/Tella/Domain/Entity/Report/FileAPI.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/FileStatus.swift b/Tella/Domain/Entity/Report/FileStatus.swift index b9d0dd969..226e02ebc 100644 --- a/Tella/Domain/Entity/Report/FileStatus.swift +++ b/Tella/Domain/Entity/Report/FileStatus.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/FileToUpload.swift b/Tella/Domain/Entity/Report/FileToUpload.swift index 8ce79e5b3..1866848b4 100644 --- a/Tella/Domain/Entity/Report/FileToUpload.swift +++ b/Tella/Domain/Entity/Report/FileToUpload.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/Project.swift b/Tella/Domain/Entity/Report/Project.swift index 9a2ad3c05..8d789974a 100644 --- a/Tella/Domain/Entity/Report/Project.swift +++ b/Tella/Domain/Entity/Report/Project.swift @@ -12,12 +12,7 @@ class Server : Hashable { var username : String? var password : String? var accessToken : String? - var activatedMetadata : Bool? - var backgroundUpload : Bool? - var projectId : String? - var slug : String? - var autoUpload: Bool? - var autoDelete: Bool? + var serverType: ServerConnectionType? init(id: Int? = nil, name: String? = nil, @@ -25,24 +20,15 @@ class Server : Hashable { username: String? = nil, password: String? = nil, accessToken: String? = nil, - activatedMetadata: Bool? = nil, - backgroundUpload: Bool? = nil, - projectId: String? = nil, - slug: String? = nil, - autoUpload: Bool?, - autoDelete: Bool) { + serverType: ServerConnectionType? = nil + ) { self.id = id self.name = name self.url = serverURL self.username = username self.password = password self.accessToken = accessToken - self.activatedMetadata = activatedMetadata - self.backgroundUpload = backgroundUpload - self.projectId = projectId - self.slug = slug - self.autoUpload = autoUpload - self.autoDelete = autoDelete + self.serverType = serverType } init() { @@ -56,5 +42,42 @@ class Server : Hashable { func hash(into hasher: inout Hasher){ hasher.combine(id.hashValue) } - +} + +class TellaServer : Server { + var activatedMetadata : Bool? + var backgroundUpload : Bool? + var projectId : String? + var slug : String? + var autoUpload: Bool? + var autoDelete: Bool? + init(id: Int? = nil, + name: String? = nil, + serverURL: String? = nil, + username: String? = nil, + password: String? = nil, + accessToken: String? = nil, + activatedMetadata: Bool? = nil, + backgroundUpload: Bool? = nil, + projectId: String? = nil, + slug: String? = nil, + autoUpload: Bool, + autoDelete: Bool, + serverType: ServerConnectionType? = .tella + ) { + self.activatedMetadata = activatedMetadata + self.backgroundUpload = backgroundUpload + self.projectId = projectId + self.slug = slug + self.autoUpload = autoUpload + self.autoDelete = autoDelete + super.init(id: id, + name: name, + serverURL: serverURL, + username: username, + password: password, + accessToken: accessToken, + serverType: serverType + ) + } } diff --git a/Tella/Domain/Entity/Report/Report.swift b/Tella/Domain/Entity/Report/Report.swift index f1bed1e59..8a3547c2c 100644 --- a/Tella/Domain/Entity/Report/Report.swift +++ b/Tella/Domain/Entity/Report/Report.swift @@ -13,7 +13,7 @@ class Report : Hashable { var createdDate : Date? var updatedDate : Date? var status : ReportStatus? - var server : Server? + var server : TellaServer? var reportFiles : [ReportFile]? var apiID : String? var currentUpload: Bool? @@ -24,7 +24,7 @@ class Report : Hashable { createdDate: Date? = nil, updatedDate: Date? = nil, status: ReportStatus? = nil, - server: Server? = nil, + server: TellaServer? = nil, vaultFiles: [ReportFile]? = nil, apiID: String? = nil, currentUpload: Bool? = nil ) { diff --git a/Tella/Domain/Entity/Report/ReportActionType.swift b/Tella/Domain/Entity/Report/ReportActionType.swift index 8c8bd34b8..ef0c284f3 100644 --- a/Tella/Domain/Entity/Report/ReportActionType.swift +++ b/Tella/Domain/Entity/Report/ReportActionType.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/ReportFile.swift b/Tella/Domain/Entity/Report/ReportFile.swift index 6d9346769..ed0d8a045 100644 --- a/Tella/Domain/Entity/Report/ReportFile.swift +++ b/Tella/Domain/Entity/Report/ReportFile.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/ReportVaultFile.swift b/Tella/Domain/Entity/Report/ReportVaultFile.swift index 34326390e..beb3fe7c3 100644 --- a/Tella/Domain/Entity/Report/ReportVaultFile.swift +++ b/Tella/Domain/Entity/Report/ReportVaultFile.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/ServerFileSize.swift b/Tella/Domain/Entity/Report/ServerFileSize.swift index 841d046bd..3dd19211c 100644 --- a/Tella/Domain/Entity/Report/ServerFileSize.swift +++ b/Tella/Domain/Entity/Report/ServerFileSize.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Domain/Entity/Report/ServerType.swift b/Tella/Domain/Entity/Report/ServerType.swift index fc437cb19..b7d4b5a23 100644 --- a/Tella/Domain/Entity/Report/ServerType.swift +++ b/Tella/Domain/Entity/Report/ServerType.swift @@ -4,9 +4,17 @@ import Foundation -enum ServerType: String { - case unknown = "UNKNOWN" - case odkCollect = "ODK_COLLECT" - case tellaUpload = "TELLA_UPLOAD" - case uwazi = "UWAZI" +enum ServerConnectionType: Int, Decodable { + case tella = 0 + case uwazi = 1 + case odkCollect = 3 + case unknown +} + +func mapServerTypeFromInt(_ serverTypeInt: Int?) -> ServerConnectionType { + if let serverType = serverTypeInt { + return ServerConnectionType(rawValue: serverType) ?? .unknown + } else { + return .unknown + } } diff --git a/Tella/Domain/Entity/Report/UwaziLocale.swift b/Tella/Domain/Entity/Report/UwaziLocale.swift new file mode 100644 index 000000000..76abb3511 --- /dev/null +++ b/Tella/Domain/Entity/Report/UwaziLocale.swift @@ -0,0 +1,38 @@ +// +// UwaziLocale.swift +// Tella +// +// Created by Robert Shrestha on 7/21/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziLocale : Hashable, Codable { + + var id : Int? + var locale : String? + var serverId : Int? + + init(id: Int? = nil, + locale: String? = nil, + serverId: Int? = nil + ) { + self.id = id + self.locale = locale + self.serverId = serverId + } + enum CodingKeys: String, CodingKey { + case id = "c_locale_id" + case locale = "c_locale" + case serverId = "c_server_id" + } + + static func == (lhs: UwaziLocale, rhs: UwaziLocale) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id.hashValue) + } +} diff --git a/Tella/Domain/Entity/Uwazi/CollectedTemplate.swift b/Tella/Domain/Entity/Uwazi/CollectedTemplate.swift new file mode 100644 index 000000000..2d97e2bcd --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/CollectedTemplate.swift @@ -0,0 +1,91 @@ +// +// CollectedTemplate.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +// Made Codable to Parse this object to and from Database so not using DTO pattern here +class CollectedTemplate: Codable, Hashable { + var id: Int? + var templateId: String? + var serverId: Int? + var serverName: String? + var username: String? + var entityRow: UwaziTemplateRow? + var isDownloaded: Bool? + var isFavorite: Bool? + var isUpdated: Bool? + + init(id: Int? = nil, + serverId: Int?, + templateId: String?, + serverName: String? = nil, + username: String? = nil, + entityRow: UwaziTemplateRow, + isDownloaded: Bool? = nil, + isFavorite: Bool? = nil, + isUpdated: Bool? = nil) { + + self.id = id + self.templateId = templateId + self.serverId = serverId + self.serverName = serverName + self.username = username + self.entityRow = entityRow + self.isDownloaded = isDownloaded + self.isFavorite = isFavorite + self.isUpdated = isUpdated + } + + enum CodingKeys: String, CodingKey { + case id = "c_id" + case templateId = "c_template_id" + case serverId = "c_server_id" + case serverName = "c_server_name" + case username + case entityRow = "c_entity" + case isDownloaded = "c_downloaded" + case isFavorite = "c_favorite" + case isUpdated = "c_updated" + } + + static func == (lhs: CollectedTemplate, rhs: CollectedTemplate) -> Bool { + return (lhs.id == rhs.id) && (lhs.templateId == rhs.templateId) + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + id = try container.decodeIfPresent(Int.self, forKey: .id) + templateId = try container.decodeIfPresent(String.self, forKey: .templateId) + serverId = try container.decodeIfPresent(Int.self, forKey: .serverId) + serverName = try container.decodeIfPresent(String.self, forKey: .serverName) + username = try container.decodeIfPresent(String.self, forKey: .username) + // Decode entityRow as a string + if let entityRowString = try container.decodeIfPresent(String.self, forKey: .entityRow) { + if let jsonData = entityRowString.data(using: .utf8), let array = try? JSONDecoder().decode(UwaziTemplateRow.self, from: jsonData) { + entityRow = array + } else { + throw DecodingError.dataCorruptedError(forKey: .entityRow, in: container, debugDescription: "entityRow issue") + } + } + isDownloaded = try container.decodeIfPresent(Int.self, forKey: .isDownloaded) == 1 ? true : false + isFavorite = try container.decodeIfPresent(Int.self, forKey: .isFavorite) == 1 ? true : false + isUpdated = try container.decodeIfPresent(Int.self, forKey: .isUpdated) == 1 ? true : false + } + // TODO: Ask if this also need to be changed + var entityRowString: String? { + if let jsonData = try? JSONEncoder().encode(entityRow), let json = String(bytes: jsonData, encoding: .utf8) { + return json + } else { + return nil + } + } + + public func hash(into hasher: inout Hasher) { + return hasher.combine(templateId) + } +} diff --git a/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionary.swift b/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionary.swift new file mode 100644 index 000000000..33b8ca468 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionary.swift @@ -0,0 +1,15 @@ +// +// UwaziDictionary.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziDictionary: DomainModel { + let rows: [UwaziDictionaryRow]? + init(rows: [UwaziDictionaryRow]?) { + self.rows = rows + } +} diff --git a/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionaryRow.swift b/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionaryRow.swift new file mode 100644 index 000000000..bbbd4cf26 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Dictionary/UwaziDictionaryRow.swift @@ -0,0 +1,18 @@ +// +// UwaziDictionaryRow.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziDictionaryRow: DomainModel { + let id, name: String? + var values: [SelectValue]? = [] + init(id: String?, name: String?, values: [SelectValue]?) { + self.id = id + self.name = name + self.values = values + } +} diff --git a/Tella/Domain/Entity/Uwazi/Language/UwaziLanguage.swift b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguage.swift new file mode 100644 index 000000000..8c632103a --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguage.swift @@ -0,0 +1,16 @@ +// +// UwaziLanguageAPI.swift +// Tella +// +// Created by Robert Shrestha on 8/31/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziLanguage: DomainModel { + let rows: [UwaziLanguageRow]? + init(rows: [UwaziLanguageRow]?) { + self.rows = rows + } +} diff --git a/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageContext.swift b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageContext.swift new file mode 100644 index 000000000..89bf9a5e9 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageContext.swift @@ -0,0 +1,23 @@ +// +// UwaziLanguageContext.swift +// Tella +// +// Created by Robert Shrestha on 9/5/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziLanguageContext: DomainModel { + let contextID, label: String? + let id: String? + + init(contextID: String? = nil, + label: String? = nil, + id: String? = nil) { + self.contextID = contextID + self.label = label + self.id = id + + } +} diff --git a/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageRow.swift b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageRow.swift new file mode 100644 index 000000000..f40f4eed7 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Language/UwaziLanguageRow.swift @@ -0,0 +1,29 @@ +// +// UwaziLanguageRow.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziLanguageRow: DomainModel, Hashable { + let id: String? + let locale: String? + let languageName: String + + init(id: String?, + locale: String?, + languageName: String) { + self.id = id + self.locale = locale + self.languageName = languageName + + } + static func == (lhs: UwaziLanguageRow, rhs: UwaziLanguageRow) -> Bool { + lhs.locale == rhs.locale + } + func hash(into hasher: inout Hasher) { + hasher.combine(locale) + } +} diff --git a/Tella/Domain/Entity/Uwazi/Template/UwaziCommonProperty.swift b/Tella/Domain/Entity/Uwazi/Template/UwaziCommonProperty.swift new file mode 100644 index 000000000..d8ee283a8 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Template/UwaziCommonProperty.swift @@ -0,0 +1,27 @@ +// +// UwaziCommonProperty.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class CommonProperty: DomainModel, Codable { + let id, label, name: String? + let isCommonProperty: Bool? + let type: String? + var translatedLabel: String? = "" + let prioritySorting, generatedID: Bool? + + init(id: String?, label: String?, name: String?, isCommonProperty: Bool?, type: String?, translatedLabel: String?, prioritySorting: Bool?, generatedID: Bool?) { + self.id = id + self.label = label + self.name = name + self.isCommonProperty = isCommonProperty + self.type = type + self.translatedLabel = translatedLabel + self.prioritySorting = prioritySorting + self.generatedID = generatedID + } +} diff --git a/Tella/Domain/Entity/Uwazi/Template/UwaziProperty.swift b/Tella/Domain/Entity/Uwazi/Template/UwaziProperty.swift new file mode 100644 index 000000000..062797b8f --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Template/UwaziProperty.swift @@ -0,0 +1,32 @@ +// +// UwaziProperty.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class Property: DomainModel, Codable{ + let content, id, label, type: String? + let propertyRequired: Bool? + let name: String? + var translatedLabel : String? = "" + let filter, showInCard: Bool? + let relationType: String? + var values : [SelectValue]? = nil + + init(content: String?, id: String?, label: String?, type: String?, propertyRequired: Bool?, name: String?, translatedLabel: String?, filter: Bool?, showInCard: Bool?, relationType: String?, values: [SelectValue]?) { + self.content = content + self.id = id + self.label = label + self.type = type + self.propertyRequired = propertyRequired + self.name = name + self.translatedLabel = translatedLabel + self.filter = filter + self.showInCard = showInCard + self.relationType = relationType + self.values = values + } +} diff --git a/Tella/Domain/Entity/Uwazi/Template/UwaziSetting.swift b/Tella/Domain/Entity/Uwazi/Template/UwaziSetting.swift new file mode 100644 index 000000000..eb0a984ee --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Template/UwaziSetting.swift @@ -0,0 +1,19 @@ +// +// UwaziSetting.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziSetting: DomainModel { + let id: String? + let allowedPublicTemplates: [String] + let mapAPIKey: String? + init(id: String?, allowedPublicTemplates: [String], mapAPIKey: String?) { + self.id = id + self.allowedPublicTemplates = allowedPublicTemplates + self.mapAPIKey = mapAPIKey + } +} diff --git a/Tella/Domain/Entity/Uwazi/Template/UwaziTemplate.swift b/Tella/Domain/Entity/Uwazi/Template/UwaziTemplate.swift new file mode 100644 index 000000000..8362553d9 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Template/UwaziTemplate.swift @@ -0,0 +1,15 @@ +// +// UwaziTemplate.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziTemplate: DomainModel { + let rows: [UwaziTemplateRow]? + init(rows: [UwaziTemplateRow]?) { + self.rows = rows + } +} diff --git a/Tella/Domain/Entity/Uwazi/Template/UwaziTemplateRow.swift b/Tella/Domain/Entity/Uwazi/Template/UwaziTemplateRow.swift new file mode 100644 index 000000000..6fb5147e9 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Template/UwaziTemplateRow.swift @@ -0,0 +1,38 @@ +// +// UwaziTemplateRow.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +// TODO: Removed unwanted variables when done with entity flow no removed now due to confusion on what will be need in future implementation +// We save this object in the database so it is confronting to Codable +class UwaziTemplateRow: DomainModel, Codable { + let id, name: String? + var translatedName: String? = "" + var properties: [Property] + var commonProperties: [CommonProperty] + let version: Int? + let rowDefault: Bool? + let entityViewPage: String? + + enum CodingKeys: String, CodingKey { + case id = "_id" + case name, properties, commonProperties + case version = "__v" + case rowDefault = "default" + case entityViewPage, translatedName + } + init(id: String?, name: String?, translatedName: String?, properties: [Property], commonProperties: [CommonProperty], version: Int?, rowDefault: Bool?, entityViewPage: String?) { + self.id = id + self.name = name + self.translatedName = translatedName + self.properties = properties + self.commonProperties = commonProperties + self.version = version + self.rowDefault = rowDefault + self.entityViewPage = entityViewPage + } +} diff --git a/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslation.swift b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslation.swift new file mode 100644 index 000000000..ada218a2f --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslation.swift @@ -0,0 +1,15 @@ +// +// UwaziTranslation.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziTranslation: DomainModel { + let rows: [UwaziTranslationRow]? + init(rows: [UwaziTranslationRow]?) { + self.rows = rows + } +} diff --git a/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationContext.swift b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationContext.swift new file mode 100644 index 000000000..0823ba67d --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationContext.swift @@ -0,0 +1,22 @@ +// +// TranslationContext.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziTranslationContext: DomainModel { + let contextID: String? + let values: [String: String]? + let id: String? + init(contextID: String?, + values: [String : String]?, + id: String?) { + self.contextID = contextID + self.values = values + self.id = id + } +} diff --git a/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationRow.swift b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationRow.swift new file mode 100644 index 000000000..9da175cb0 --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/Translation/UwaziTranslationRow.swift @@ -0,0 +1,20 @@ +// +// UwaziTranslationRow.swift +// Tella +// +// Created by Robert Shrestha on 9/8/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziTranslationRow: DomainModel { + let id, locale: String? + let contexts: [UwaziTranslationContext] + init(id: String?, + locale: String?, + contexts: [UwaziTranslationContext]) { + self.id = id + self.locale = locale + self.contexts = contexts + } +} diff --git a/Tella/Domain/Entity/Uwazi/UwaziCheckURL.swift b/Tella/Domain/Entity/Uwazi/UwaziCheckURL.swift new file mode 100644 index 000000000..2d363816c --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/UwaziCheckURL.swift @@ -0,0 +1,19 @@ +// +// UwaziCheckURL.swift +// Tella +// +// Created by Robert Shrestha on 9/5/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziCheckURL: DomainModel { + var id: String? + var siteName: String? + + init(id: String?, siteName: String?) { + self.id = id + self.siteName = siteName + } +} diff --git a/Tella/Domain/Entity/Uwazi/UwaziServer.swift b/Tella/Domain/Entity/Uwazi/UwaziServer.swift new file mode 100644 index 000000000..fec84a43f --- /dev/null +++ b/Tella/Domain/Entity/Uwazi/UwaziServer.swift @@ -0,0 +1,32 @@ +// +// UwaziServer.swift +// Tella +// +// Created by Gustavo on 13/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class UwaziServer : Server { + var locale: String? + var cookie: String? + init(id: Int? = nil, + name: String? = nil, + serverURL: String? = nil, + username: String? = nil, + password: String? = nil, + accessToken: String? = nil, + locale: String? = nil, + serverType: ServerConnectionType? = .uwazi + ) { + self.locale = locale + super.init(id: id, name: name, serverURL: serverURL, username: username, password: password, accessToken: accessToken, serverType: serverType) + self.cookie = createCookie() + } + private func createCookie() -> String { + let accessTokenValue = accessToken ?? "" + let localeValue = locale ?? "" + return "connect.sid=\(accessTokenValue);locale=\(localeValue)" + } +} diff --git a/Tella/Domain/Entity/UwaziEntity/UwaziEntryPrompt.swift b/Tella/Domain/Entity/UwaziEntity/UwaziEntryPrompt.swift new file mode 100644 index 000000000..001a35290 --- /dev/null +++ b/Tella/Domain/Entity/UwaziEntity/UwaziEntryPrompt.swift @@ -0,0 +1,61 @@ +// +// UwaziEntryPrompt.swift +// Tella +// +// Created by Robert Shrestha on 9/12/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import SwiftUI + +class UwaziEntryPrompt: Hashable, ObservableObject { + var id: String? + let formIndex: String? + let type: String + var question: String + var answer: String? + let required: Bool? + let readonly = false + let helpText: String? + var selectValues: [SelectValue]? + @Published var showMandatoryError: Bool + @Published var value: UwaziValue + let name: String? + @Published var showClear: Bool? + + + init(id: String, + formIndex: String?, + type: String, + question: String, + answer: String? = nil, + required: Bool?, + helpText: String?, + selectValues: [SelectValue]? = nil, + showMandatoryError: Bool = false, + value: UwaziValue = UwaziValue.defaultValue(), + name: String?, + showClear: Bool = false + ) { + self.id = id + self.formIndex = formIndex + self.type = type + self.question = question + self.answer = answer + self.required = required + self.helpText = helpText + self.selectValues = selectValues + self.showMandatoryError = showMandatoryError + self.value = value + self.name = name + self.showClear = showClear + } + static func == (lhs: UwaziEntryPrompt, rhs: UwaziEntryPrompt) -> Bool { + lhs.id == rhs.id + } + public func hash(into hasher: inout Hasher) { + return hasher.combine(id) + } +} + diff --git a/Tella/Domain/Entity/UwaziEntity/UwaziValue.swift b/Tella/Domain/Entity/UwaziEntity/UwaziValue.swift new file mode 100644 index 000000000..df8363e2d --- /dev/null +++ b/Tella/Domain/Entity/UwaziEntity/UwaziValue.swift @@ -0,0 +1,20 @@ +// +// UwaziValue.swift +// Tella +// +// Created by Robert Shrestha on 9/13/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziValue: ObservableObject { + @Published var stringValue: String + @Published var selectedValue: [SelectValue] + init(stringValue: String, selectedValue: [SelectValue]) { + self.stringValue = stringValue + self.selectedValue = selectedValue + } + static func defaultValue() -> UwaziValue { + return UwaziValue(stringValue: "", selectedValue: []) + } +} diff --git a/Tella/NewAssets.xcassets/arrow.clockwise.imageset/Contents.json b/Tella/NewAssets.xcassets/arrow.clockwise.imageset/Contents.json new file mode 100644 index 000000000..ffe0408fb --- /dev/null +++ b/Tella/NewAssets.xcassets/arrow.clockwise.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "circular-arrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/arrow.clockwise.imageset/circular-arrow.png b/Tella/NewAssets.xcassets/arrow.clockwise.imageset/circular-arrow.png new file mode 100644 index 000000000..4cee20938 Binary files /dev/null and b/Tella/NewAssets.xcassets/arrow.clockwise.imageset/circular-arrow.png differ diff --git a/Tella/NewAssets.xcassets/home.uwazi.imageset/Contents.json b/Tella/NewAssets.xcassets/home.uwazi.imageset/Contents.json new file mode 100644 index 000000000..fe2ff8424 --- /dev/null +++ b/Tella/NewAssets.xcassets/home.uwazi.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/home.uwazi.imageset/uwazi.svg b/Tella/NewAssets.xcassets/home.uwazi.imageset/uwazi.svg new file mode 100644 index 000000000..dba0e1633 --- /dev/null +++ b/Tella/NewAssets.xcassets/home.uwazi.imageset/uwazi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Tella/NewAssets.xcassets/select.arrow.up.imageset/Contents.json b/Tella/NewAssets.xcassets/select.arrow.up.imageset/Contents.json new file mode 100644 index 000000000..fd576f868 --- /dev/null +++ b/Tella/NewAssets.xcassets/select.arrow.up.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "select.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/select.arrow.up.imageset/select.svg b/Tella/NewAssets.xcassets/select.arrow.up.imageset/select.svg new file mode 100644 index 000000000..c389ee0ea --- /dev/null +++ b/Tella/NewAssets.xcassets/select.arrow.up.imageset/select.svg @@ -0,0 +1,3 @@ + + + diff --git a/Tella/NewAssets.xcassets/template.add.imageset/Contents.json b/Tella/NewAssets.xcassets/template.add.imageset/Contents.json new file mode 100644 index 000000000..d81af2de3 --- /dev/null +++ b/Tella/NewAssets.xcassets/template.add.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "template-add.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/template.add.imageset/template-add.svg b/Tella/NewAssets.xcassets/template.add.imageset/template-add.svg new file mode 100644 index 000000000..c4a90b9be --- /dev/null +++ b/Tella/NewAssets.xcassets/template.add.imageset/template-add.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Tella/NewAssets.xcassets/uwazi.add-files.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.add-files.imageset/Contents.json new file mode 100644 index 000000000..b289e64ee --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.add-files.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi-file-more.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.add-files.imageset/uwazi-file-more.svg b/Tella/NewAssets.xcassets/uwazi.add-files.imageset/uwazi-file-more.svg new file mode 100644 index 000000000..b47a15eb5 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.add-files.imageset/uwazi-file-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/Tella/NewAssets.xcassets/uwazi.cancel.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.cancel.imageset/Contents.json new file mode 100644 index 000000000..a36aad626 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.cancel.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "cancel.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.cancel.imageset/cancel.svg b/Tella/NewAssets.xcassets/uwazi.cancel.imageset/cancel.svg new file mode 100644 index 000000000..29c4e8fb9 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.cancel.imageset/cancel.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/Contents.json new file mode 100644 index 000000000..1290ff811 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi-chevron-down.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/uwazi-chevron-down.svg b/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/uwazi-chevron-down.svg new file mode 100644 index 000000000..1bc664924 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.chevron-down.imageset/uwazi-chevron-down.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/Contents.json new file mode 100644 index 000000000..3eebb9583 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi-chevron-up.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/uwazi-chevron-up.svg b/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/uwazi-chevron-up.svg new file mode 100644 index 000000000..b1555f02b --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.chevron-up.imageset/uwazi-chevron-up.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tella/NewAssets.xcassets/uwazi.date.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.date.imageset/Contents.json new file mode 100644 index 000000000..6ac2d01e8 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.date.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi-date.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.date.imageset/uwazi-date.svg b/Tella/NewAssets.xcassets/uwazi.date.imageset/uwazi-date.svg new file mode 100644 index 000000000..cb8ed348c --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.date.imageset/uwazi-date.svg @@ -0,0 +1,3 @@ + + + diff --git a/Tella/NewAssets.xcassets/uwazi.empty.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.empty.imageset/Contents.json new file mode 100644 index 000000000..677ce7069 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.empty.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "uwazi-template.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.empty.imageset/uwazi-template.svg b/Tella/NewAssets.xcassets/uwazi.empty.imageset/uwazi-template.svg new file mode 100644 index 000000000..4dfe93558 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.empty.imageset/uwazi-template.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Tella/NewAssets.xcassets/uwazi.star.imageset/Contents.json b/Tella/NewAssets.xcassets/uwazi.star.imageset/Contents.json new file mode 100644 index 000000000..825e37316 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.star.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Star.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/uwazi.star.imageset/Star.svg b/Tella/NewAssets.xcassets/uwazi.star.imageset/Star.svg new file mode 100644 index 000000000..c5c2e0520 --- /dev/null +++ b/Tella/NewAssets.xcassets/uwazi.star.imageset/Star.svg @@ -0,0 +1,3 @@ + + + diff --git a/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift b/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift index b152e575f..ddf664479 100644 --- a/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift +++ b/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift @@ -18,19 +18,20 @@ struct BottomLockView:View { var nextButtonAction: NextButtonAction var destination: Destination? var shouldHideNext : Bool = false + var shouldHideBack: Bool = false var nextAction : (() -> Void)? var backAction : (() -> Void)? var body: some View { HStack { - - BottomButtonActionView(title: LocalizableLock.actionBack.localized, isValid: shouldEnableBackButton) { - self.backAction?() - self.presentationMode.wrappedValue.dismiss() + if !shouldHideBack{ + BottomButtonActionView(title: LocalizableLock.actionBack.localized, isValid: true) { + self.backAction?() + self.presentationMode.wrappedValue.dismiss() + + } } - Spacer() - if !shouldHideNext { BottomButtonActionView(title: LocalizableLock.actionNext.localized,isValid: isValid) { self.nextAction?() diff --git a/Tella/Scenes/HomeView/Models/ServerDataItem.swift b/Tella/Scenes/HomeView/Models/ServerDataItem.swift index bba2554b2..f1ed0d5c3 100644 --- a/Tella/Scenes/HomeView/Models/ServerDataItem.swift +++ b/Tella/Scenes/HomeView/Models/ServerDataItem.swift @@ -8,9 +8,9 @@ class ServerDataItem: Hashable { var servers : [Server] - var serverType : ServerType + var serverType : ServerConnectionType - init(servers: [Server], serverType: ServerType) { + init(servers: [Server], serverType: ServerConnectionType) { self.servers = servers self.serverType = serverType } diff --git a/Tella/Scenes/HomeView/ViewModel/HomeViewModel.swift b/Tella/Scenes/HomeView/ViewModel/HomeViewModel.swift index cae4480ab..26023b90b 100644 --- a/Tella/Scenes/HomeView/ViewModel/HomeViewModel.swift +++ b/Tella/Scenes/HomeView/ViewModel/HomeViewModel.swift @@ -22,20 +22,22 @@ class HomeViewModel: ObservableObject { var showingFilesTitle: Bool { return (hasRecentFile && appModel.settings.showRecentFiles) || !serverDataItemArray.isEmpty } - init(appModel:MainAppModel) { self.appModel = appModel getServersList() } - - func getServersList() { - + func getServersList() { self.appModel.vaultManager.tellaData?.servers.sink { result in } receiveValue: { serverArray in self.serverDataItemArray.removeAll() if !serverArray.isEmpty { - self.serverDataItemArray.append(ServerDataItem(servers: serverArray, serverType: .tellaUpload)) + // here i group all the tella servers in one array and the third party services in diferents arrays + let uwaziConnections = serverArray.filter { $0.serverType == .uwazi } + let tellaUploadServers = serverArray.filter { $0.serverType == .tella } + if !uwaziConnections.isEmpty { self.serverDataItemArray.append(ServerDataItem(servers: uwaziConnections, serverType: .uwazi)) } + if !tellaUploadServers.isEmpty { self.serverDataItemArray.append(ServerDataItem(servers: tellaUploadServers, serverType: .tella)) } + } }.store(in: &subscribers) } diff --git a/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift b/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift index 215335811..8aa7265a4 100644 --- a/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift +++ b/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift @@ -26,9 +26,9 @@ struct ConnectionsView: View { } var serversView: some View { - // ScrollView(.horizontal, showsIndicators: false) { - serverItems - // } + ScrollView(.horizontal, showsIndicators: false) { + serverItems + } } @ViewBuilder @@ -38,10 +38,14 @@ struct ConnectionsView: View { ForEach(homeViewModel.serverDataItemArray, id: \.self) { server in switch server.serverType { - case .tellaUpload: + case .tella: ConnectionsItemView(title: LocalizableReport.reportsTitle.localized, image: "home.report", destination: ReportsView(mainAppModel: appModel)) + case .uwazi: + ConnectionsItemView(title: LocalizableHome.uwaziServerTitle.localized, + image: "home.uwazi", + destination: UwaziView().environmentObject(UwaziViewModel(mainAppModel: appModel, server: parseUwaziServer(server: server.servers[0])))) default: ConnectionsItemView(title: LocalizableReport.reportsTitle.localized, image: "home.report", @@ -54,6 +58,16 @@ struct ConnectionsView: View { }.padding(.trailing, 17) } + + private func parseUwaziServer(server: Server) -> UwaziServer { + return UwaziServer( + id: server.id, + name: server.name, + username: server.username, + password: server.password, + accessToken: server.accessToken + ) + } } struct ConnectionsItemView: View { diff --git a/Tella/Scenes/HomeView/Views/Home/HomeView.swift b/Tella/Scenes/HomeView/Views/Home/HomeView.swift index 1cff3ca0b..ca1e11980 100644 --- a/Tella/Scenes/HomeView/Views/Home/HomeView.swift +++ b/Tella/Scenes/HomeView/Views/Home/HomeView.swift @@ -20,8 +20,6 @@ struct HomeView: View { ContainerView { VStack() { - - Spacer() .frame( height: !viewModel.serverDataItemArray.isEmpty ? 16 : 0 ) ConnectionsView() diff --git a/Tella/Scenes/MainView.swift b/Tella/Scenes/MainView.swift index 3e0af2a62..6c3a8c777 100644 --- a/Tella/Scenes/MainView.swift +++ b/Tella/Scenes/MainView.swift @@ -19,24 +19,19 @@ struct MainView: View { var body: some View { ZStack { tabbar - DragView(modalHeight: sheetManager.modalHeight, shouldHideOnTap: sheetManager.shouldHideOnTap, backgroundColor: sheetManager.backgroundColor, isShown: $sheetManager.isPresented) { sheetManager.content } - securityScreenView }.navigationBarHidden(false) } private var tabbar: some View { - ZStack { - CustomNavigation() { - TabView(selection: $appModel.selectedTab) { HomeView(appModel: appModel) .tabItem { @@ -56,7 +51,6 @@ struct MainView: View { Text(LocalizableRecorder.tabBar.localized) }.tag(MainAppModel.Tabs.mic) } - .toolbar { ToolbarItem(placement: .navigationBarLeading) { leadingView @@ -68,7 +62,7 @@ struct MainView: View { .navigationBarTitle(LocalizableHome.appBar.localized, displayMode: .inline) } .accentColor(.white) - + if appModel.selectedTab == .mic { RecordView(appModel: appModel, sourceView: .tab, diff --git a/Tella/Scenes/Reports/DeleteReportConfirmationView.swift b/Tella/Scenes/Reports/DeleteReportConfirmationView.swift index 002ef0e79..3feb1b2c8 100644 --- a/Tella/Scenes/Reports/DeleteReportConfirmationView.swift +++ b/Tella/Scenes/Reports/DeleteReportConfirmationView.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import SwiftUI diff --git a/Tella/Scenes/Reports/Models/ReportPages.swift b/Tella/Scenes/Reports/Models/ReportPages.swift new file mode 100644 index 000000000..3e6029772 --- /dev/null +++ b/Tella/Scenes/Reports/Models/ReportPages.swift @@ -0,0 +1,16 @@ +// +// ReportPages.swift +// Tella +// +// Created by Dhekra Rouatbi on 27/9/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +public enum ReportPages : Int { + case draft + case outbox + case submitted +} + diff --git a/Tella/Scenes/Reports/ReportList/ReportCardView.swift b/Tella/Scenes/Reports/ReportList/ReportCardView.swift index d1793cada..b90eb8bdc 100644 --- a/Tella/Scenes/Reports/ReportList/ReportCardView.swift +++ b/Tella/Scenes/Reports/ReportList/ReportCardView.swift @@ -26,11 +26,14 @@ struct ReportCardView : View { HStack { - reportDetails + ConnectionCardDetail(title: report.title ?? "", subtitle: report.getReportDate) Spacer() - moreButtonView + MoreButtonView(imageName: "reports.more", action: { + reportsViewModel.selectedReport = report + showReportActionBottomSheet() + }) }.padding(.all, 16) @@ -40,31 +43,6 @@ struct ReportCardView : View { } } - private var reportDetails : some View { - - VStack(alignment: .leading, spacing: 6) { - - Text(report.title ?? "") - .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) - .foregroundColor(.white) - .lineLimit(1) - - Text(report.getReportDate) - .font(.custom(Styles.Fonts.regularFontName, size: 12)) - .foregroundColor(.white) - } - } - - private var moreButtonView : some View { - Button { - reportsViewModel.selectedReport = report - showReportActionBottomSheet() - - } label: { - Image("reports.more") - .padding() - } - } private func showReportActionBottomSheet() { sheetManager.showBottomSheet(modalHeight: 176) { @@ -142,7 +120,7 @@ struct ReportCardView_Previews: PreviewProvider { ReportCardView(report: .constant(Report(title: LocalizableReport.reportsListTitle.localized, description: LocalizableReport.reportsListDescription.localized, status: .draft, - server: Server(), vaultFiles: []))) + server: TellaServer(autoUpload: false, autoDelete: true), vaultFiles: []))) } } } diff --git a/Tella/Scenes/Reports/ReportList/ReportListView.swift b/Tella/Scenes/Reports/ReportList/ReportListView.swift index 6dabdbd42..936952c2c 100644 --- a/Tella/Scenes/Reports/ReportList/ReportListView.swift +++ b/Tella/Scenes/Reports/ReportList/ReportListView.swift @@ -36,7 +36,7 @@ struct ReportListView_Previews: PreviewProvider { ReportListView(reportArray: .constant([Report(title: LocalizableReport.reportsListTitle.localized, description: LocalizableReport.reportsListDescription.localized, status: ReportStatus.draft, - server: Server(), vaultFiles: [])]), + server: TellaServer(autoUpload: false, autoDelete: true), vaultFiles: [])]), message: LocalizableReport.reportsListMessage.localized) } } diff --git a/Tella/Scenes/Reports/ReportsView.swift b/Tella/Scenes/Reports/ReportsView.swift index da77c58a7..179071d3c 100644 --- a/Tella/Scenes/Reports/ReportsView.swift +++ b/Tella/Scenes/Reports/ReportsView.swift @@ -53,6 +53,8 @@ struct ReportsView: View { case .submitted: ReportListView(reportArray: $reportsViewModel.submittedReports, message: LocalizableReport.reportsSubmitedEmpty.localized) + default: + EmptyView() } Spacer() diff --git a/Tella/Scenes/Reports/View Model/DraftReportVM.swift b/Tella/Scenes/Reports/View Model/DraftReportVM.swift index a89fc1f34..424e610cc 100644 --- a/Tella/Scenes/Reports/View Model/DraftReportVM.swift +++ b/Tella/Scenes/Reports/View Model/DraftReportVM.swift @@ -16,7 +16,7 @@ class DraftReportVM: ObservableObject { @Published var title : String = "" @Published var description : String = "" @Published var files : Set = [] - @Published var server : Server? + @Published var server : TellaServer? @Published var status : ReportStatus? @Published var apiID : String? @@ -38,10 +38,11 @@ class DraftReportVM: ObservableObject { @Published var successSavingReport : Bool = false @Published var failureSavingReport : Bool = false - var serverArray : [Server] = [] + var serverArray : [TellaServer] = [] var cancellable : Cancellable? = nil private var subscribers = Set() + var delayTime = 2.0 var serverName : String { guard let serverName = server?.name else { return LocalizableReport.selectProject.localized } @@ -102,7 +103,7 @@ class DraftReportVM: ObservableObject { } private func getServers() { - serverArray = mainAppModel.vaultManager.tellaData?.servers.value ?? [] + serverArray = mainAppModel.vaultManager.tellaData?.tellaServers.value ?? [] } private func initcurrentReportVM(reportId:Int?) { @@ -139,7 +140,6 @@ class DraftReportVM: ObservableObject { } self.objectWillChange.send() } - DispatchQueue.main.async { self.isValidTitle = self.title.textValidator() self.isValidDescription = self.description.textValidator() @@ -208,5 +208,6 @@ class DraftReportVM: ObservableObject { func deleteReport() { mainAppModel.deleteReport(reportId: reportId) + mainAppModel.deleteReport(reportId: reportId) } } diff --git a/Tella/Scenes/Reports/View Model/OutboxReportVM.swift b/Tella/Scenes/Reports/View Model/OutboxReportVM.swift index ec0b3bfb6..b561b4800 100644 --- a/Tella/Scenes/Reports/View Model/OutboxReportVM.swift +++ b/Tella/Scenes/Reports/View Model/OutboxReportVM.swift @@ -279,5 +279,6 @@ class OutboxReportVM: ObservableObject { func deleteReport() { mainAppModel.deleteReport(reportId: reportViewModel.id) + mainAppModel.deleteReport(reportId: reportViewModel.id) } } diff --git a/Tella/Scenes/Reports/View Model/ReportViewModel.swift b/Tella/Scenes/Reports/View Model/ReportViewModel.swift index cf59947d0..8c3a1be34 100644 --- a/Tella/Scenes/Reports/View Model/ReportViewModel.swift +++ b/Tella/Scenes/Reports/View Model/ReportViewModel.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation @@ -10,7 +10,7 @@ class ReportViewModel { @Published var description : String = "" @Published var files : [ReportVaultFile] = [] @Published var reportFiles : [ReportFile] = [] - @Published var server : Server? + @Published var server : TellaServer? @Published var status : ReportStatus? @Published var apiID : String? @@ -18,7 +18,7 @@ class ReportViewModel { } - init(id: Int?, title: String, description: String, files: [ReportVaultFile], reportFiles : [ReportFile], server: Server?, status: ReportStatus?, apiID: String?) { + init(id: Int?, title: String, description: String, files: [ReportVaultFile], reportFiles : [ReportFile], server: TellaServer?, status: ReportStatus?, apiID: String?) { self.id = id self.title = title self.description = description diff --git a/Tella/Scenes/Reports/View Model/SubmittedReportVM.swift b/Tella/Scenes/Reports/View Model/SubmittedReportVM.swift index 48c0a4641..9b5d6c7a9 100644 --- a/Tella/Scenes/Reports/View Model/SubmittedReportVM.swift +++ b/Tella/Scenes/Reports/View Model/SubmittedReportVM.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Scenes/Settings/Models/DeleteServerTexts.swift b/Tella/Scenes/Settings/Models/DeleteServerTexts.swift new file mode 100644 index 000000000..396e3a1c0 --- /dev/null +++ b/Tella/Scenes/Settings/Models/DeleteServerTexts.swift @@ -0,0 +1,26 @@ +// +// ServerDeleteMessage.swift +// Tella +// +// Created by Robert Shrestha on 9/7/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +// TODO: Maybe use another class name +struct DeleteServerTexts { + var titleText: String = "" + var messageText: String = "" + let cancelText = "CANCEL" + let actionText = "DELETE" + init(server: Server) { + if server.serverType == .tella { + titleText = "Delete \(server.name ?? "") server?" + messageText = "f you delete this server, all draft and submitted forms will be deleted from your device." + } else if server.serverType == .uwazi { + titleText = "Delete \"\(server.name ?? "")\" connection?" + messageText = "If you delete this server, all draft and submitted entities will be deleted from your device. Delete anyway?" + } else {} + } +} diff --git a/Tella/Scenes/Settings/Models/ServerActionType.swift b/Tella/Scenes/Settings/Models/ServerActionType.swift index 790060a6c..819feb8d8 100644 --- a/Tella/Scenes/Settings/Models/ServerActionType.swift +++ b/Tella/Scenes/Settings/Models/ServerActionType.swift @@ -11,14 +11,13 @@ enum ServerActionType: ActionType { case delete } - var serverActionItems : [ListActionSheetItem] { return [ ListActionSheetItem(imageName: "edit-icon", - content: "Edit", + content: LocalizableUwazi.uwaziServerEdit.localized, type: ServerActionType.edit), ListActionSheetItem(imageName: "delete-icon-white", - content: "Delete", + content: LocalizableUwazi.uwaziServerDelete.localized, type: ServerActionType.delete) ] } diff --git a/Tella/Scenes/Settings/ViewModel/ServerViewModel.swift b/Tella/Scenes/Settings/ViewModel/ServerViewModel.swift index e8de59287..25259207d 100644 --- a/Tella/Scenes/Settings/ViewModel/ServerViewModel.swift +++ b/Tella/Scenes/Settings/ViewModel/ServerViewModel.swift @@ -38,28 +38,23 @@ class ServerViewModel: ObservableObject { private var cancellable: Cancellable? = nil var subscribers = Set() - var currentServer : Server? + var currentServer : TellaServer? var isAutoUploadServerExist: Bool { return mainAppModel.vaultManager.tellaData?.getAutoUploadServer() != nil && self.currentServer?.autoUpload == false } - init(mainAppModel : MainAppModel, currentServer: Server?) { - + init(mainAppModel : MainAppModel, currentServer: TellaServer?) { self.mainAppModel = mainAppModel self.currentServer = currentServer - cancellable = $validUsername.combineLatest($validPassword).sink(receiveValue: { validUsername, validPassword in self.validCredentials = validUsername && validPassword }) - fillReportVM() - } func addServer(token: String, project: ProjectAPI) { - - let server = Server(name: project.name, + let server = TellaServer(name: project.name, serverURL: projectURL.getBaseURL(), username: username, password: password, @@ -115,10 +110,7 @@ class ServerViewModel: ObservableObject { } }, receiveValue: { result in -// - self.getProjetSlug(token: result.accessToken) - } ) .store(in: &subscribers) @@ -164,8 +156,6 @@ class ServerViewModel: ObservableObject { backgroundUpload = server.backgroundUpload ?? false autoUpload = server.autoUpload ?? false autoDelete = server.autoDelete ?? false - - } } diff --git a/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift b/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift index 4f4dda330..14ac2c806 100644 --- a/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift +++ b/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift @@ -26,11 +26,19 @@ class ServersViewModel: ObservableObject { } func deleteServer() { - guard let serverId = self.currentServer?.id else { return } + guard let serverId = self.currentServer?.id else { return } + + if currentServer?.serverType == .uwazi { + mainAppModel.vaultManager.tellaData?.deleteUwaziServer(serverId: serverId) + + return + } + mainAppModel.vaultManager.tellaData?.deleteServer(serverId: serverId) } func deleteAllServersConnection() { mainAppModel.vaultManager.tellaData?.deleteAllServers() + mainAppModel.vaultManager.tellaData?.deleteAllServers() } } diff --git a/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift b/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift new file mode 100644 index 000000000..7bfabb172 --- /dev/null +++ b/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift @@ -0,0 +1,318 @@ +// +// UwaziServerViewModel.swift +// Tella +// +// Created by Robert Shrestha on 4/30/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import Combine + +class UwaziServerViewModel: ObservableObject { + var mainAppModel : MainAppModel + + // Server propreties + @Published var serverURL : String = "https://" + + @Published var name : String? + @Published var username : String = "" + @Published var password : String = "" + @Published var activatedMetadata : Bool = false + @Published var backgroundUpload : Bool = false + @Published var autoUpload : Bool = false + @Published var autoDelete : Bool = false + + + // Add URL + @Published var validURL : Bool = false + @Published var shouldShowURLError : Bool = false + @Published var urlErrorMessage : String = "" + @Published var isPublicInstance: Bool? + + // Login + @Published var validUsername : Bool = false + @Published var validPassword : Bool = false + @Published var validCode: Bool = false + @Published var validCredentials : Bool = false + @Published var shouldShowLoginError : Bool = false + @Published var loginErrorMessage : String = "" + @Published var isLoading : Bool = false + @Published var showNextSuccessLoginView : Bool = false + @Published var showNextLanguageSelectionView: Bool = false + @Published var showNext2FAView: Bool = false + + // Authentication + @Published var validAuthenticationCode: Bool = false + @Published var shouldShowAuthenticationError: Bool = false + @Published var code: String = "" + @Published var codeErrorMessage: String = "" + @Published var showLanguageSelectionView: Bool = false + + // Language + @Published var languages: [UwaziLanguageRow] = [] + @Published var selectedLanguage: UwaziLanguageRow? + private var cancellableLogin: Cancellable? = nil + private var cancellableAuthenticationCode: Cancellable? = nil + var subscribers = Set() + + var currentServer : UwaziServer? + var token: String? + var setting: UwaziCheckURL? + var cookie: String? + + init(mainAppModel : MainAppModel, currentServer: UwaziServer?) { + + self.mainAppModel = mainAppModel + self.currentServer = currentServer + + cancellableLogin = $validUsername.combineLatest($validPassword).sink(receiveValue: { validUsername, validPassword in + self.validCredentials = validUsername && validPassword + }) + cancellableAuthenticationCode = $validCode.sink(receiveValue: { validCode in + self.validAuthenticationCode = validCode + }) + } + + func handleServerAction() { + if currentServer != nil { + updateServer() + } else { + addServer() + } + } + + func addServer() { + let server = UwaziServer(name: setting?.siteName, + serverURL: serverURL.getBaseURL(), + username: username, + password: password, + accessToken: self.token, + locale: selectedLanguage?.locale + ) + debugLog(server) + guard let id = mainAppModel.vaultManager.tellaData?.addUwaziServer(server: server) else { return } + server.id = id + self.currentServer = server + } + + func updateServer() { + guard let currentServer = currentServer, let currentServerId = currentServer.id else { return } + let server = UwaziServer(id: currentServerId, + name: setting?.siteName, + serverURL: serverURL.getBaseURL(), + username: username, + password: password, + accessToken: self.token, + locale: selectedLanguage?.locale) + + + guard let id = mainAppModel.vaultManager.tellaData?.updateUwaziServer(server: server) else { return } + server.id = id + } + + // MARK: - Get Language API Call Methods + func getLanguage() { + isLoading = true + guard let baseURL = serverURL.getBaseURL() else { return } + UwaziServerRepository().getLanguage(serverURL: baseURL) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + self.handleCompletionForGetLanguage(completion) + }, receiveValue: { wrapper in + self.handleRecieveValueForGetLanguage(wrapper) + }).store(in: &subscribers) + } + + fileprivate func handleCompletionForGetLanguage(_ completion: Subscribers.Completion) { + self.isLoading = false + switch completion { + case .finished: + debugLog("Finished") + // TODO: Handle this error + case .failure(let error): + debugLog(error) + switch error { + case .noInternetConnection: + Toast.displayToast(message: error.errorDescription ?? error.localizedDescription) + default: + break + } + self.isLoading = false + } + } + + fileprivate func handleRecieveValueForGetLanguage(_ wrapper: UwaziLanguage) { + self.isLoading = false + self.languages.append(contentsOf: wrapper.rows ?? []) + if let server = self.currentServer { + let locale = server.locale + self.selectedLanguage = self.languages.compactMap{$0}.first(where: {$0.locale == locale}) + } + self.showNextSuccessLoginView = true + } + + + // MARK: - Check URL API Call Methods + func checkURL() { + self.isLoading = true + guard let baseURL = serverURL.getBaseURL() else { return } + UwaziServerRepository().checkServerURL(serverURL: baseURL) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + self.handleCompletionForCheckURL(completion) + }, receiveValue: { wrapper in + self.handleRecieveValueForCheckURL(wrapper) + }).store(in: &subscribers) + } + + fileprivate func handleCompletionForCheckURL(_ completion: Subscribers.Completion) { + self.isLoading = false + switch completion { + case .finished: + debugLog("Finished") + // TODO: handle this error + case .failure(let error): + switch error { + case .noInternetConnection: + Toast.displayToast(message: error.errorDescription ?? error.localizedDescription) + default: + debugLog(error) + urlErrorMessage = error.errorDescription ?? error.localizedDescription + shouldShowURLError = true + } + } + } + + + fileprivate func handleRecieveValueForCheckURL(_ wrapper: UwaziCheckURL) { + self.setting = wrapper + debugLog("Finished") + self.isLoading = false + self.isPublicInstance = true + } + + // MARK: - Login API Call Methods + func login() { + guard let baseURL = serverURL.getBaseURL() else { return } + + isLoading = true + + UwaziServerRepository().login(username: username, password: password, serverURL: baseURL) + .receive(on: DispatchQueue.main) + .sink( + receiveCompletion: { completion in + self.handleCompletionForLogin(completion) + }, + receiveValue: { result in + self.handleReceiveValueForLogin(result) + } + ) + .store(in: &subscribers) + } + + fileprivate func handleCompletionForLogin(_ completion: Subscribers.Completion) { + switch completion { + case .failure(let error): + switch error { + case .invalidURL, .unexpectedResponse, .badServer: + self.shouldShowLoginError = true + self.loginErrorMessage = error.errorDescription ?? "" + case .httpCode(let code): + // if the status code is 401 then username or password is not matching + // if the status code is 409 then 2FA is needed + let httpError = HTTPErrorCodes(rawValue: code) ?? .unknown + switch httpError { + case .need2FA: + self.showNext2FAView = true + default: + self.shouldShowLoginError = true + self.loginErrorMessage = error.errorDescription ?? error.localizedDescription + } + case .noInternetConnection: + Toast.displayToast(message: error.errorDescription ?? error.localizedDescription) + } + case .finished: + self.shouldShowLoginError = false + self.loginErrorMessage = "" + break + } + self.isLoading = false + } + + fileprivate func handleReceiveValueForLogin(_ result: String?) { + self.isLoading = false + if let result = result { + self.token = result + self.showNextLanguageSelectionView = true + } else { + self.shouldShowLoginError = true + // TODO: More appropiate message here + self.loginErrorMessage = "Something went wrong!!" + } + } + + + + // MARK: - 2FA API Call Methods + func twoFactorAuthentication() { + + guard let baseURL = serverURL.getBaseURL() else { return } + + isLoading = true + + UwaziServerRepository().twoFactorAuthentication(username: username, password: password, token: code, serverURL: baseURL) + .receive(on: DispatchQueue.main) + .sink( + receiveCompletion: { completion in + self.handleCompletionFor2FA(completion) + }, + receiveValue: { result in + self.handleReceiveValueForLogin(result) + } + ) + .store(in: &subscribers) + } + fileprivate func handleCompletionFor2FA(_ completion: Subscribers.Completion) { + switch completion { + case .failure(let error): + switch error { + case .invalidURL, .unexpectedResponse, .badServer: + self.codeErrorMessage = error.errorDescription ?? "" + case .httpCode(let code): + // if the status code is 401 then the 2FA code is incorrect + let httpError = HTTPErrorCodes(rawValue: code) ?? .unknown + switch httpError { + case .unauthorized: + self.codeErrorMessage = "Two-factor authentication failed." + default: + self.codeErrorMessage = error.errorDescription ?? "" + } + case .noInternetConnection: + Toast.displayToast(message: error.errorDescription ?? error.localizedDescription) + } + self.shouldShowAuthenticationError = true + case .finished: + self.shouldShowAuthenticationError = false + self.codeErrorMessage = "" + break + } + self.isLoading = false + } + + func fillUwaziServer() { + guard let server = self.currentServer else { return } + self.serverURL = server.url ?? "" + // To Avoid the animation of textfield in login view + self.username = "" + self.username = "" + + } + + func fillUwaziCredentials() { + guard let server = self.currentServer else { return } + self.username = server.username ?? "" + self.password = server.password ?? "" + } +} + diff --git a/Tella/Scenes/Settings/Views/Common/CardFrameView.swift b/Tella/Scenes/Settings/Views/Common/CardFrameView.swift index 2a421f7ad..684f8a4f7 100644 --- a/Tella/Scenes/Settings/Views/Common/CardFrameView.swift +++ b/Tella/Scenes/Settings/Views/Common/CardFrameView.swift @@ -23,9 +23,7 @@ struct CardFrameView: View { #Preview { CardFrameView(content: { - Text("Test") + Text("Test") }) - } - diff --git a/Tella/Scenes/Settings/Views/Common/SettingCheckboxItem.swift b/Tella/Scenes/Settings/Views/Common/SettingCheckboxItem.swift index 745edd9a3..9f83d22fc 100644 --- a/Tella/Scenes/Settings/Views/Common/SettingCheckboxItem.swift +++ b/Tella/Scenes/Settings/Views/Common/SettingCheckboxItem.swift @@ -3,7 +3,7 @@ // Tella // // Created by Gustavo on 24/02/2023. -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Scenes/Settings/Views/Common/SettingsBottomView.swift b/Tella/Scenes/Settings/Views/Common/SettingsBottomView.swift index 17ed9b9a1..170e2e495 100644 --- a/Tella/Scenes/Settings/Views/Common/SettingsBottomView.swift +++ b/Tella/Scenes/Settings/Views/Common/SettingsBottomView.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import SwiftUI @@ -8,6 +8,8 @@ struct SettingsBottomView: View { var cancelAction : (() -> Void) var saveAction : (() -> Void) + var saveActionTitle = LocalizableSettings.settLockTimeoutSaveSheetAction.localized + var isDisable: Bool = false var body: some View { @@ -18,7 +20,7 @@ struct SettingsBottomView: View { Button { cancelAction() } label: { - Text("CANCEL") + Text(LocalizableSettings.UwaziLanguageCancel.localized) .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) .foregroundColor(.white) .padding(EdgeInsets(top: 10, leading: 25, bottom: 10, trailing: 25)) @@ -29,14 +31,14 @@ struct SettingsBottomView: View { Button { saveAction() } label: { - Text("SAVE") + Text(saveActionTitle) .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) .foregroundColor(.white) .padding(EdgeInsets(top: 10, leading: 25, bottom: 10, trailing: 25)) .background(Styles.Colors.yellow) + .opacity(isDisable ? 0.8 : 1) .cornerRadius(25) - - } + }.disabled(isDisable) }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) } diff --git a/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift b/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift index bb96377eb..dcb6147b7 100644 --- a/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift +++ b/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift @@ -199,5 +199,4 @@ struct FeedbackView: View { #Preview { FeedbackView(mainAppModel: MainAppModel.stub()) .environmentObject(MainAppModel.stub()) - } diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerURLView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerURLView.swift index 50a0a2697..b8f34bd6f 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerURLView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerURLView.swift @@ -17,7 +17,7 @@ struct AddServerURLView: View { @EnvironmentObject var serversViewModel : ServersViewModel @StateObject var serverViewModel : ServerViewModel - init(appModel:MainAppModel, server: Server? = nil) { + init(appModel:MainAppModel, server: TellaServer? = nil) { _serverViewModel = StateObject(wrappedValue: ServerViewModel(mainAppModel: appModel, currentServer: server)) } diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/ServerLoginView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/ServerLoginView.swift index db376e61a..a78957c55 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/ServerLoginView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/ServerLoginView.swift @@ -77,7 +77,6 @@ struct ServerLoginView: View { } .navigationBarHidden(true) - .onReceive(serverViewModel.$showNextSuccessLoginView) { value in if value { navigateTo(destination: successLoginView) @@ -93,9 +92,10 @@ struct ServerLoginView: View { } } - -struct ServerLoginView_Previews: PreviewProvider { - static var previews: some View { - ServerLoginView() - } -} +//struct ServerLoginView_Previews: PreviewProvider { +// static var previews: some View { +// ServerLoginView() +// .environmentObject(ServersViewModel(mainAppModel: MainAppModel())) +// .environmentObject(ServerViewModel(mainAppModel: MainAppModel(), currentServer: nil)) +// } +//} diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift index 2884d096a..2e6e393ba 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift @@ -9,6 +9,7 @@ struct SettingsAddServerCardView: View { @EnvironmentObject var serversViewModel : ServersViewModel @EnvironmentObject var mainAppModel : MainAppModel + @EnvironmentObject var settingViewModel: SettingsViewModel var body: some View { ZStack { @@ -26,7 +27,8 @@ struct SettingsAddServerCardView: View { Spacer() Button { - navigateTo(destination: AddServerURLView(appModel: mainAppModel) ) + //navigateTo(destination: AddServerURLView(appModel: mainAppModel)) + navigateTo(destination: ServerSelectionView(appModel: mainAppModel).environmentObject(serversViewModel)) } label: { Image("settings.add") .padding(.all, 14) @@ -46,5 +48,7 @@ struct SettingsAddServerCardView: View { struct SettingsAddServerCardView_Previews: PreviewProvider { static var previews: some View { SettingsAddServerCardView() + .environmentObject(ServerViewModel(mainAppModel: MainAppModel.stub(), currentServer: nil)) + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) } } diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift index a6f018b00..4e5b83682 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift @@ -80,5 +80,7 @@ struct SuccessLoginView: View { struct SuccessLoginView_Previews: PreviewProvider { static var previews: some View { SuccessLoginView() + .environmentObject(MainAppModel.stub()) + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) } } diff --git a/Tella/Scenes/Settings/Views/Servers/EditSettingsServerView.swift b/Tella/Scenes/Settings/Views/Servers/EditSettingsServerView.swift index 4eec413c3..57e636896 100644 --- a/Tella/Scenes/Settings/Views/Servers/EditSettingsServerView.swift +++ b/Tella/Scenes/Settings/Views/Servers/EditSettingsServerView.swift @@ -11,7 +11,7 @@ struct EditSettingsServerView: View { @StateObject private var serverViewModel : ServerViewModel - init(appModel:MainAppModel, isPresented : Binding, server: Server? = nil) { + init(appModel:MainAppModel, isPresented : Binding, server: TellaServer? = nil) { _serverViewModel = StateObject(wrappedValue: ServerViewModel(mainAppModel: appModel, currentServer: server)) self.isPresented = isPresented } diff --git a/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift b/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift new file mode 100644 index 000000000..14d3d7a83 --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift @@ -0,0 +1,105 @@ +// +// ServerSelectionView.swift +// Tella +// +// Created by Robert Shrestha on 4/12/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct ServerSelectionView: View { + @EnvironmentObject var serversViewModel : ServersViewModel + @StateObject var serverViewModel : ServerViewModel + @EnvironmentObject var mainAppModel : MainAppModel + @State var selectedServerType: ServerConnectionType = .unknown + @Environment(\.presentationMode) var presentationMode: Binding + + init(appModel:MainAppModel, server: TellaServer? = nil) { + _serverViewModel = StateObject(wrappedValue: ServerViewModel(mainAppModel: appModel, currentServer: server)) + } + var body: some View { + ContainerView { + + VStack(spacing: 20) { + Spacer() + HeaderView() + buttonViews() + Spacer() + bottomView() + } + .toolbar { + LeadingTitleToolbar(title: LocalizableSettings.settServersAppBar.localized) + } + } + } + fileprivate func buttonViews() -> Group> { + return Group { + TellaButtonView(title: LocalizableSettings.settServerTellaWeb.localized, + nextButtonAction: .action, + isOverlay: selectedServerType == .tella, + isValid: .constant(true),action: { + selectedServerType = .tella + }) + .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)) + TellaButtonView(title: LocalizableSettings.settServerUwazi.localized, + nextButtonAction: .action, + isOverlay: selectedServerType == .uwazi, + isValid: .constant(true), action: { + selectedServerType = .uwazi + }).padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)) + } + } + + fileprivate func bottomView() -> BottomLockView { + return BottomLockView(isValid: .constant(true), + nextButtonAction: .action, + shouldHideNext: false, + shouldHideBack: true, + nextAction: { + switch selectedServerType { + case .tella: + navigateToTellaWebFlow() + case .uwazi: + navigateToUwaziFlow() + default: + break + } + }) + } + + fileprivate func navigateToTellaWebFlow() { + navigateTo(destination: AddServerURLView(appModel: mainAppModel)) + } + + fileprivate func navigateToUwaziFlow() { + navigateTo(destination: UwaziAddServerURLView(appModel: mainAppModel) + .environmentObject(serverViewModel) + .environmentObject(serversViewModel)) + } + + + struct HeaderView: View { + var body: some View { + VStack(spacing: 20) { + Image("settings.server") + Text(LocalizableSettings.settServerSelectionTitle.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 18)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + Text(LocalizableSettings.settServerSelectionMessage.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + } + } + } +} + +struct ServerSelectionView_Previews: PreviewProvider { + static var previews: some View { + ServerSelectionView(appModel: MainAppModel.stub()) + .environmentObject(MainAppModel.stub()) + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) + } +} diff --git a/Tella/Scenes/Settings/Views/Servers/ServersListView.swift b/Tella/Scenes/Settings/Views/Servers/ServersListView.swift index 7d559f4ac..4ca57ebeb 100644 --- a/Tella/Scenes/Settings/Views/Servers/ServersListView.swift +++ b/Tella/Scenes/Settings/Views/Servers/ServersListView.swift @@ -10,6 +10,7 @@ struct ServersListView: View { @EnvironmentObject var serversViewModel : ServersViewModel @EnvironmentObject var sheetManager: SheetManager @EnvironmentObject var mainAppModel : MainAppModel + @EnvironmentObject var settingViewModel : SettingsViewModel @State var shouldShowEditServer : Bool = false @@ -29,7 +30,7 @@ struct ServersListView: View { } } .fullScreenCover(isPresented: $shouldShowEditServer, content: { - EditSettingsServerView(appModel: mainAppModel, isPresented: $shouldShowEditServer, server: serversViewModel.currentServer) + EditSettingsServerView(appModel: mainAppModel, isPresented: $shouldShowEditServer, server: mainAppModel.vaultManager.tellaData?.getTellaServer(serverId: (serversViewModel.currentServer?.id)!)) }) .toolbar { @@ -56,35 +57,51 @@ struct ServersListView: View { action: {item in serversViewModel.currentServer = server - - self.handleActions(item : item) - }) - } - } - - private func showDeleteServerConfirmationView() { - sheetManager.showBottomSheet(modalHeight: 200) { - ConfirmBottomSheet(titleText: "Delete “Election monitoring” server?", - msgText: "If you delete this server, all draft and submitted forms will be deleted from your device.", - cancelText: "CANCEL", - actionText: "DELETE", didConfirmAction: { - serversViewModel.deleteServer() - sheetManager.hide() + self.handleActions(item : item, server: server) }) } } - - private func handleActions(item: ListActionSheetItem) { + private func handleActions(item: ListActionSheetItem, server: Server) { guard let type = item.type as? ServerActionType else { return } - switch type { case .edit: - shouldShowEditServer = true + handleEditServer(server) sheetManager.hide() case .delete: - showDeleteServerConfirmationView() + let deleteMessages = DeleteServerTexts(server: server) + showDeleteServerConfirmationView(message: deleteMessages) } } + + fileprivate func handleEditServer(_ server: Server) { + guard let serverType = server.serverType else { return } + switch serverType { + case .tella: + shouldShowEditServer = true + case .uwazi: + navigateToUwaziAddServerView( + UwaziServer( + id: server.id, name: server.name, serverURL: server.url, accessToken: server.accessToken) + ) + default: + break + } + } + private func showDeleteServerConfirmationView(message: DeleteServerTexts) { + sheetManager.showBottomSheet(modalHeight: 210) { + ConfirmBottomSheet(titleText: message.titleText, + msgText: message.messageText, + cancelText: message.cancelText, + actionText: message.actionText, didConfirmAction: { + serversViewModel.deleteServer() + }) + } + } + + fileprivate func navigateToUwaziAddServerView(_ server: UwaziServer) { + navigateTo(destination: UwaziAddServerURLView(appModel: mainAppModel, server: server) + .environmentObject(serversViewModel)) + } } struct ServersListView_Previews: PreviewProvider { diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step1/UwaziAddServerURLView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step1/UwaziAddServerURLView.swift new file mode 100644 index 000000000..ce9d55dbb --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step1/UwaziAddServerURLView.swift @@ -0,0 +1,95 @@ +// +// UwaziAddServerURLView.swift +// Tella +// +// Created by Robert Shrestha on 4/18/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziAddServerURLView: View { + @Environment(\.presentationMode) var presentationMode: Binding + var nextButtonAction: NextButtonAction = .action + @EnvironmentObject var serversViewModel : ServersViewModel + @StateObject var uwaziServerViewModel : UwaziServerViewModel + + init(appModel:MainAppModel, server: UwaziServer? = nil) { + _uwaziServerViewModel = StateObject(wrappedValue: UwaziServerViewModel(mainAppModel: appModel, currentServer: server)) + } + var body: some View { + + ContainerView { + + ZStack { + + VStack(spacing: 0) { + Spacer() + .frame(height: 80) + + Image("settings.server") + + + Spacer() + .frame(height: 24) + + Text(LocalizableSettings.UwaziServerURL.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 18)) + .foregroundColor(.white) + + Spacer() + .frame(height: 40) + TextfieldView(fieldContent: $uwaziServerViewModel.serverURL, + isValid: $uwaziServerViewModel.validURL, + shouldShowError: $uwaziServerViewModel.shouldShowURLError, + errorMessage: uwaziServerViewModel.urlErrorMessage, + fieldType: .url) + Spacer() + + BottomLockView(isValid: $uwaziServerViewModel.validURL, + nextButtonAction: .action, + nextAction: { + self.uwaziServerViewModel.checkURL() + }, + backAction: { + self.presentationMode.wrappedValue.dismiss() + }) + } .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + + if uwaziServerViewModel.isLoading { + CircularActivityIndicatory() + } + } + } + .navigationBarHidden(true) + .onAppear { + self.uwaziServerViewModel.fillUwaziServer() + } + .onReceive(uwaziServerViewModel.$isPublicInstance) { isPublicInstance in + guard let isPublicInstance = isPublicInstance else { return } + handleNavigation(isPublicInstance: isPublicInstance) + } + } + func handleNavigation(isPublicInstance: Bool) { + if isPublicInstance { + let serverAccess = UwaziServerAccessSelectionView() + .environmentObject(uwaziServerViewModel) + .environmentObject(serversViewModel) + navigateTo(destination: serverAccess) + } else { + let loginView = UwaziLoginView() + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + navigateTo(destination: loginView) + } + } +} + +struct UwaziAddServerURLView_Previews: PreviewProvider { + static var previews: some View { + UwaziAddServerURLView(appModel: MainAppModel.stub()) + .environmentObject(MainAppModel.stub()) + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) + .environmentObject(ServerViewModel(mainAppModel: MainAppModel.stub(), currentServer: nil)) + } +} diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift new file mode 100644 index 000000000..6f007f3b7 --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift @@ -0,0 +1,111 @@ +// +// UwaziServerAccessSelectionView.swift +// Tella +// +// Created by Robert Shrestha on 4/18/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziServerAccessSelectionView: View { + enum UwaziAccessServerType { + case publicServer + case privateServer + case none + } + + @State var isButtonValid = true + @Environment(\.presentationMode) var presentationMode: Binding + @State var accessServerType: UwaziAccessServerType = .none + @EnvironmentObject var serversViewModel : ServersViewModel + @EnvironmentObject var uwaziServerViewModel : UwaziServerViewModel + + var body: some View { + ContainerView { + VStack { + Spacer() + VStack(spacing: 12) { + HeaderView() + Spacer().frame(height:24) + buttonView() + } + Spacer() + BottomLockView(isValid: $isButtonValid, + nextButtonAction: .action, + nextAction: { + handleNavigation() + }, + backAction: { + self.presentationMode.wrappedValue.dismiss() + }) + }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + } + .navigationBarBackButtonHidden(true) + + } + struct HeaderView: View { + var body: some View { + VStack(spacing: 12) { + Image("settings.server") + Text(LocalizableSettings.UwaziAccessServerTitle.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 18)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + } + } + } + fileprivate func buttonView() -> some View { + return VStack(spacing: 12) { + TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, + nextButtonAction: .action, + isOverlay: accessServerType == .privateServer, + isValid: .constant(true),action: { + accessServerType = .privateServer + }) + TellaButtonView(title: LocalizableSettings.UwaziPublicInstance.localized, + nextButtonAction: .action, + isOverlay: accessServerType == .publicServer, + isValid: .constant(true),action: { + accessServerType = .publicServer + }) + } + } + + fileprivate func handleNavigation() { + switch accessServerType { + case .publicServer: + navigateToLanguageView() + case .privateServer: + navigateToLoginView() + case .none: + break + } + } + fileprivate func navigateToLoginView() { + let loginView = UwaziLoginView() + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + navigateTo(destination: loginView) + } + + fileprivate func navigateToLanguageView() { + let languageSelection = UwaziLanguageSelectionView(isPresented: .constant(true)) + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + navigateTo(destination: languageSelection) + } + +} + +struct UwaziServerAccessSelectionView_Previews: PreviewProvider { + static var previews: some View { + UwaziServerAccessSelectionView() + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) + .environmentObject(ServerViewModel(mainAppModel: MainAppModel.stub(), currentServer: nil)) + } +} + + + + diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step3/UwaziLoginView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step3/UwaziLoginView.swift new file mode 100644 index 000000000..25a96e5ab --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step3/UwaziLoginView.swift @@ -0,0 +1,107 @@ +// +// UwaziLoginView.swift +// Tella +// +// Created by Robert Shrestha on 4/24/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziLoginView: View { + @EnvironmentObject var uwaziServerViewModel : UwaziServerViewModel + @EnvironmentObject var serversViewModel : ServersViewModel + + @Environment(\.presentationMode) var presentationMode: Binding + + var body: some View { + + ContainerView { + ZStack { + VStack(spacing: 0) { + VStack(spacing: 32) { + Spacer() + TopServerView(title: LocalizableSettings.UwaziLoginAccess.localized) + usernameTextFieldView() + passwordTextFieldView() + loginButtonView() + Spacer() + }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + + BottomLockView(isValid: $uwaziServerViewModel.validPassword, + nextButtonAction: .action, + shouldHideNext: true) + } + if uwaziServerViewModel.isLoading { + CircularActivityIndicatory() + } + } + } + .navigationBarHidden(true) + .onReceive(uwaziServerViewModel.$showNextLanguageSelectionView, perform: { value in + if value { + showLanguageSelectionView() + } + }) + .onReceive(uwaziServerViewModel.$showNext2FAView, perform: { value in + if value { + show2FAView() + } + }) + .onAppear { + self.uwaziServerViewModel.fillUwaziCredentials() + } + } + fileprivate func usernameTextFieldView() -> some View { + return TextfieldView(fieldContent: $uwaziServerViewModel.username, + isValid: $uwaziServerViewModel.validUsername, + shouldShowError: $uwaziServerViewModel.shouldShowLoginError, + fieldType: .text, + placeholder : LocalizableSettings.UwaziUsername.localized) + .autocapitalization(.none) + .frame(height: 30) + } + + fileprivate func passwordTextFieldView() -> some View { + return TextfieldView(fieldContent: $uwaziServerViewModel.password, + isValid: $uwaziServerViewModel.validPassword, + shouldShowError: $uwaziServerViewModel.shouldShowLoginError, + errorMessage: uwaziServerViewModel.loginErrorMessage, + fieldType: .text, + placeholder : LocalizableSettings.UwaziPassword.localized) + .autocapitalization(.none) + .frame(height: 57) + } + + fileprivate func loginButtonView() -> TellaButtonView { + return TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, + nextButtonAction: .action, + isValid: $uwaziServerViewModel.validCredentials) { + UIApplication.shared.endEditing() + self.uwaziServerViewModel.login() + } + } + fileprivate func showLanguageSelectionView() { + let languageView = UwaziLanguageSelectionView(isPresented: .constant(true)) + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + navigateTo(destination: languageView) + } + + fileprivate func show2FAView() { + let twoStepVerification = UwaziTwoStepVerification() + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + if !uwaziServerViewModel.shouldShowLoginError { + navigateTo(destination: twoStepVerification) + } + } +} + +struct UwaziLoginView_Previews: PreviewProvider { + static var previews: some View { + UwaziLoginView() + .environmentObject(ServersViewModel(mainAppModel: MainAppModel.stub())) + .environmentObject(ServerViewModel(mainAppModel: MainAppModel.stub(), currentServer: nil)) + } +} diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift new file mode 100644 index 000000000..033e07bf3 --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift @@ -0,0 +1,77 @@ +// +// UwaziTwoStepVerification.swift +// Tella +// +// Created by Robert Shrestha on 4/25/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziTwoStepVerification: View { + + @Environment(\.presentationMode) var presentationMode: Binding + @EnvironmentObject var uwaziServerViewModel : UwaziServerViewModel + @EnvironmentObject var serversViewModel : ServersViewModel + + var body: some View { + ContainerView { + ZStack { + VStack { + VStack(spacing: 10) { + Spacer() + TopServerView(title: LocalizableSettings.UwaziTwoStepTitle.localized) + Text(LocalizableSettings.UwaziTwoStepMessage.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + Spacer() + .frame(height: 15) + TextfieldView(fieldContent: $uwaziServerViewModel.code, + isValid: $uwaziServerViewModel.validCode, + shouldShowError: $uwaziServerViewModel.shouldShowAuthenticationError, + errorMessage: uwaziServerViewModel.codeErrorMessage, + fieldType: .code, + placeholder: LocalizableSettings.UwaziAuthenticationPlaceholder.localized, + keyboardType: .numberPad) + .frame(height: 57) + Spacer() + .frame(height: 19) + TellaButtonView(title: LocalizableSettings.UwaziAuthenticationVerify.localized, + nextButtonAction: .action, + isValid: $uwaziServerViewModel.validAuthenticationCode) { + UIApplication.shared.endEditing() + uwaziServerViewModel.twoFactorAuthentication() + } + Spacer() + } + .padding(.leading, 23) + .padding(.trailing,23) + BottomLockView(isValid: .constant(true), + nextButtonAction: .action, + shouldHideNext: true) + } + if uwaziServerViewModel.isLoading { + CircularActivityIndicatory() + } + } + + } + .navigationBarHidden(true) + .onReceive(uwaziServerViewModel.$showLanguageSelectionView) { value in + if value { + let languageView = UwaziLanguageSelectionView(isPresented: .constant(true)) + //.environmentObject(SettingsViewModel(appModel: MainAppModel())) + .environmentObject(serversViewModel) + .environmentObject(uwaziServerViewModel) + navigateTo(destination: languageView) + } + } + } +} + +struct UwaziTwoStepVerification_Previews: PreviewProvider { + static var previews: some View { + UwaziTwoStepVerification() + } +} diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step5/UwaziLanguageSelectionView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step5/UwaziLanguageSelectionView.swift new file mode 100644 index 000000000..a454c7a90 --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step5/UwaziLanguageSelectionView.swift @@ -0,0 +1,146 @@ +// +// UwaziLanguageSelectionView.swift +// Tella +// +// Created by Robert Shrestha on 4/25/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziLanguageSelectionView: View { + @Binding var isPresented : Bool + @EnvironmentObject var uwaziServerViewModel: UwaziServerViewModel + @EnvironmentObject var serversViewModel: ServersViewModel + @Environment(\.presentationMode) var presentationMode: Binding + + var body: some View { + ContainerView { + ZStack { + VStack { + headerView() + listView() + Spacer() + Rectangle().frame(height: 0.4).foregroundColor(.white) + bottomView() + } + if uwaziServerViewModel.isLoading { + CircularActivityIndicatory() + } + } + } + .onAppear(perform: { + self.uwaziServerViewModel.languages.removeAll() + self.uwaziServerViewModel.getLanguage() + }) + .toolbar { + LeadingTitleToolbar(title: LocalizableSettings.UwaziLanguageTitle.localized) + } + } + fileprivate func bottomView() -> some View { + let isDisable = uwaziServerViewModel.selectedLanguage == nil + return HStack{ + Spacer() + SettingsBottomView(cancelAction: { + self.presentationMode.wrappedValue.dismiss() + }, saveAction: { + uwaziServerViewModel.handleServerAction() + navigateTo(destination: UwaziSuccessView()) + + }, saveActionTitle: LocalizableSettings.UwaziLanguageOk.localized, isDisable: isDisable) + } + .padding(.trailing, 20) + .padding(.top, 12) + } + + fileprivate func headerView() -> some View { + return VStack { + Spacer() + .frame(height: 20) + Text(LocalizableSettings.UwaziLanguageMessage.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .opacity(0.88) + .multilineTextAlignment(.center) + .padding(.trailing, 20) + .padding(.leading, 20) + } + } + + fileprivate func listView() -> some View { + return List { + ForEach(uwaziServerViewModel.languages, id:\.locale) { item in + UwaziLanguageItemView(languageItem: item, + selectedLanguage: $uwaziServerViewModel.selectedLanguage, + isPresented: $isPresented) + }.listRowBackground(Color.red) + } + .listStyle(.plain) + .overlay(Group { + if(uwaziServerViewModel.languages.isEmpty) { + ZStack() { + Styles.Colors.backgroundMain + .edgesIgnoringSafeArea(.all) + } + } + }) + } +} +struct UwaziLanguageItemView : View { + + var languageItem : UwaziLanguageRow? + @Binding var selectedLanguage: UwaziLanguageRow? + + @Binding var isPresented : Bool + @EnvironmentObject private var appModel: MainAppModel + var delayTime = 0.1 + + var body: some View { + + ZStack { + HStack { + VStack(alignment: .leading) { + Text(languageItem?.languageName ?? "") + .font(.custom(Styles.Fonts.regularFontName, size: 15)) + .foregroundColor(.white) + + Text(languageItem?.languageName ?? "") + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(.white) + } + Spacer() + if isCurrentLanguage(languageItem: languageItem) { + Image("settings.done") + } + + } + Button("") { + selectedLanguage = languageItem + DispatchQueue.main.asyncAfter(deadline: .now() + delayTime) { + isPresented = false + } + } + + }.padding(EdgeInsets(top: 7, leading: 20, bottom: 11, trailing: 16)) + .frame(height: 70) + .listRowBackground(isCurrentLanguage(languageItem: languageItem) ? Color.white.opacity(0.15) : Color.clear ) + .listRowInsets(EdgeInsets()) + } + + func isCurrentLanguage(languageItem: UwaziLanguageRow?) -> Bool { + guard let languageItem = languageItem else { return false } + if let selectedLanguage = selectedLanguage { + return selectedLanguage.locale == languageItem.locale + } else { + return false + } + } +} + +struct UwaziLanguageSelectionView_Previews: PreviewProvider { + static var previews: some View { + UwaziLanguageSelectionView(isPresented: .constant(true)) + .environmentObject(SettingsViewModel(appModel: MainAppModel.stub())) + } +} + diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift new file mode 100644 index 000000000..c95800ae0 --- /dev/null +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift @@ -0,0 +1,43 @@ +// +// UwaziSuccessView.swift +// Tella +// +// Created by Robert Shrestha on 4/27/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziSuccessView: View { + var body: some View { + ContainerView { + VStack(spacing: 10) { + Spacer() + TopServerView(title: LocalizableSettings.UwaziSuccess.localized) + Text(LocalizableSettings.UwaziSuccessMessage.localized) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + Spacer().frame(height: 53) + Image("settings.checked-circle") + Spacer() + BottomLockView(isValid: .constant(true), + nextButtonAction: .action, + shouldHideNext: false, + shouldHideBack: true, + nextAction: { + self.popTo(UIHostingController>>>>.self) + }) + }.padding(.leading, 23) + .padding(.trailing, 23) + + } + .navigationBarBackButtonHidden(true) + } +} + +struct UwaziSuccessView_Previews: PreviewProvider { + static var previews: some View { + UwaziSuccessView() + } +} diff --git a/Tella/Scenes/Settings/Views/SettingsMainView.swift b/Tella/Scenes/Settings/Views/SettingsMainView.swift index caff28fbb..b0fcdd89b 100644 --- a/Tella/Scenes/Settings/Views/SettingsMainView.swift +++ b/Tella/Scenes/Settings/Views/SettingsMainView.swift @@ -94,7 +94,6 @@ struct SettingsMainView: View { } } - struct SettingsMainView_Previews: PreviewProvider { static var previews: some View { SettingsMainView(appModel: MainAppModel.stub()) diff --git a/Tella/Scenes/Uwazi/Models/TemplateActionType.swift b/Tella/Scenes/Uwazi/Models/TemplateActionType.swift new file mode 100644 index 000000000..dee6ef73a --- /dev/null +++ b/Tella/Scenes/Uwazi/Models/TemplateActionType.swift @@ -0,0 +1,34 @@ +// +// TemplateActionType.swift +// Tella +// +// Created by Gustavo on 22/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +enum TemplateActionType: ActionType { + case delete +} +enum DownloadedTemplateActionType: ActionType { + case delete + case createEntity +} + + +var templateActionItems : [ListActionSheetItem] { return [ + ListActionSheetItem(imageName: "delete-icon-white", + content: LocalizableUwazi.uwaziDeleteEntitySheetExpl.localized, + type: TemplateActionType.delete) + ] +} +var downloadTemplateActionItems : [ListActionSheetItem] { return [ + ListActionSheetItem(imageName: "edit-icon", + content: LocalizableUwazi.uwaziCreateEntitySheetExpl.localized, + type: DownloadedTemplateActionType.createEntity), + ListActionSheetItem(imageName: "delete-icon-white", + content: LocalizableUwazi.uwaziDeleteEntitySheetExpl.localized, + type: DownloadedTemplateActionType.delete) + ] +} diff --git a/Tella/Scenes/Uwazi/Models/UwaziAttachment.swift b/Tella/Scenes/Uwazi/Models/UwaziAttachment.swift new file mode 100644 index 000000000..887c2746f --- /dev/null +++ b/Tella/Scenes/Uwazi/Models/UwaziAttachment.swift @@ -0,0 +1,23 @@ +// +// UwaziAttachment.swift +// Tella +// +// Created by Gustavo on 30/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +public struct UwaziAttachment { + var filename: String + var data: Data + var fileExtension: String + var mimeType: String = "" + + public init(filename: String, data: Data, fileExtension: String) { + self.filename = filename + self.data = data + self.fileExtension = fileExtension + self.mimeType = MIMEType.mime(for: fileExtension) + } +} diff --git a/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift b/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift new file mode 100644 index 000000000..c4b3f06ea --- /dev/null +++ b/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift @@ -0,0 +1,34 @@ +// +// UwaziAttachmentsActionItems.swift +// Tella +// +// Created by Gustavo on 02/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +var addFileToDraftItems : [ListActionSheetItem] { return [ + + ListActionSheetItem(imageName: "report.camera-filled", + content: LocalizableReport.cameraFilled.localized, + type: ManageFileType.camera), + ListActionSheetItem(imageName: "report.mic-filled", + content: LocalizableReport.micFilled.localized, + type: ManageFileType.recorder), + ListActionSheetItem(imageName: "report.gallery", + content: LocalizableReport.galleryFilled.localized, + type: ManageFileType.tellaFile), + ListActionSheetItem(imageName: "report.phone", + content: LocalizableReport.phoneFilled.localized, + type: ManageFileType.fromDevice) + ]} + +var addFileToPdfItems: [ListActionSheetItem] { return [ + ListActionSheetItem(imageName: "report.gallery", + content: LocalizableReport.galleryFilled.localized, + type: ManageFileType.tellaFile), + ListActionSheetItem(imageName: "report.phone", + content: LocalizableReport.phoneFilled.localized, + type: ManageFileType.fromDevice) +]} diff --git a/Tella/Scenes/Uwazi/Models/UwaziConstants.swift b/Tella/Scenes/Uwazi/Models/UwaziConstants.swift new file mode 100644 index 000000000..bfb42e64a --- /dev/null +++ b/Tella/Scenes/Uwazi/Models/UwaziConstants.swift @@ -0,0 +1,41 @@ +// +// UwaziConstants.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +enum UwaziEntityPropertyType: String, CodingKey { + case dataTypeText = "text" + case dataTypeNumeric = "numeric" + case dataTypeSelect = "select" + case dataTypeMultiSelect = "multiselect" + case dataTypeDate = "date" + case dataTypeDateRange = "daterange" + case dataTypeMultiDate = "multidate" + case dataTypeMultiDateRange = "multidaterange" + case dataTypeMarkdown = "markdown" + case dataTypeLink = "link" + case dataTypeImage = "image" + case dataTypePreview = "preview" + case dataTypeMedia = "media" + case dataTypeGeolocation = "geolocation" + case dataTypeMultiFiles = "multifiles" + case dataTypeMultiPDFFiles = "multipdffiles" + case dataTypeGeneratedID = "generatedid" + case dataTypeDivider = "divider" +} + +struct UwaziEntityMetadataKeys { + static let attachments = "attachments" + static let documents = "documents" + static let template = "template" + static let metadata = "metadata" + static let entity = "entity" + static let title = "title" + static let value = "value" + static let label = "label" +} diff --git a/Tella/Scenes/Uwazi/Models/UwaziPages.swift b/Tella/Scenes/Uwazi/Models/UwaziPages.swift new file mode 100644 index 000000000..c72ca6c1a --- /dev/null +++ b/Tella/Scenes/Uwazi/Models/UwaziPages.swift @@ -0,0 +1,13 @@ +// +// Copyright © 2021 HORIZONTAL. All rights reserved. +// + +import Foundation + + +public enum UwaziPages : Int { + case templates + case draft + case outbox + case submitted +} diff --git a/Tella/Scenes/Uwazi/Utils/Protocol/UwaziEntityParserProtocol.swift b/Tella/Scenes/Uwazi/Utils/Protocol/UwaziEntityParserProtocol.swift new file mode 100644 index 000000000..4f6592cb9 --- /dev/null +++ b/Tella/Scenes/Uwazi/Utils/Protocol/UwaziEntityParserProtocol.swift @@ -0,0 +1,21 @@ +// +// UwaziEntityParserProtocol.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +protocol UwaziEntityParserProtocol { + var entryPrompts: [UwaziEntryPrompt] { get set } + var template: CollectedTemplate { get set } + func handleEntryPrompts() + func getEntryPrompts() -> [UwaziEntryPrompt] +} +extension UwaziEntityParserProtocol { + func getEntryPrompts() -> [UwaziEntryPrompt] { + return entryPrompts + } +} diff --git a/Tella/Scenes/Uwazi/Utils/UwaziEntityParser.swift b/Tella/Scenes/Uwazi/Utils/UwaziEntityParser.swift new file mode 100644 index 000000000..17074509e --- /dev/null +++ b/Tella/Scenes/Uwazi/Utils/UwaziEntityParser.swift @@ -0,0 +1,84 @@ +// +// UwaziEntityParser.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +class UwaziEntityParser: UwaziEntityParserProtocol { + var entryPrompts: [UwaziEntryPrompt] = [] + var template: CollectedTemplate + let uwaziTitleString = "title" + + init(template: CollectedTemplate) { + self.template = template + handleEntryPrompts() + } + func handleEntryPrompts() { + handlePdfsPrompt() + handleSupportPrompt() + handleDividerPrompt() + handleTitlePrompt() + handleEntryPromptForProperties() + } + + + fileprivate func handlePdfsPrompt() { + let pdfPrompt = UwaziEntryPrompt(id: "10242050", + formIndex: "10242050", + type: UwaziEntityPropertyType.dataTypeMultiPDFFiles.rawValue, + question: LocalizableUwazi.uwaziMultiFileWidgetPrimaryDocuments.localized, + required: false, + helpText: LocalizableUwazi.uwaziMultiFileWidgetAttachManyPDFFiles.localized, + name: UwaziEntityPropertyType.dataTypeMultiPDFFiles.rawValue) + entryPrompts.append(pdfPrompt) + } + fileprivate func handleSupportPrompt() { + let supportPrompt = UwaziEntryPrompt(id: "10242049", + formIndex: "10242049", + type: UwaziEntityPropertyType.dataTypeMultiFiles.rawValue, + question: LocalizableUwazi.uwaziMultiFileWidgetSupportingFiles.localized, + required: false, + helpText: LocalizableUwazi.uwaziMultiFileWidgetSelectManyFiles.localized, + name: UwaziEntityPropertyType.dataTypeMultiFiles.rawValue) + entryPrompts.append(supportPrompt) + } + fileprivate func handleDividerPrompt() { + let dividerPrompt = UwaziEntryPrompt(id: "", + formIndex: "", + type: UwaziEntityPropertyType.dataTypeDivider.rawValue, + question: "", + required: false, + helpText: "", + name: "") + entryPrompts.append(dividerPrompt) + } + + fileprivate func handleTitlePrompt() { + guard let titleProperty = template.entityRow?.commonProperties.first (where:{ $0.name == uwaziTitleString }) else { return } + let titlePrompt = UwaziEntryPrompt(id: titleProperty.id ?? "", + formIndex: titleProperty.id, + type: titleProperty.type ?? "", + question: titleProperty.translatedLabel ?? "", + required: true, + helpText: titleProperty.translatedLabel, + name:titleProperty.name) + self.entryPrompts.append(titlePrompt) + } + fileprivate func handleEntryPromptForProperties() { + let entryPromptyProperties = template.entityRow?.properties.compactMap { + UwaziEntryPrompt(id: $0.id ?? "", + formIndex: $0.id, + type: $0.type ?? "", + question: $0.translatedLabel ?? "", + required: $0.propertyRequired, + helpText: $0.translatedLabel, + selectValues: $0.values, + name: $0.name) + + } ?? [] + entryPrompts.append(contentsOf: entryPromptyProperties) + } +} diff --git a/Tella/Scenes/Uwazi/Utils/UwaziFileUtility.swift b/Tella/Scenes/Uwazi/Utils/UwaziFileUtility.swift new file mode 100644 index 000000000..f1196b2e2 --- /dev/null +++ b/Tella/Scenes/Uwazi/Utils/UwaziFileUtility.swift @@ -0,0 +1,43 @@ +// +// UwaziFileUtility.swift +// Tella +// +// Created by Gustavo on 02/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + + +struct UwaziFileUtility { + var files: Set + var mainAppModel: MainAppModel? + + func getFilesInfo() -> [UwaziAttachment] { + return files.compactMap { file in + if let fileData = self.mainAppModel?.vaultManager.loadFileData(fileName: file.id) { + return UwaziAttachment(filename: file.name, data: fileData, fileExtension: file.fileExtension) + } else { + return nil + } + } + } + + func extractFilesAsAttachments() ->[[String: Any]] { + var attachments = [[String: Any]]() + for file in files { + let attachment = [ + "originalname": "\(file.name).\(file.fileExtension)", + "filename": "\(file.name).\(file.fileExtension)", + "type": "attachment", + "mimetype": MIMEType.mime(for: file.fileExtension), + "entity": "NEW_ENTITY" + ] as [String: Any] + + attachments.append(attachment) + } + + return attachments + } + +} diff --git a/Tella/Scenes/Uwazi/Utils/UwaziMultipartData.swift b/Tella/Scenes/Uwazi/Utils/UwaziMultipartData.swift new file mode 100644 index 000000000..2d9a9d9f6 --- /dev/null +++ b/Tella/Scenes/Uwazi/Utils/UwaziMultipartData.swift @@ -0,0 +1,57 @@ +// +// UwaziMultipartData.swift +// Tella +// +// Created by Gustavo on 03/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +struct UwaziMultipartFormDataBuilder { + + static func createBodyWith( + keyValues: [String: Any], + attachments: [UwaziAttachment]?, + documents: [UwaziAttachment]? + ) -> (body: Data, ContentTypeHeader: String) { + + let keyValues = keyValues.compactMapValues { $0 } + var multipartRequest = MultipartRequest() + + for (key, value) in keyValues { + if let jsonData = try? JSONSerialization.data(withJSONObject: value, options: []) { + if let jsonString = String(data: jsonData, encoding: .utf8) { + multipartRequest.add(key: key, value: jsonString) + } + } + } + + // Add attachments + attachments?.enumerated().forEach { index, attachment in + multipartRequest.add( + key: "attachments[\(index)]", + fileName: attachment.filename, + fileMimeType: attachment.mimeType, + fileData: attachment.data + ) + multipartRequest.add(key: "attachments_originalname[\(index)]", value: attachment.filename) + } + + // Add documents + documents?.enumerated().forEach { index, document in + multipartRequest.add( + key: "documents[\(index)]", + fileName: document.filename, + fileMimeType: document.mimeType, + fileData: document.data + ) + multipartRequest.add(key: "documents_originalname[\(index)]", value: document.filename) + } + + return ( + body: multipartRequest.httpBody, + ContentTypeHeader: multipartRequest.httpContentTypeHeadeValue + ) + } +} diff --git a/Tella/Scenes/Uwazi/UwaziView.swift b/Tella/Scenes/Uwazi/UwaziView.swift new file mode 100644 index 000000000..8751b98c3 --- /dev/null +++ b/Tella/Scenes/Uwazi/UwaziView.swift @@ -0,0 +1,83 @@ +// +// UwaziView.swift +// Tella +// +// Created by Gustavo on 27/07/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziView: View { + @EnvironmentObject var uwaziViewModel: UwaziViewModel + + var body: some View { + contentView + .navigationBarTitle(LocalizableUwazi.uwaziTitle.localized, displayMode: .large) + .environmentObject(uwaziViewModel) + } + + private var contentView :some View { + + ContainerView { + VStack(alignment: .center) { + + PageView(selectedOption: $uwaziViewModel.selectedCell, pageViewItems: uwaziViewModel.pageViewItems) + .frame(maxWidth: .infinity, maxHeight: 40, alignment: .leading) + + VStack (spacing: 0) { + Spacer() + switch self.uwaziViewModel.selectedCell { + + case .template: + TemplateListView( + message: LocalizableUwazi.uwaziTemplateListEmptyExpl.localized, serverName: uwaziViewModel.serverName) + .environmentObject(DownloadedTemplatesViewModel(mainAppModel: uwaziViewModel.mainAppModel, serverId: uwaziViewModel.server.id!)) + case .draft: + ReportListView(reportArray: $uwaziViewModel.draftEntities, + message: LocalizableReport.reportsDraftEmpty.localized) + + case .outbox: + + ReportListView(reportArray: $uwaziViewModel.outboxedEntities, + message: LocalizableReport.reportsOutboxEmpty.localized) + + case .submitted: + ReportListView(reportArray: $uwaziViewModel.submittedEntities, + message: LocalizableReport.reportsSubmitedEmpty.localized) + } + + Spacer() + } + + AddFileYellowButton(action: { + navigateTo(destination: AddTemplatesView() + .environmentObject(AddTemplateViewModel(mainAppModel: uwaziViewModel.mainAppModel, serverId: uwaziViewModel.server.id!))) + }).frame(maxWidth: .infinity, maxHeight: 40, alignment: .leading) + + }.background(Styles.Colors.backgroundMain) + .padding(EdgeInsets(top: 15, leading: 20, bottom: 16, trailing: 20)) + } + .navigationBarBackButtonHidden(true) + .navigationBarItems(leading: backButton) + + } + + var backButton : some View { + Button { + self.popToRoot() + } label: { + Image("back") + .flipsForRightToLeftLayoutDirection(true) + .padding(EdgeInsets(top: -3, leading: -8, bottom: 0, trailing: 12)) + } + } + + +} + +struct UwaziView_Previews: PreviewProvider { + static var previews: some View { + UwaziView() + } +} diff --git a/Tella/Scenes/Uwazi/ViewModel/AddTemplateVM.swift b/Tella/Scenes/Uwazi/ViewModel/AddTemplateVM.swift new file mode 100644 index 000000000..3a67b831e --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/AddTemplateVM.swift @@ -0,0 +1,156 @@ +// +// AddTemplateVM.swift +// Tella +// +// Created by Gustavo on 27/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import Combine + +class AddTemplateViewModel: ObservableObject { + + var mainAppModel : MainAppModel + + @Published private var templates : [CollectedTemplate] = [] + + @Published var templateItemsViewModel : [TemplateItemViewModel] = [] + + @Published var isLoading: Bool = false + @Published var showToast: Bool = false + + var errorMessage: String = "" + var serverName : String = "" + var subscribers = Set() + var server: UwaziServer? = nil + + var tellaData: TellaData? { + return self.mainAppModel.vaultManager.tellaData + } + + init(mainAppModel : MainAppModel, serverId: Int) { + + self.mainAppModel = mainAppModel + self.server = self.getServerById(id: serverId) + self.serverName = server?.name ?? "" + } + + func getServerById(id: Int) -> UwaziServer { + return (self.tellaData?.getUwaziServer(serverId: id))! + } + + func getTemplates() { + self.isLoading = true + Task { + guard let id = self.server?.id else { return } + let template = UwaziServerRepository().handleTemplate(server: self.server!) + template.receive(on: DispatchQueue.main).sink { completion in + self.handleGetTemplateCompletion(completion) + } receiveValue: { templates in + self.handleRecieveValue(self.mapToCollectedTemplate(serverId: id, templates)) + }.store(in: &subscribers) + } + } + + + fileprivate func mapToCollectedTemplate(serverId: Int, _ templates: [UwaziTemplateRow]) -> [CollectedTemplate] { + return templates.map { template in + return CollectedTemplate(serverId: serverId, + templateId: template.id, + serverName: self.server?.name ?? "", + username: self.server?.username, + entityRow: template, + isDownloaded: false, + isFavorite: false, + isUpdated: false) + } + } + + func downloadTemplate(template: CollectedTemplate) { + var template = template + self.downloadTemplate(template: &template) + self.templateItemsViewModel.first(where: {template.templateId == $0.id})?.isDownloaded = true + } + + fileprivate func handleGetTemplateCompletion(_ completion: Subscribers.Completion) { + switch completion { + case .finished: + debugLog("Fetching template completed.") + case .failure(let error): + showToast = true + errorMessage = error.errorDescription ?? "" + } + self.isLoading = false + } + + fileprivate func handleRecieveValue(_ templates: [CollectedTemplate]) { + self.handleTemplateDownload(templates: templates) + self.templates = templates + self.isLoading = false + + self.templateItemsViewModel = self.templates.map({ collectedTemplate in + TemplateItemViewModel(template: collectedTemplate, + downloadTemplate: {self.downloadTemplate(template: collectedTemplate)} , + deleteTemplate: {self.deleteTemplate(template:collectedTemplate)}) + }) + } + + + func handleDeleteActionsForAddTemplate(item: ListActionSheetItem, template: CollectedTemplate, completion: ()-> Void) { + guard let type = item.type as? TemplateActionType else { return } + if type == .delete { + self.deleteTemplate(template: template) + completion() + } + } + + /// To determine if the templates are already download or not reflect on the UI for template download list + /// - Parameter templates: Collection of CollectedTemplate to determine if it downloaded or not + func handleTemplateDownload(templates: [CollectedTemplate]) { + templates.forEach { template in + let savedTemplateid = self.getAllDownloadedTemplate()?.compactMap({$0.templateId}) + if let savedTemplate = savedTemplateid,let templateId = template.templateId { + if savedTemplate.contains(templateId) { + template.isDownloaded = true + } + } + } + } + + /// Save the template to the database + /// - Parameter template: The template that we need to save into the database + func saveTemplate( template: inout CollectedTemplate) { + let savedTemplateid = self.getAllDownloadedTemplate()?.compactMap({$0.templateId}) + if let savedTemplate = savedTemplateid,let templateId = template.templateId { + // To only save the template if it is not already saved Not necessary because the UI will not have a download button if it is already downloaded + if !savedTemplate.contains(templateId) { + guard let savedItem = self.tellaData?.addUwaziTemplate(template: template) else { return } + template = savedItem + } + } + } + + /// Delete the saved template from database using the template id of the template and changing the status of isDownloaded property to 0 for template listing view + /// - Parameter template: The CollectedTemplate Object and changing the status of isDownloaded property to 0 + func deleteTemplate(template: CollectedTemplate) { + if let templateId = template.templateId { + _ = self.tellaData?.deleteAllUwaziTemplate(templateId: templateId) + template.isDownloaded = false + self.templateItemsViewModel.first(where: {template.templateId == $0.id})?.isDownloaded = false + } + } + + /// Get all the downloaded templates + /// - Returns: Collection of CollectedTemplate object which are stored in the database + func getAllDownloadedTemplate() -> [CollectedTemplate]? { + self.tellaData?.getAllUwaziTemplate() + } + + + func downloadTemplate(template: inout CollectedTemplate) -> Void { + isLoading = true + self.saveTemplate(template: &template) + isLoading = false + } +} diff --git a/Tella/Scenes/Uwazi/ViewModel/DownloadedTemplatesVM.swift b/Tella/Scenes/Uwazi/ViewModel/DownloadedTemplatesVM.swift new file mode 100644 index 000000000..dcd373a11 --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/DownloadedTemplatesVM.swift @@ -0,0 +1,53 @@ +// +// DownloadedTemplatesVM.swift +// Tella +// +// Created by Gustavo on 27/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import Combine + +class DownloadedTemplatesViewModel: ObservableObject { + + var mainAppModel : MainAppModel + var server: UwaziServer? = nil + + @Published private var downloadedTemplates : [CollectedTemplate] = [] + + @Published var templateCardsViewModel : [TemplateCardViewModel] = [] + + var tellaData: TellaData? { + return self.mainAppModel.vaultManager.tellaData + } + + init(mainAppModel : MainAppModel, serverId: Int) { + self.mainAppModel = mainAppModel + self.server = self.getServerById(id: serverId) + } + + func getServerById(id: Int) -> UwaziServer? { + return (self.tellaData?.getUwaziServer(serverId: id)) + } + + func getDownloadedTemplates() { + self.downloadedTemplates = self.tellaData?.getAllUwaziTemplate() ?? [] + self.templateCardsViewModel = self.downloadedTemplates.map({ collectedTemplate in + TemplateCardViewModel(template: collectedTemplate, + deleteTemplate: {self.deleteDownloadedTemplate(templateId:collectedTemplate.id)}, + serverName: self.server?.name ?? "", + serverId: (self.server?.id)! + ) + }) + } + + /// Delete the saved template from database using the template id of the template for downloaded template listing view + /// - Parameter template: The template object which we need to delete + func deleteDownloadedTemplate(templateId: Int?) { + guard let templateId else { return } + self.tellaData?.deleteAllUwaziTemplate(id: templateId) + getDownloadedTemplates() + } + +} diff --git a/Tella/Scenes/Uwazi/ViewModel/TemplateCardViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/TemplateCardViewModel.swift new file mode 100644 index 000000000..ab657553a --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/TemplateCardViewModel.swift @@ -0,0 +1,39 @@ +// +// TemplateCardViewModel.swift +// Tella +// +// Created by Robert Shrestha on 9/23/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class TemplateCardViewModel: Hashable { + + var id : Int? + var translatedName: String + var deleteTemplate: (() -> Void) + var serverName: String + var serverId: Int + + init(template : CollectedTemplate, + deleteTemplate: @escaping (() -> Void), + serverName: String, + serverId: Int + ) { + self.id = template.id + self.translatedName = template.entityRow?.translatedName ?? "" + self.deleteTemplate = deleteTemplate + self.serverId = serverId + self.serverName = serverName + } + + static func == (lhs: TemplateCardViewModel, rhs: TemplateCardViewModel) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + +} diff --git a/Tella/Scenes/Uwazi/ViewModel/TemplateItemViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/TemplateItemViewModel.swift new file mode 100644 index 000000000..3a3009aa5 --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/TemplateItemViewModel.swift @@ -0,0 +1,36 @@ +// +// TemplateItemViewModel.swift +// Tella +// +// Created by Robert Shrestha on 9/23/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +class TemplateItemViewModel: Hashable { + + var id : String? + var name: String + var isDownloaded : Bool + var downloadTemplate : (() -> Void) + var deleteTemplate: (() -> Void) + + init(template : CollectedTemplate, + downloadTemplate: @escaping (() -> Void), + deleteTemplate: @escaping (() -> Void) ) { + self.id = template.templateId + self.name = template.entityRow?.name ?? "" + self.isDownloaded = template.isDownloaded ?? false + self.downloadTemplate = downloadTemplate + self.deleteTemplate = deleteTemplate + } + + static func == (lhs: TemplateItemViewModel, rhs: TemplateItemViewModel) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} diff --git a/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift new file mode 100644 index 000000000..715f76e2e --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift @@ -0,0 +1,227 @@ +// +// UwaziEntityViewModel.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import SwiftUI +import Combine + +class UwaziEntityViewModel: ObservableObject { + + var mainAppModel : MainAppModel + + @Published var template: CollectedTemplate? = nil + @Published var entryPrompts: [UwaziEntryPrompt] = [] + + // files + @Published var files : Set = [] + @Published var pdfDocuments: Set = [] + @Published var resultFile : [VaultFileDB]? + + @Published var showingSuccessMessage : Bool = false + @Published var showingImagePicker : Bool = false + @Published var showingImportDocumentPicker : Bool = false + @Published var showingFileList : Bool = false + @Published var showingRecordView : Bool = false + @Published var showingCamera : Bool = false + + @Published var isLoading : Bool = false + @Published var server: UwaziServer? = nil + + var subscribers = Set() + + init(mainAppModel : MainAppModel, templateId: Int, serverId: Int) { + self.mainAppModel = mainAppModel + self.template = self.getTemplateById(id: templateId) + self.bindVaultFileTaken() + self.server = self.getServerById(id: serverId) + entryPrompts = UwaziEntityParser(template: template!).getEntryPrompts() + } + + var tellaData: TellaData? { + return self.mainAppModel.vaultManager.tellaData + } + + + func getTemplateById (id: Int) -> CollectedTemplate { + return (self.tellaData?.getUwaziTemplateById(id: id))! + } + + func getServerById(id: Int) -> UwaziServer { + return (self.tellaData?.getUwaziServer(serverId: id))! + } + + + func getEntityTitle() -> String { + return self.entryPrompts.first(where: { $0.name == UwaziEntityMetadataKeys.title })?.value.stringValue ?? "" + } + + func handleMandatoryProperties() -> Bool { + let requiredPrompts = entryPrompts.filter({$0.required ?? false}) + var hasMandatoryErrors = false + + requiredPrompts.forEach { prompt in + prompt.showMandatoryError = prompt.value.stringValue.isEmpty + if prompt.value.stringValue.isEmpty { + prompt.showMandatoryError = true + hasMandatoryErrors = true + } + } + + return hasMandatoryErrors + } + + func submitEntity(onCompletion: @escaping () -> Void) { + self.isLoading = true + let isPublic = server?.accessToken == nil + // Extract entity data and metadata + let entityData = extractEntityDataAndMetadata() + +// Submit the entity data + let (body, contentTypeHeader) = UwaziMultipartFormDataBuilder.createBodyWith( + keyValues: entityData, + attachments: UwaziFileUtility(files: files, mainAppModel: mainAppModel).getFilesInfo(), + documents: UwaziFileUtility(files: pdfDocuments, mainAppModel: mainAppModel).getFilesInfo() + ) + + let response = UwaziServerRepository().submitEntity( + serverURL: self.server?.url ?? "", + cookie: self.server?.cookie ?? "", + multipartHeader: contentTypeHeader, + multipartBody: body, + isPublic: isPublic + ) + response + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + self?.isLoading = false + switch completion { + case .finished: + debugLog("Finished") + Toast.displayToast(message: LocalizableUwazi.uwaziEntitySubmitted.localized) + onCompletion() + case .failure(let error): + debugLog(error.localizedDescription) + Toast.displayToast(message: LocalizableUwazi.uwaziEntityFailedSubmission.localized) + } + } receiveValue: { value in + debugLog(value) + } + .store(in: &subscribers) + } + + private func extractEntityDataAndMetadata() -> ([String: Any]) { + let attachments = extractAttachmentsIfAny() + let documents = extractDocumentsIfAny() + + var entityData: [String: Any] = [ + UwaziEntityMetadataKeys.attachments: attachments, + UwaziEntityMetadataKeys.documents: documents, + UwaziEntityMetadataKeys.template: template?.templateId ?? "" + ] + + var metadata: [String: Any] = [:] + + for entryPrompt in entryPrompts { + guard let propertyName = entryPrompt.name else { break } + + switch UwaziEntityPropertyType(rawValue: entryPrompt.type) { + case .dataTypeText where propertyName == UwaziEntityMetadataKeys.title: + entityData[propertyName] = entryPrompt.value.stringValue + case .dataTypeText, .dataTypeNumeric, .dataTypeMarkdown: + metadata[propertyName] = [[UwaziEntityMetadataKeys.value: entryPrompt.value.stringValue]] + case .dataTypeDate: + metadata[propertyName] = [[UwaziEntityMetadataKeys.value: Int(entryPrompt.value.stringValue)]] + case .dataTypeSelect, .dataTypeMultiSelect: + if let selectedValue = entryPrompt.value.selectedValue.first { + metadata[propertyName] = [[UwaziEntityMetadataKeys.value: selectedValue.id, UwaziEntityMetadataKeys.label: selectedValue.label]] + } + default: + break + } + } + + entityData[UwaziEntityMetadataKeys.metadata] = metadata + + return [UwaziEntityMetadataKeys.entity: entityData] + } + + + private func extractAttachmentsIfAny() -> [[String: Any]] { + !files.isEmpty ? UwaziFileUtility(files: files).extractFilesAsAttachments() : [] + } + + private func extractDocumentsIfAny() -> [[String: Any]] { + !pdfDocuments.isEmpty ? UwaziFileUtility(files: pdfDocuments).extractFilesAsAttachments() : [] + } + + func getEntityResponseSize() -> String { + do { + let entityData = extractEntityDataAndMetadata() + let jsonData = try JSONSerialization.data(withJSONObject: entityData, options: []) + let sizeInBytes = Int(jsonData.count) + let sizeInMB = sizeInBytes.getFormattedFileSize() + return sizeInMB + } catch { + debugLog(error) + return "\(error)" + } + } + + // files + private func bindVaultFileTaken() { + $resultFile + .sink(receiveValue: { [weak self] files in + // Unwrap files safely + guard let self = self, let files = files else { return } + + files.forEach { file in + if file.tellaFileType == .document { + self.pdfDocuments.insert(file) + self.toggleShowClear(forId: "10242050", value: true) + } else { + self.files.insert(file) + self.toggleShowClear(forId: "10242049", value: true) + } + } + self.publishUpdates() + }) + .store(in: &subscribers) + } + + + private func publishUpdates() { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + + func toggleShowClear(forId id: String, value: Bool) { + for entryPrompt in entryPrompts { + if entryPrompt.id == id { + entryPrompt.showClear = value + break + } + } + } + + func clearValues(forId id: String) { + if id == "10242050" { + pdfDocuments.removeAll() + } else if id == "10242049" { + files.removeAll() + } else { + if let index = entryPrompts.firstIndex(where: { $0.id == id }) { + entryPrompts[index].value.stringValue = "" + entryPrompts[index].value.selectedValue = [] + } + + } + + toggleShowClear(forId: id, value: false) + } +} diff --git a/Tella/Scenes/Uwazi/ViewModel/UwaziTemplateViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/UwaziTemplateViewModel.swift new file mode 100644 index 000000000..b9772efe2 --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/UwaziTemplateViewModel.swift @@ -0,0 +1,165 @@ +// +// UwaziTemplateViewModel.swift +// Tella +// +// Created by Gustavo on 31/07/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import Combine + +class UwaziTemplateViewModel: ObservableObject { + + var mainAppModel : MainAppModel + + @Published private var templates : [CollectedTemplate] = [] + @Published private var downloadedTemplates : [CollectedTemplate] = [] + + @Published var templateItemsViewModel : [TemplateItemViewModel] = [] + @Published var templateCardsViewModel : [TemplateCardViewModel] = [] + + @Published var isLoading: Bool = false + @Published var serverName : String + var subscribers = Set() + var server: Server + + var tellaData: TellaData? { + return self.mainAppModel.vaultManager.tellaData + } + + init(mainAppModel : MainAppModel, server: Server) { + + self.mainAppModel = mainAppModel + self.server = server + self.serverName = server.name ?? "" + } + + func getTemplates() { + self.isLoading = true + Task { + guard let id = self.server.id else { return } + guard let locale = self.tellaData?.getUwaziLocale(serverId: id) else { return } + let template = try await UwaziServerRepository().handleTemplate(server: self.server, locale: locale) + template.receive(on: DispatchQueue.main).sink { completion in + self.handleGetTemplateCompletion(completion) + } receiveValue: { templates in + self.handleRecieveValue(self.mapToCollectedTemplate(serverId: id, templates)) + }.store(in: &subscribers) + } + } + + + fileprivate func mapToCollectedTemplate(serverId: Int, _ templates: [UwaziTemplateRow]) -> [CollectedTemplate] { + return templates.map { template in + return CollectedTemplate(serverId: serverId, + templateId: template.id, + serverName: self.server.name ?? "", + username: self.server.username, + entityRow: template, + isDownloaded: false, + isFavorite: false, + isUpdated: false) + } + } + + func downloadTemplate(template: CollectedTemplate) { + var template = template + Toast.displayToast(message: "“\(template.entityRow?.translatedName ?? "")” “\(LocalizableUwazi.uwaziAddTemplateSavedToast.localized)”") + self.downloadTemplate(template: &template) + } + + fileprivate func handleGetTemplateCompletion(_ completion: Subscribers.Completion) { + switch completion { + case .finished: + debugLog("Fetching template completed.") + case .failure(let error): + debugLog("Error: \(error.localizedDescription)") + } + self.isLoading = false + } + + fileprivate func handleRecieveValue(_ templates: [CollectedTemplate]) { + self.handleTemplateDownload(templates: templates) + self.templates = templates + self.isLoading = false + + self.templateItemsViewModel = self.templates.map({ collectedTemplate in + TemplateItemViewModel(template: collectedTemplate, + downloadTemplate: {self.downloadTemplate(template: collectedTemplate)} , + deleteTemplate: {self.deleteDownloadedTemplate(templateId:collectedTemplate.id)}) + }) + } + + func getDownloadedTemplates() { + self.downloadedTemplates = self.getAllDownloadedTemplate() ?? [] + + self.templateCardsViewModel = self.downloadedTemplates.map({ collectedTemplate in + TemplateCardViewModel(template: collectedTemplate, + deleteTemplate: {self.deleteDownloadedTemplate(templateId:collectedTemplate.id)}) + }) + } + + func handleDeleteActionsForAddTemplate(item: ListActionSheetItem, template: CollectedTemplate, completion: ()-> Void) { + guard let type = item.type as? TemplateActionType else { return } + if type == .delete { + self.deleteTemplate(template: template) + completion() + } + } + + /// To determine if the templates are already download or not reflect on the UI for template download list + /// - Parameter templates: Collection of CollectedTemplate to determine if it downloaded or not + func handleTemplateDownload(templates: [CollectedTemplate]) { + templates.forEach { template in + let savedTemplateid = self.getAllDownloadedTemplate()?.compactMap({$0.templateId}) + if let savedTemplate = savedTemplateid,let templateId = template.templateId { + if savedTemplate.contains(templateId) { + template.isDownloaded = true + } + } + } + } + + /// Save the template to the database + /// - Parameter template: The template that we need to save into the database + func saveTemplate( template: inout CollectedTemplate) { + let savedTemplateid = self.getAllDownloadedTemplate()?.compactMap({$0.templateId}) + if let savedTemplate = savedTemplateid,let templateId = template.templateId { + // To only save the template if it is not already saved Not necessary because the UI will not have a download button if it is already downloaded + if !savedTemplate.contains(templateId) { + guard let savedItem = self.tellaData?.addUwaziTemplate(template: template) else { return } + template = savedItem + } + } + } + + /// Delete the saved template from database using the template id of the template and changing the status of isDownloaded property to 0 for template listing view + /// - Parameter template: The CollectedTemplate Object and changing the status of isDownloaded property to 0 + func deleteTemplate(template: CollectedTemplate) { + if let templateId = template.templateId { + _ = self.tellaData?.deleteAllUwaziTemplate(templateId: templateId) + template.isDownloaded = false + } + } + + /// Get all the downloaded templates + /// - Returns: Collection of CollectedTemplate object which are stored in the database + func getAllDownloadedTemplate() -> [CollectedTemplate]? { + self.tellaData?.getAllUwaziTemplate() + } + + /// Delete the saved template from database using the template id of the template for downloaded template listing view + /// - Parameter template: The template object which we need to delete + func deleteDownloadedTemplate(templateId: Int?) { + guard let templateId else { return } + self.tellaData?.deleteAllUwaziTemplate(id: templateId) + getDownloadedTemplates() + } + + func downloadTemplate(template: inout CollectedTemplate) -> Void { + isLoading = true + self.saveTemplate(template: &template) + isLoading = false + } +} diff --git a/Tella/Scenes/Uwazi/ViewModel/UwaziViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/UwaziViewModel.swift new file mode 100644 index 000000000..490f98b02 --- /dev/null +++ b/Tella/Scenes/Uwazi/ViewModel/UwaziViewModel.swift @@ -0,0 +1,39 @@ +// +// UwaziViewModel.swift +// Tella +// +// Created by Gustavo on 25/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + + +class UwaziViewModel: ObservableObject { + + var mainAppModel : MainAppModel + + @Published var templates : [CollectedTemplate] = [] + @Published var downloadedTemplates : [CollectedTemplate] = [] + @Published var draftEntities : [Report] = [] + @Published var outboxedEntities : [Report] = [] + @Published var submittedEntities : [Report] = [] + @Published var selectedCell = Pages.template + @Published var pageViewItems : [PageViewItem] = [ + PageViewItem(title: LocalizableUwazi.uwaziPageViewTemplate.localized, page: .template, number: 0) + ] + @Published var isLoading: Bool = false + @Published var serverName : String + var server: UwaziServer + + var tellaData: TellaData? { + return self.mainAppModel.vaultManager.tellaData + } + + init(mainAppModel : MainAppModel, server: UwaziServer) { + + self.mainAppModel = mainAppModel + self.server = server + self.serverName = server.name ?? "" + } +} diff --git a/Tella/Scenes/Uwazi/Views/Common/UwaziEmptyView.swift b/Tella/Scenes/Uwazi/Views/Common/UwaziEmptyView.swift new file mode 100644 index 000000000..d4e16da4d --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Common/UwaziEmptyView.swift @@ -0,0 +1,31 @@ +// +// UwaziEmptyView.swift +// Tella +// +// Created by Gustavo on 21/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziEmptyView: View { + var message : String + + var body: some View { + VStack(alignment: .center, spacing: 22) { + Image("uwazi.empty") + Text(message) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .multilineTextAlignment(.center) + }.padding(EdgeInsets(top: 0, leading: 31, bottom: 0, trailing: 31)) + } +} + +struct UwaziEmptyView_Previews: PreviewProvider { + static var previews: some View { + ZStack { + EmptyReportView(message: LocalizableReport.reportsDraftEmpty.localized) + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift b/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift new file mode 100644 index 000000000..07225d2ff --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift @@ -0,0 +1,124 @@ +// +// CreateEntityView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct CreateEntityView: View { + @StateObject var entityViewModel : UwaziEntityViewModel + @EnvironmentObject var sheetManager : SheetManager + @Environment(\.presentationMode) var presentationMode: Binding + let modelHeight = 200.0 + + init(appModel: MainAppModel, templateId: Int, serverId: Int) { + _entityViewModel = StateObject(wrappedValue: UwaziEntityViewModel(mainAppModel: appModel, templateId:templateId, serverId: serverId)) + } + var body: some View { + ContainerView { + contentView + + photoVideoPickerView + } + .navigationBarHidden(true) + .overlay(cameraView) + .overlay(recordView) + } + + fileprivate var contentView: some View { + VStack(alignment: .leading) { + createEntityHeaderView + draftContentView + Spacer() + UwaziDividerWidget() + bottomActionView + } + } + + fileprivate var createEntityHeaderView: some View { + CreateDraftHeaderView(title: entityViewModel.template!.entityRow?.name ?? "", + isDraft: true, + hideSaveButton: true, + closeAction: { showSaveEntityConfirmationView() }, + saveAction: { }) + } + + + + fileprivate var draftContentView: some View { + GeometryReader { geometry in + ScrollView { + VStack(alignment: .leading) { + ForEach(entityViewModel.entryPrompts, id: \.id) { prompt in + RenderPropertyComponentView(prompt: prompt) + .environmentObject(sheetManager) + .environmentObject(entityViewModel) + } + }.padding(EdgeInsets(top: 12, leading: 16, bottom: 0, trailing: 16)) + } + } + } + + fileprivate var bottomActionView: some View { + Button(action: { + let checkMandatoryFields = self.entityViewModel.handleMandatoryProperties() + + if !checkMandatoryFields { + navigateTo(destination: SubmitEntityView(entityViewModel: entityViewModel)) + } + }) { + Text(LocalizableUwazi.uwaziEntityActionNext.localized) + .frame(maxWidth: .infinity, alignment: .trailing) + .padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 32)) + .foregroundColor(Color.white) + } + .buttonStyle(PlainButtonStyle()) + } + + var cameraView : some View { + entityViewModel.showingCamera ? + CameraView(sourceView: SourceView.addReportFile, + showingCameraView: $entityViewModel.showingCamera, + resultFile: $entityViewModel.resultFile, + mainAppModel: entityViewModel.mainAppModel) : nil + } + + var recordView : some View { + entityViewModel.showingRecordView ? + RecordView(appModel: entityViewModel.mainAppModel, + sourceView: .addReportFile, + showingRecoredrView: $entityViewModel.showingRecordView, + resultFile: $entityViewModel.resultFile) : nil + } + + var photoVideoPickerView : some View { + PhotoVideoPickerView(showingImagePicker: $entityViewModel.showingImagePicker, + showingImportDocumentPicker: $entityViewModel.showingImportDocumentPicker, + appModel: entityViewModel.mainAppModel, + resultFile: $entityViewModel.resultFile, + shouldReloadVaultFiles: .constant(false)) + } + + private func showSaveEntityConfirmationView() { + sheetManager.showBottomSheet(modalHeight: modelHeight) { + ConfirmBottomSheet(titleText: LocalizableUwazi.uwaziEntityExitSheetTitle.localized, + msgText: LocalizableUwazi.uwaziEntityExitSheetExpl.localized, + cancelText: LocalizableReport.exitCancel.localized.uppercased(), + actionText: LocalizableSettings.UwaziLanguageCancel.localized.uppercased(), + didConfirmAction: { + + }, didCancelAction: { + dismissViews() + }) + } + } + + private func dismissViews() { + sheetManager.hide() + self.presentationMode.wrappedValue.dismiss() + } + +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift b/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift new file mode 100644 index 000000000..7f07614eb --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift @@ -0,0 +1,72 @@ +// +// RenderPropertyComponentView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct RenderPropertyComponentView: View { + @StateObject var prompt: UwaziEntryPrompt + @EnvironmentObject var sheetManager: SheetManager + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + + var body: some View { + GenericEntityWidget(title: prompt.question, + isRequired: prompt.required ?? false, + showMandatory: $prompt.showMandatoryError, + shouldRender:shouldRenderPrompt(forType: prompt.type), + showClear: prompt.showClear ?? false, + onClearAction: { entityViewModel.clearValues(forId: prompt.id ?? "")} + ) { + renderPropertyComponent( + prompt: prompt + ) + if(prompt.showMandatoryError) { + UwaziEntityMandatoryTextView() + } + } + } + + @ViewBuilder + private func renderPropertyComponent(prompt: UwaziEntryPrompt) -> some View { + switch UwaziEntityPropertyType(rawValue: prompt.type) { + case .dataTypeText, .dataTypeNumeric, .dataTypeMarkdown: + UwaziTextWidget(value: prompt.value) + case .dataTypeSelect, .dataTypeMultiSelect: + UwaziSelectWidget(value: prompt.value) + .environmentObject(prompt) + .environmentObject(entityViewModel) + case .dataTypeMultiFiles: + SupportingFileWidget() + .environmentObject(prompt) + .environmentObject(sheetManager) + .environmentObject(entityViewModel) + case .dataTypeMultiPDFFiles: + PrimaryDocuments() + .environmentObject(prompt) + .environmentObject(sheetManager) + .environmentObject(entityViewModel) + case .dataTypeDivider: + UwaziDividerWidget() + case .dataTypeDate: + UwaziDatePicker(value: prompt.value) + .environmentObject(prompt) + default: + EmptyView() + } + } + + private func shouldRenderPrompt(forType type: String) -> Bool { + guard let propertyType = UwaziEntityPropertyType(rawValue: type) else { return false } + + switch propertyType { + case .dataTypeText, .dataTypeNumeric, .dataTypeSelect, .dataTypeMultiSelect, .dataTypeMultiFiles, .dataTypeMultiPDFFiles, .dataTypeDivider, .dataTypeDate, .dataTypeMarkdown: + return true + default: + return false + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/SubmitEntityView.swift b/Tella/Scenes/Uwazi/Views/Entity/SubmitEntityView.swift new file mode 100644 index 000000000..b4c0c5d0d --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/SubmitEntityView.swift @@ -0,0 +1,139 @@ +// +// SubmitEntityView.swift +// Tella +// +// Created by Gustavo on 06/11/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct SubmitEntityView: View { + @ObservedObject var entityViewModel: UwaziEntityViewModel + @Environment(\.presentationMode) var presentationMode: Binding + + var body: some View { + ContainerView { + VStack { + templateData + + UwaziDividerWidget() + + Spacer() + .frame(height: 20) + + entityTitle + + entityContent + + Spacer() + UwaziDividerWidget() + + bottomActionView + } + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .principal) { + HStack { + Text(LocalizableUwazi.uwaziEntitySummaryDetailToolbarItem.localized) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 18)) + .foregroundColor(.white) + .frame(maxWidth: .infinity, alignment: .leading) // Align to leading + } + } + } + } + + var templateData: some View { + VStack { + Text("\(LocalizableUwazi.uwaziEntitySummaryDetailServerTitle.localized) \(entityViewModel.server?.name ?? "")") + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .frame(maxWidth: .infinity, alignment: .leading) + Text("\(LocalizableUwazi.uwaziEntitySummaryDetailTemplateTitle.localized) \(entityViewModel.template!.entityRow?.name ?? "")") + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .frame(maxWidth: .infinity, alignment: .leading) + }.padding() + } + + var entityTitle: some View { + VStack { + Text(entityViewModel.getEntityTitle()) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 16)) + .foregroundColor(.white) + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding() + } + + var entityContent: some View { + VStack { + entityResponseItem + FileItems(files: $entityViewModel.pdfDocuments) + FileItems(files: $entityViewModel.files) + } + } + + var bottomActionView: some View { + HStack { + Spacer() + Button { + entityViewModel.submitEntity { + navigateTo(destination: UwaziView().environmentObject(UwaziViewModel(mainAppModel: entityViewModel.mainAppModel, server: entityViewModel.server!))) + } + } label: { + if entityViewModel.isLoading { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .frame(maxWidth:.infinity) + .frame(height: 55) + } else { + Text(LocalizableUwazi.uwaziEntitySummaryDetailSubmitAction.localized) + .frame(maxWidth:.infinity) + .frame(height: 55) + .contentShape(Rectangle()) + } + } + .disabled(entityViewModel.isLoading) + .frame(width: UIScreen.main.bounds.width / 2, alignment: .trailing) + .cornerRadius(50) + .buttonStyle(TellaButtonStyle(buttonStyle: YellowButtonStyle(), isValid: true)) + }.padding(.horizontal) + } + + var entityResponseItem: some View { + HStack { + RoundedRectangle(cornerRadius: 5) + .fill(Color.white.opacity(0.2)) + .frame(width: 48, height: 48, alignment: .center) + .overlay( + ZStack{ + Image("document") + } + .frame(width: 48, height: 48) + .cornerRadius(5) + ) + VStack(alignment: .leading) { + Text(LocalizableUwazi.uwaziEntitySummaryDetailEntityResponseTitle.localized) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .foregroundColor(Color.white) + .lineLimit(1) + + Spacer() + .frame(height: 2) + + Text(entityViewModel.getEntityResponseSize()) + .font(.custom(Styles.Fonts.regularFontName, size: 10)) + .foregroundColor(Color.white) + } + .padding(.horizontal, 12) + } + .padding(.bottom, 17) + .padding(.horizontal, 12) + .frame(maxWidth: .infinity, alignment: .leading) + } + +} + diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/FileDropdown.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/FileDropdown.swift new file mode 100644 index 000000000..ae3c70e25 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/FileDropdown.swift @@ -0,0 +1,81 @@ +// +// FileDropdown.swift +// Tella +// +// Created by Gustavo on 30/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct FileDropdown: View { + @State private var showFiles = false + @Binding var files: Set + + var body: some View { + VStack { + Button(action: { + showFiles.toggle() + }) { + dropdownHeader + } + + if showFiles { + FileItems(files: $files) + } + } + } + + var dropdownHeader: some View { + HStack { + Text("\(files.count) \(files.count == 1 ? LocalizableUwazi.uwaziEntitySelectFilesDropdownTitleSingle.localized : LocalizableUwazi.uwaziEntitySelectFilesDropdownTitle.localized)") + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + Spacer() + Text(showFiles ? LocalizableUwazi.uwaziEntitySelectFilesDropdownHide.localized : LocalizableUwazi.uwaziEntitySelectFilesDropdownShow.localized) + .font(.custom(Styles.Fonts.boldFontName, size: 14)) + .foregroundColor(Styles.Colors.yellow) + Image(showFiles ? "uwazi.chevron-up" : "uwazi.chevron-down") + } + .padding(.bottom, 12) + .frame(maxWidth: .infinity, alignment: .leading) + } + +} + + +struct FileItems: View { + @Binding var files: Set + var body: some View { + VStack { + ForEach(files.sorted{$0.created < $1.created}, id: \.id) { file in + HStack { + RoundedRectangle(cornerRadius: 5) + .fill(Color.white.opacity(0.2)) + .frame(width: 35, height: 35, alignment: .center) + .overlay( + file.listImage + .frame(width: 48, height: 48) + .cornerRadius(5) + ) + VStack(alignment: .leading) { + Text(file.name) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .foregroundColor(Color.white) + .lineLimit(1) + + Spacer() + .frame(height: 2) + + Text(file.size.getFormattedFileSize()) + .font(.custom(Styles.Fonts.regularFontName, size: 10)) + .foregroundColor(Color.white) + } + .padding(.horizontal, 17) + } + .padding(.vertical, 8) + .padding(.horizontal, 17) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziDividerWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziDividerWidget.swift new file mode 100644 index 000000000..edc0c8d5b --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziDividerWidget.swift @@ -0,0 +1,24 @@ +// +// UwaziDividerWidget.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziDividerWidget: View { + var body: some View { + Color.white.opacity(0.2) + .frame(height: 1) + } +} + +struct UwaziDividerWidget_Previews: PreviewProvider { + static var previews: some View { + ContainerView { + UwaziDividerWidget() + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityButtonView.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityButtonView.swift new file mode 100644 index 000000000..73ec699a8 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityButtonView.swift @@ -0,0 +1,36 @@ +// +// UwaziEntityButtonView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation +import SwiftUI + +// TODO: Did not use the TellaButtonView because the button content need to be different rather than just a text. Maybe Modify TellaButtonView later. +struct UwaziEntityButtonView : View { + let content: Content + var action : (() -> ())? + + init( action: (() -> Void)? = nil, + @ViewBuilder content: () -> Content) { + self.action = action + self.content = content() + } + var body: some View { + Button { + if action != nil { + action?() + } + } label: { + HStack { + content + Spacer() + } + } + .cornerRadius(15) + .buttonStyle(TellaButtonStyle(buttonStyle: ClearButtonStyle(), isValid: true)) + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityMandatoryTextView.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityMandatoryTextView.swift new file mode 100644 index 000000000..5b3bdfdb7 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityMandatoryTextView.swift @@ -0,0 +1,25 @@ +// +// UwaziEntityMandatoryTextView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziEntityMandatoryTextView: View { + var body: some View { + Text(LocalizableUwazi.uwaziEntityMandatoryExpl.localized) + .font(Font.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(Styles.Colors.yellow) + .frame(maxWidth: .infinity, alignment: .topLeading) + .padding(.top, 4) + } +} + +struct UwaziEntityMandatoryTextView_Previews: PreviewProvider { + static var previews: some View { + UwaziEntityMandatoryTextView() + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntitySubtitleView.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntitySubtitleView.swift new file mode 100644 index 000000000..6505b6dff --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntitySubtitleView.swift @@ -0,0 +1,27 @@ +// +// UwaziEntitySubtitleView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziEntitySubtitleView: View { + var subTitle: String + var body: some View { + Text(subTitle) + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(Color.white.opacity(0.8)) + } +} +struct UwaziEntitySubtitleView_Previews: PreviewProvider { + static var previews: some View { + ZStack { + Color.purple + .ignoresSafeArea() + UwaziEntitySubtitleView(subTitle: "Hello") + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityTitleView.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityTitleView.swift new file mode 100644 index 000000000..4798596d1 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziEntityTitleView.swift @@ -0,0 +1,54 @@ +// +// UwaziEntityTitleView.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziEntityTitleView: View { + var title: String + @State var isRequired: Bool + var showClear: Bool + var onClearAction: () -> Void + var body: some View { + Group { + HStack { + Text(title) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(Color.white) + .frame(alignment: .leading) + Spacer() + if isRequired { + Text("*") + .font(Font.custom(Styles.Fonts.boldFontName, size: 14)) + .kerning(0.5) + .foregroundColor(Styles.Colors.yellow) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + Spacer() + if(showClear) { + Button(action: { + onClearAction() + }) { + Image("uwazi.cancel") + } + } + } + } + + } +} + +struct UwaziEntityTitleView_Previews: PreviewProvider { + static var previews: some View { + ZStack { + Color.purple + .ignoresSafeArea() + UwaziEntityTitleView(title: "Hello", isRequired: true, showClear: false, onClearAction: {}) + } + + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift new file mode 100644 index 000000000..c120aa5bb --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift @@ -0,0 +1,52 @@ +// +// UwaziFileSelector.swift +// Tella +// +// Created by Gustavo on 24/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziFileSelector: View { + @EnvironmentObject var prompt: UwaziEntryPrompt + var addFiles: () -> Void + var title: String + var body: some View { + VStack { + Spacer() + Text(prompt.helpText!) + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(Color.white.opacity(0.87)) + .frame(maxWidth: .infinity, alignment: .leading) + Button { + addFiles() + } label: { + SelectFileComponent(title: title) + } + .background(Color.white.opacity(0.08)) + .cornerRadius(15) + } + } +} + +struct SelectFileComponent: View { + let title: String + + var body: some View { + HStack { + Image("uwazi.add-files") + .padding(.vertical, 20) + Text(title) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(Color.white.opacity(0.87)) + .frame(maxWidth: .infinity, alignment: .leading) + }.padding(.horizontal, 16) + } +} + +struct UwaziFileSelector_Previews: PreviewProvider { + static var previews: some View { + UwaziFileSelector(addFiles: {}, title: "Title") + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/GenericEntityWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/GenericEntityWidget.swift new file mode 100644 index 000000000..3613c0448 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/GenericEntityWidget.swift @@ -0,0 +1,56 @@ +// +// GenericEntityWidget.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct GenericEntityWidget: View { + var title = "" + let content: Content + var isRequired: Bool + var shouldRender: Bool + var showClear: Bool + @Binding var showManatory: Bool + var onClearAction: () -> Void + + init(title: String = "", + isRequired: Bool = false, + showMandatory: Binding, + shouldRender: Bool = true, + showClear: Bool = false, + onClearAction: @escaping () -> Void = {}, + @ViewBuilder content: () -> Content) + { + self.title = title + self.content = content() + self.isRequired = isRequired + self.shouldRender = shouldRender + self.showClear = showClear + self._showManatory = showMandatory + self.onClearAction = onClearAction + } + + var body: some View { + if shouldRender { + VStack() { + UwaziEntityTitleView(title: title, + isRequired: isRequired, + showClear: showClear, + onClearAction: onClearAction) + content + }.padding(.vertical, 14) + } + } +} + +struct GenericEntityWidget_Previews: PreviewProvider { + static var previews: some View { + GenericEntityWidget(showMandatory: .constant(false)) { + Text("") + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift new file mode 100644 index 000000000..d39fb20f1 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift @@ -0,0 +1,71 @@ +// +// PrimaryDocuments.swift +// Tella +// +// Created by Gustavo on 25/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct PrimaryDocuments: View { + @EnvironmentObject var prompt: UwaziEntryPrompt + @EnvironmentObject var sheetManager: SheetManager + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + + var body: some View { + UwaziFileSelector(addFiles: { + UIApplication.shared.endEditing() + showAddFileSheet() + }, title: LocalizableUwazi.uwaziMultiFileWidgetAttachManyPDFFilesSelectTitle.localized) + .environmentObject(prompt) + FileItems(files: $entityViewModel.pdfDocuments) + } + + func showAddFileSheet() { + + sheetManager.showBottomSheet( modalHeight: CGFloat(200), content: { + ActionListBottomSheet(items: addFileToPdfItems, + headerTitle: LocalizableUwazi.uwaziEntitySelectFiles.localized, + action: {item in + self.handleActions(item : item) + }) + }) + } + + func showAddPhotoVideoSheet() { + entityViewModel.showingImportDocumentPicker = true + } + + var fileListView : some View { + FileListView(appModel: entityViewModel.mainAppModel, + filterType: .documents, + title: LocalizableReport.selectFiles.localized, + fileListType: .selectFiles, + resultFile: $entityViewModel.resultFile) + } + + private func handleActions(item: ListActionSheetItem) { + + guard let type = item.type as? ManageFileType else { return } + + switch type { + + case .fromDevice: + showAddPhotoVideoSheet() + + case .tellaFile: + sheetManager.hide() + navigateTo(destination: fileListView) + + default: + break + } + } +} + +struct PrimaryDocuments_Previews: PreviewProvider { + static var previews: some View { + PrimaryDocuments() + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift new file mode 100644 index 000000000..93f22b502 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift @@ -0,0 +1,82 @@ +// +// SupportingFileWidget.swift +// Tella +// +// Created by Gustavo on 25/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct SupportingFileWidget: View { + @EnvironmentObject var prompt: UwaziEntryPrompt + @EnvironmentObject var sheetManager: SheetManager + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + + var body: some View { + UwaziFileSelector(addFiles: { + UIApplication.shared.endEditing() + showAddFileSheet() + }, title: LocalizableUwazi.uwaziEntitySelectFiles.localized) + .environmentObject(prompt) + if(entityViewModel.files.count > 0) { + FileDropdown(files: $entityViewModel.files) + } + } + + func showAddFileSheet() { + + sheetManager.showBottomSheet( modalHeight: CGFloat(300), content: { + ActionListBottomSheet(items: addFileToDraftItems, + headerTitle: LocalizableUwazi.uwaziEntitySelectFiles.localized, + action: {item in + self.handleActions(item : item) + }) + }) + } + + func showAddPhotoVideoSheet() { + + entityViewModel.showingImagePicker = true + } + + var fileListView : some View { + FileListView(appModel: entityViewModel.mainAppModel, + filterType: .audioPhotoVideo, + title: LocalizableReport.selectFiles.localized, + fileListType: .selectFiles, + resultFile: $entityViewModel.resultFile) + } + + private func handleActions(item: ListActionSheetItem) { + + guard let type = item.type as? ManageFileType else { return } + + switch type { + + case .camera: + sheetManager.hide() + entityViewModel.showingCamera = true + + case .recorder: + sheetManager.hide() + entityViewModel.showingRecordView = true + + case .fromDevice: + showAddPhotoVideoSheet() + + case .tellaFile: + sheetManager.hide() + navigateTo(destination: fileListView) + + default: + break + } + } +} + +struct SupportingFileWidget_Previews: PreviewProvider { + static var previews: some View { + SupportingFileWidget() + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift new file mode 100644 index 000000000..eabf6fe44 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift @@ -0,0 +1,80 @@ +// +// UwaziDatePicker.swift +// Tella +// +// Created by Gustavo on 04/01/2024. +// Copyright © 2024 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziDatePicker: View { + @State private var selectedDate = Date() + @State private var dateString: String + @EnvironmentObject var prompt: UwaziEntryPrompt + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + @ObservedObject var value: UwaziValue + + init(value: UwaziValue) { + self.value = value + _dateString = State(initialValue: LocalizableUwazi.uwaziEntitySelectDateTitle.localized) + } + + var body: some View { + HStack(alignment: .lastTextBaseline) { + ZStack(alignment: .topLeading) { + DateLabel() + TransparentDatePicker(selection: $selectedDate) { + updatePromptWithDate($0) + } + + } + }.frame(maxWidth: .infinity, alignment: .leading) + .background(Color.white.opacity(0.08)) + .cornerRadius(12) + } + + private func TransparentDatePicker(selection: Binding, onChange: @escaping (Date) -> Void) -> some View { + DatePicker("", selection: selection, displayedComponents: [.date]) + .datePickerStyle(.compact) + .labelsHidden() + .accentColor(Styles.Colors.lightBlue) + .colorInvert() + .colorMultiply(Color.clear) + .onChange(of: selection.wrappedValue, perform: onChange) + } + + fileprivate func DateLabel() -> some View{ + ZStack { + HStack(alignment: .top) { + HStack { + Image("uwazi.date") + Text(parseDateFromPrompt(value.stringValue)) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(.white) + .padding(.horizontal, 8) + } + } + }.padding() + } + + private func updatePromptWithDate(_ date: Date) { + let unixTimestamp = date.getUnixTimestamp() + prompt.value.stringValue = String(unixTimestamp) + entityViewModel.toggleShowClear(forId: prompt.id ?? "", value: true) + } + + private func parseDateFromPrompt(_ date: String) -> String { + guard !date.isEmpty, let unixTimeStamp = Double(date) else { + return dateString + } + + return unixTimeStamp.getDate()?.getFormattedDateString(format: DateFormat.uwaziDate.rawValue) ?? "" + } +} + +struct UwaziDatePicker_Previews: PreviewProvider { + static var previews: some View { + UwaziDatePicker(value: UwaziValue(stringValue: "", selectedValue: [])) + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift new file mode 100644 index 000000000..ffffb420e --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift @@ -0,0 +1,109 @@ +// +// UwaziSelectWidget.swift +// Tella +// +// Created by Gustavo on 23/10/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziSelectWidget: View { + @State private var shouldShowMenu : Bool = false + @EnvironmentObject var prompt: UwaziEntryPrompt + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + @ObservedObject var value: UwaziValue + var body: some View { + Button { + DispatchQueue.main.async { + shouldShowMenu = true + } + + } label: { + SelectWidgetButton(title: selectTitle(value: value.stringValue), shouldShowMenu: shouldShowMenu) + }.background(Color.white.opacity(0.08)) + .cornerRadius(12) + + if shouldShowMenu { + SelectListOptions(prompt: prompt, shouldShowMenu: $shouldShowMenu, value: value).environmentObject(entityViewModel) + } + } + + func selectTitle(value: String) -> String { + return value.isEmpty ? "Select" : value + } +} + +struct SelectWidgetButton: View { + let title: String + let shouldShowMenu: Bool + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + var body: some View { + HStack { + Text(title) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(Color.white.opacity(0.87)) + .padding() + .frame(maxWidth: .infinity, alignment: .leading) + + if(shouldShowMenu) { + Image("select.arrow.up") + .padding() + } else { + Image("reports.arrow-down") + .padding() + } + } + } +} + +struct SelectListOptions: View { + var prompt: UwaziEntryPrompt + @Binding var shouldShowMenu: Bool + @ObservedObject var value: UwaziValue + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + var body: some View { + VStack(spacing: 0) { + ScrollView { + VStack(spacing: 0) { + ForEach(prompt.selectValues ?? [], id: \.self) { selectedOptions in + SelectOptionButton( + selectedOption: selectedOptions, + promptId: prompt.id ?? "", + shouldShowMenu: $shouldShowMenu, + value: value + ).environmentObject(entityViewModel) + } + } + .frame(minHeight: 40, maxHeight: 250) + .background(Styles.Colors.backgroundMain) + .cornerRadius(12) + } + Spacer() + } + .background(Color.clear) + } +} + +struct SelectOptionButton: View { + let selectedOption: SelectValue + var promptId: String + @Binding var shouldShowMenu: Bool + @ObservedObject var value: UwaziValue + @EnvironmentObject var entityViewModel: UwaziEntityViewModel + var body: some View { + Button(action: { + shouldShowMenu = false + value.selectedValue = [selectedOption] + value.stringValue = selectedOption.translatedLabel ?? "" + entityViewModel.toggleShowClear(forId: promptId, value: true) + }) { + Text(selectedOption.translatedLabel ?? "") + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundColor(.white) + .padding(.all, 14) + } + .background(value.stringValue == selectedOption.translatedLabel ? Color.white.opacity(0.16) : Color.white.opacity(0.08)) + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift new file mode 100644 index 000000000..032a874cd --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift @@ -0,0 +1,34 @@ +// +// UwaziTextWidget.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct UwaziTextWidget: View { + @State var isValidText = false + @State var value: UwaziValue + var body: some View { + VStack(alignment: .leading) { + TextField("", text: $value.stringValue) + .keyboardType(.default) + .textFieldStyle(TextfieldStyle(shouldShowError: false)) + .frame( height: 22) + + Divider() + .frame(height: 1) + .background(Color.white) + .opacity(0.64) + } + } +} +struct UwaziTextWidget_Previews: PreviewProvider { + static var previews: some View { + ContainerView { + UwaziTextWidget(value:UwaziValue.defaultValue()) + } + } +} diff --git a/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift b/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift new file mode 100644 index 000000000..02221af89 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift @@ -0,0 +1,106 @@ +// +// AddTemplatesView.swift +// Tella +// +// Created by Gustavo on 03/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct AddTemplatesView: View { + @EnvironmentObject var uwaziTemplateViewModel: AddTemplateViewModel + @EnvironmentObject var sheetManager: SheetManager + var body: some View { + ContainerView { + ZStack { + VStack { + headerView() + if !self.uwaziTemplateViewModel.isLoading { + handleListView() + } + Spacer() + }.padding(.top, 0) + if uwaziTemplateViewModel.isLoading { + CircularActivityIndicatory() + } + } + } + // TODO: Remove this line with more appropiate solution + .navigationBarTitle("", displayMode: .inline) + .toolbar { + LeadingTitleToolbar(title: LocalizableUwazi.uwaziAddTemplateTitle.localized) + reloadTemplatesButton() + } + .onAppear { + self.uwaziTemplateViewModel.getTemplates() + } + } + fileprivate func reloadTemplatesButton() -> ToolbarItem<(), Button> { + return ToolbarItem(placement: .navigationBarTrailing) { + Button { + self.uwaziTemplateViewModel.getTemplates() + } label: { + Image("arrow.clockwise") + .resizable() + .frame(width: 24, height: 24) + } + } + } + + fileprivate func headerView() -> some View { + let firstPart = Text(LocalizableUwazi.uwaziAddTemplateExpl.localized) + .foregroundColor(.white) + let secondPart = Text(LocalizableUwazi.uwaziAddTemplateSecondExpl.localized) + .foregroundColor(Styles.Colors.yellow) + + return Group { + HStack { + firstPart + secondPart + } + .onTapGesture { + navigateTo(destination: ServersListView().environmentObject(ServersViewModel(mainAppModel: uwaziTemplateViewModel.mainAppModel))) + } + } + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .padding(.all, 18) + } + + fileprivate func handleListView() -> some View { + VStack { + if uwaziTemplateViewModel.templateItemsViewModel.count > 0 { + Text("") + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Text(uwaziTemplateViewModel.serverName) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 16)) + .foregroundColor(.white) + .padding(.all, 14) + ForEach($uwaziTemplateViewModel.templateItemsViewModel, id: \.id) { itemViewModel in + TemplateItemView(templateItemViewModel: itemViewModel) + if itemViewModel.wrappedValue.id != (uwaziTemplateViewModel.templateItemsViewModel.last?.id ?? "") { + DividerView() + } + } + } + .background(Color.white.opacity(0.08)) + .cornerRadius(15) + .padding(.all, 18) + .padding(.top, 0) + } + } else { + Group { + Spacer() + UwaziEmptyView(message: LocalizableUwazi.uwaziAddTemplateEmptydExpl.localized) + Spacer() + } + } + } + .onReceive(uwaziTemplateViewModel.$showToast, perform: { showToast in + if showToast { + Toast.displayToast(message: uwaziTemplateViewModel.errorMessage) + } + }) + } +} + diff --git a/Tella/Scenes/Uwazi/Views/Template/Common/TemplateItemView.swift b/Tella/Scenes/Uwazi/Views/Template/Common/TemplateItemView.swift new file mode 100644 index 000000000..6be032250 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Template/Common/TemplateItemView.swift @@ -0,0 +1,56 @@ +// +// TemplateItemView.swift +// Tella +// +// Created by Gustavo on 03/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct TemplateItemView: View { + + @EnvironmentObject var sheetManager: SheetManager + @Binding var templateItemViewModel: TemplateItemViewModel + + var body: some View { + HStack { + if(templateItemViewModel.isDownloaded) { + Image("report.submitted") + .padding(.leading, 8) + } + Text(templateItemViewModel.name) + .font(.custom(Styles.Fonts.regularFontName, size: 16)) + .foregroundColor(.white) + .padding(.horizontal, 8) + + Spacer() + + if(!templateItemViewModel.isDownloaded) { + MoreButtonView(imageName: "template.add", + action: { + templateItemViewModel.downloadTemplate() + Toast.displayToast(message: "“\(templateItemViewModel.name )” \(LocalizableUwazi.uwaziAddTemplateSavedToast.localized)") + }) + } else { + MoreButtonView(imageName: "reports.more", action: { + showTemplateActionBottomSheet() + }).padding(.trailing, 8) + } + + }.padding(.all, 4) + } + + private func showTemplateActionBottomSheet() { + sheetManager.showBottomSheet(modalHeight: 176) { + ActionListBottomSheet(items: templateActionItems, + headerTitle: templateItemViewModel.name , + action: {item in + self.sheetManager.hide() + self.templateItemViewModel.deleteTemplate() + Toast.displayToast(message: "“\(templateItemViewModel.name )” \(LocalizableUwazi.uwaziDeleteEntitySheetExpl.localized)") + }) + } + } + +} diff --git a/Tella/Scenes/Uwazi/Views/Template/TemplateCardView.swift b/Tella/Scenes/Uwazi/Views/Template/TemplateCardView.swift new file mode 100644 index 000000000..830a985ec --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Template/TemplateCardView.swift @@ -0,0 +1,73 @@ +// +// TemplateCardView.swift +// Tella +// +// Created by Gustavo on 02/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct TemplateCardView: View { + @EnvironmentObject var sheetManager: SheetManager + @EnvironmentObject var mainAppModel: MainAppModel + var templateCardViewModel: TemplateCardViewModel + var body: some View { + VStack(spacing: 0) { + Button(action: { + showtemplateActionBottomSheet() + }) { + HStack { +// MoreButtonView(imageName: "uwazi.star", action: { +// //add this template to favorite +// }) + ConnectionCardDetail(title: templateCardViewModel.translatedName, subtitle: templateCardViewModel.serverName) + Spacer() + MoreButtonView(imageName: "reports.more", action: { + //show detail + showtemplateActionBottomSheet() + }) + } + .padding(.all, 16) + } + } + .background(Color.white.opacity(0.08)) + .cornerRadius(15) + .padding(EdgeInsets(top: 6, leading: 0, bottom: 6, trailing: 0)) + } + + private func showtemplateActionBottomSheet() { + sheetManager.showBottomSheet(modalHeight: 176) { + ActionListBottomSheet(items: downloadTemplateActionItems, + headerTitle: templateCardViewModel.translatedName, + action: {item in + let type = item.type as? DownloadedTemplateActionType + if type == .delete { + showDeleteTemplateConfirmationView() + } else { + navigateTo(destination: CreateEntityView( + appModel: mainAppModel, + templateId: templateCardViewModel.id!, + serverId: templateCardViewModel.serverId + ).environmentObject(sheetManager)) + sheetManager.hide() + } + }) + } + } + + private func showDeleteTemplateConfirmationView() { + sheetManager.showBottomSheet(modalHeight: 200) { + let titleText = LocalizableReport.viewModelDelete.localized + " " + "\"\(templateCardViewModel.translatedName)\"" + return ConfirmBottomSheet(titleText: titleText, + msgText: LocalizableUwazi.uwaziDeleteTemplateExpl.localized, + cancelText: LocalizableReport.deleteCancel.localized, + actionText: LocalizableReport.deleteConfirm.localized) { + templateCardViewModel.deleteTemplate() + Toast.displayToast(message: "“\(templateCardViewModel.translatedName)” \(LocalizableUwazi.uwaziDeleteEntitySheetExpl.localized)") + } + } + } +} + + diff --git a/Tella/Scenes/Uwazi/Views/Template/TemplateListView.swift b/Tella/Scenes/Uwazi/Views/Template/TemplateListView.swift new file mode 100644 index 000000000..7f60d4457 --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Template/TemplateListView.swift @@ -0,0 +1,45 @@ +// +// TemplateListView.swift +// Tella +// +// Created by Gustavo on 02/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct TemplateListView: View { + @EnvironmentObject var uwaziViewModel : DownloadedTemplatesViewModel + var message : String + var serverName : String + + var body: some View { + ZStack { + if !uwaziViewModel.templateCardsViewModel.isEmpty { + VStack(alignment: .center, spacing: 0) { + Text(LocalizableUwazi.uwaziTemplateListExpl.localized) + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .foregroundColor(.white.opacity(0.64)) + .padding(.all, 14) + ScrollView { + ForEach(uwaziViewModel.templateCardsViewModel, id: \.id) { cardViewModel in + TemplateCardView(templateCardViewModel: cardViewModel) + .environmentObject(uwaziViewModel.mainAppModel) + } + } + } + } else { + UwaziEmptyView(message: message) + } + } + .onAppear { + self.uwaziViewModel.getDownloadedTemplates() + } + } +} + +struct TemplateListView_Previews: PreviewProvider { + static var previews: some View { + TemplateListView( message: "", serverName: "") + } +} diff --git a/Tella/Supporting Files/ar.lproj/Localizable.strings b/Tella/Supporting Files/ar.lproj/Localizable.strings index 9971a015e..bf9ca8587 100644 --- a/Tella/Supporting Files/ar.lproj/Localizable.strings +++ b/Tella/Supporting Files/ar.lproj/Localizable.strings @@ -240,6 +240,53 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_DeleteDraftReport_SheetExpl" = "Are you sure you want to delete this draft?"; "Reports_DeleteOutboxReport_SheetExpl" = "Are you sure you want to delete this upload?"; "Reports_DeleteSubmittedReport_SheetExpl" = "Are you sure you want to delete this sent report from your records?"; +"Reports_DraftSaved_Toast" = "The report was saved as a draft"; +"Reports_ReportDeleted_Toast" = "“%@” has been deleted."; +"Reports_ReportSubmitted_Toast" = "Upload complete: “%@”"; +"Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; +"Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + "Settings_SettLang_Arabic_Expl" = "عربي"; "Settings_SettLang_SpanishLatinAmerican_Expl" = "Spanish Latin American"; "Settings_SettLang_Belarusian_Expl" = "Belarusian"; @@ -278,6 +325,36 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/be.lproj/Localizable.strings b/Tella/Supporting Files/be.lproj/Localizable.strings index bafef1183..7a1c1cba7 100644 --- a/Tella/Supporting Files/be.lproj/Localizable.strings +++ b/Tella/Supporting Files/be.lproj/Localizable.strings @@ -278,6 +278,77 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/en.lproj/Localizable.strings b/Tella/Supporting Files/en.lproj/Localizable.strings index 69285186c..4e08b0969 100644 --- a/Tella/Supporting Files/en.lproj/Localizable.strings +++ b/Tella/Supporting Files/en.lproj/Localizable.strings @@ -35,7 +35,6 @@ "LockUnlock_UnlockUpdatePin_Error_IncorrectPIN" = "Incorrect PIN, please try again"; "Home_TabBar" = "Home"; "Home_AppBar" = "Tella"; -"Home_RecentFiles_Subhead" = "Recent files"; "Home_RecentFiles_MoreFiles" = "%i more files"; "Home_TellaFiles_Subhead" = "Tella files"; "Home_TellaFiles_Action_AllFiles" = "All files"; @@ -194,6 +193,35 @@ If you want to take a picture or a video, proceed in the following way:\ "Settings_SettLockTimeout_Cancel_SheetAction" = "CANCEL"; "Settings_SettLockTimeout_Save_SheetAction" = "SAVE"; "Settings_SettServers_AppBar" = "Servers"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Home_RecentFiles_Subhead" = "Recent files"; + + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + "Settings_SettAbout_AppBar" = "About & Help"; "Settings_SettAbout_Head" = "Tella"; "Settings_SettAbout_Subhead" = "Version"; @@ -278,6 +306,53 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; +"Uwazi_Entity_SelectDate_Title" = "Select date"; + + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/es-419.lproj/Localizable.strings b/Tella/Supporting Files/es-419.lproj/Localizable.strings index 3ab76215d..857263a61 100644 --- a/Tella/Supporting Files/es-419.lproj/Localizable.strings +++ b/Tella/Supporting Files/es-419.lproj/Localizable.strings @@ -278,6 +278,76 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "El reporte fue guardado en tu Bandeja de salida"; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/es.lproj/Localizable.strings b/Tella/Supporting Files/es.lproj/Localizable.strings index 04d0567e5..38ba5b6a7 100644 --- a/Tella/Supporting Files/es.lproj/Localizable.strings +++ b/Tella/Supporting Files/es.lproj/Localizable.strings @@ -195,10 +195,35 @@ If you want to take a picture or a video, proceed in the following way:\ "Settings_SettLockTimeout_Save_SheetAction" = "SAVE"; "Settings_SettServers_AppBar" = "Servidores"; "Settings_SettAbout_AppBar" = "Acerca de y Ayuda"; + "Settings_SettAbout_Head" = "Tella"; "Settings_SettAbout_Subhead" = "Versión"; "Settings_SettAbout_ContactUs" = "Contáctanos"; "Settings_SettAbout_PrivacyPolicy" = "Política de Privacidad"; +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; "Reports_AppBar" = "Informes"; "Reports_Report_AppBar" = "Report"; "Reports_Draft_EmptyAllFiles_Expl" = "Tus Borradores están actualmente vacíos. Los reportes que no hayas enviado aparecerán aquí."; @@ -240,6 +265,48 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_DeleteDraftReport_SheetExpl" = "¿Estás segura(o) de querer eliminar este borrador?"; "Reports_DeleteOutboxReport_SheetExpl" = "Are you sure you want to delete this upload?"; "Reports_DeleteSubmittedReport_SheetExpl" = "¿Tienes seguridad de querer borrar este reporte enviado de tus registros?"; + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + "Settings_SettLang_Arabic_Expl" = "Árabe"; "Settings_SettLang_SpanishLatinAmerican_Expl" = "Spanish Latin American"; "Settings_SettLang_Belarusian_Expl" = "Belarusian"; diff --git a/Tella/Supporting Files/fa-IR.lproj/Localizable.strings b/Tella/Supporting Files/fa-IR.lproj/Localizable.strings index 8ac5e7d29..76d6c7deb 100644 --- a/Tella/Supporting Files/fa-IR.lproj/Localizable.strings +++ b/Tella/Supporting Files/fa-IR.lproj/Localizable.strings @@ -278,6 +278,76 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "گزارش در صندوق خروجی ذخیره شد. هر وقت آماده شدید می‌توانید آن را ارسال کنید."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/fr.lproj/Localizable.strings b/Tella/Supporting Files/fr.lproj/Localizable.strings index cce906feb..6367cb9d7 100644 --- a/Tella/Supporting Files/fr.lproj/Localizable.strings +++ b/Tella/Supporting Files/fr.lproj/Localizable.strings @@ -194,6 +194,7 @@ If you want to take a picture or a video, proceed in the following way:\ "Settings_SettLockTimeout_Cancel_SheetAction" = "ANNULER"; "Settings_SettLockTimeout_Save_SheetAction" = "SAVE"; "Settings_SettServers_AppBar" = "Serveurs"; + "Settings_SettAbout_AppBar" = "À propos et Aide"; "Settings_SettAbout_Head" = "Tella"; "Settings_SettAbout_Subhead" = "Version"; @@ -294,3 +295,70 @@ This feedback is anonymous, so make sure to include contact information if you w "Settings_SettFeedback_ExitFeedback_SheetExpl" = "You can come back anytime to submit your feedback."; "Settings_SettFeedback_ExitFeedback_Exit_SheetAction" = "Exit without saving"; "Settings_SettFeedback_ExitFeedback_Save_SheetAction" = "Enregistrer le brouillon"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; diff --git a/Tella/Supporting Files/ku-ckb.lproj/Localizable.strings b/Tella/Supporting Files/ku-ckb.lproj/Localizable.strings index c10305c7d..6a605f599 100644 --- a/Tella/Supporting Files/ku-ckb.lproj/Localizable.strings +++ b/Tella/Supporting Files/ku-ckb.lproj/Localizable.strings @@ -278,19 +278,44 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "ڕاپۆرتەکە لە Outboxـەکەتدا پاشەکەوت کرا. کاتێک ئامادە بووی دەتوانی پێشکەشی بکەیت."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; -"Settings_SettFeedback_AppBar" = "Feedback"; -"Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ -\ -This feedback is anonymous, so make sure to include contact information if you want a response from us."; -"Settings_SettFeedback_EnableFeedback_Title" = "Enable feedback sharing"; -"Settings_SettFeedback_EnableFeedback_Expl" = "WARNING: using this feature may reveal to someone observing the network that you use Tella. Only use if you feel comfortable with this risk."; -"Settings_SettFeedback_EnableFeedback_LearnMore" = "Learn more."; -"Settings_SettFeedback_Select_Feedback" = "Feedback"; -"Settings_SettFeedback_Action_Submit" = "پێشکەشکردن"; -"Settings_SettFeedback_Offline_Toast" = "You are not connected to the internet. Your feedback will be sent automatically when you are connected."; -"Settings_SettFeedback_SuccessSent_Toast" = "Thanks for your feedback!"; -"Settings_SettFeedback_BackgroundSuccessSent_Toast" = "Your feedback has been sent"; -"Settings_SettFeedback_ExitFeedback_SheetTitle" = "Save draft?"; -"Settings_SettFeedback_ExitFeedback_SheetExpl" = "You can come back anytime to submit your feedback."; -"Settings_SettFeedback_ExitFeedback_Exit_SheetAction" = "Exit without saving"; -"Settings_SettFeedback_ExitFeedback_Save_SheetAction" = "پاشەکەوتکردنی ڕەشنووس"; \ No newline at end of file + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fil out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You don't have any downloaded templates"; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can"; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; +Uwazi_Home_Server_Title diff --git a/Tella/Supporting Files/ku.lproj/Localizable.strings b/Tella/Supporting Files/ku.lproj/Localizable.strings index 5d9eebee1..077d84310 100644 --- a/Tella/Supporting Files/ku.lproj/Localizable.strings +++ b/Tella/Supporting Files/ku.lproj/Localizable.strings @@ -240,6 +240,55 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_DeleteDraftReport_SheetExpl" = "دڵنیایت لەوەی دەتەوێت ئەم ڕەشنووسە بسڕیتەوە؟"; "Reports_DeleteOutboxReport_SheetExpl" = "Are you sure you want to delete this upload?"; "Reports_DeleteSubmittedReport_SheetExpl" = "ئایا دڵنیایت لەوەی کە دەتەوێت ئەم ڕاپۆرتە نێردراوە لە تۆمارەکانتدا بسڕیتەوە؟"; +"Reports_DraftSaved_Toast" = "The report was saved as a draft"; +"Reports_ReportDeleted_Toast" = "“%@” has been deleted."; +"Reports_ReportSubmitted_Toast" = "Upload complete: “%@”"; +"Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; +"Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; + +"Uwazi_Home_Server_Title" = "Uwazi"; +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + "Settings_SettLang_Arabic_Expl" = "عەرەبی"; "Settings_SettLang_SpanishLatinAmerican_Expl" = "Spanish Latin American"; "Settings_SettLang_Belarusian_Expl" = "Belarusian"; diff --git a/Tella/Supporting Files/my.lproj/Localizable.strings b/Tella/Supporting Files/my.lproj/Localizable.strings index 2db627b8b..56a97de2b 100644 --- a/Tella/Supporting Files/my.lproj/Localizable.strings +++ b/Tella/Supporting Files/my.lproj/Localizable.strings @@ -278,6 +278,77 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "အစီရင်ခံချက်ကို သင်၏ Outbox တွင် သိမ်းဆည်ထားပါသည်။ သင်အဆင်သင့်ဖြစ်သည်နှင့် တင်သွင်းနိုင်ပါပြီ။"; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Supporting Files/ta.lproj/Localizable.strings b/Tella/Supporting Files/ta.lproj/Localizable.strings index e0d6892d8..2570f2f89 100644 --- a/Tella/Supporting Files/ta.lproj/Localizable.strings +++ b/Tella/Supporting Files/ta.lproj/Localizable.strings @@ -240,6 +240,48 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_DeleteDraftReport_SheetExpl" = "Are you sure you want to delete this draft?"; "Reports_DeleteOutboxReport_SheetExpl" = "Are you sure you want to delete this upload?"; "Reports_DeleteSubmittedReport_SheetExpl" = "Are you sure you want to delete this sent report from your records?"; + +"Uwazi_AppBar" = "Uwazi"; +"Uwazi_Server_Edit_SheetAction" = "Edit"; +"Uwazi_Server_Delete_SheetAction" = "Delete"; + +"Uwazi_PageViewItem_Template" = "Templates"; +"Uwazi_Template_TemplateList_Expl" = "These are uwazi templates you can fill out."; +"Uwazi_Template_TemplateList_EmptyExpl" = "You have no saved templates. Tap the “+” button to add a new template."; +"Uwazi_Template_CreateEntity_SheetAction" = "Create an entity"; +"Uwazi_Template_Delete_SheetAction" = " Delete from this device"; + +"Uwazi_Template_AddTemplate_Title" = "Add Templates"; +"Uwazi_Template_AddTemplate_Expl" = "These are the templates available on the Uwazi instances you are connected to. You can "; +"Uwazi_Template_AddTemplate_SecondExpl" = "manage your Uwazi instances here."; +"Uwazi_Template_AddTemplateSaved_Toast" = "successfully added to your Uwazi templates."; +"Uwazi_Template_AddTemplate_EmptyExpl" = "There are no templates"; +"Uwazi_DeleteTemplate_SheetExpl" = "Are you sure you want to delete this Template?"; + +"Uwazi_Entity_MultiFile_PrimaryDocument" = "Primary Documents"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles" = "Attach as many PDF files as you wish"; +"Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" = "Select PDF files"; +"Uwazi_Entity_MultiFile_SupportingFiles" = "Supporting files"; +"Uwazi_Entity_MultiFile_SupportingFiles_HelpText" = "Select as many files as you wish"; +"Uwazi_Entity_ExitEntity_SheetTitle" = "Exit entity?"; +"Uwazi_Entity_ExitEntity_SheetExpl" = "Your draft will be lost"; +"Uwazi_Entity_Property_Unsopported" = "Unsupported property type"; +"Uwazi_Entity_Action_Next" = "Next"; +"Uwazi_Entity_Mandatory_Expl" = "This field is mandatory."; + +"Uwazi_Entity_Submitted" = "Entity submitted succesfully"; +"Uwazi_Entity_Submission_Failed" = "There was an error submitting the entity"; +"Uwazi_Entity_SummaryDetail_ToolbarItem" = "Summary"; +"Uwazi_Entity_SummaryDetail_ServerTitle" = "Server:"; +"Uwazi_Entity_SummaryDetail_TemplateTitle" = "Template:"; +"Uwazi_Entity_SummaryDetail_EntityResponse_Title" = "Entity Response"; +"Uwazi_Entity_SummaryDetail_Submit_Action" = "SUBMIT"; +"Uwazi_Entity_SelectFiles" = "Select Files"; +"Uwazi_Entity_SelectFiles_Dropdown_Title" = "files attached"; +"Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" = "file attached"; +"Uwazi_Entity_SelectFiles_Dropdown_Hide" = "Hide files"; +"Uwazi_Entity_SelectFiles_Dropdown_Show" = "Show files"; + "Settings_SettLang_Arabic_Expl" = "Arabic"; "Settings_SettLang_SpanishLatinAmerican_Expl" = "Spanish Latin American"; "Settings_SettLang_Belarusian_Expl" = "Belarusian"; @@ -278,6 +320,36 @@ If you want to take a picture or a video, proceed in the following way:\ "Reports_OutboxSaved_Toast" = "The report was saved to your Outbox. You can submit it when you are ready."; "Reports_AllReportDeleted_Toast" = "Your submitted reports have been cleared."; "Settings_SettLang_Tamil_Expl" = "Tamil"; + +"Setting_SettServer_Selection_Title" = "What type of server would you like to connect to?"; +"Setting_SettServer_Selection_Message" = "If you are not sure, please ask your organization."; +"Setting_SettServer_TellaWeb" = "TELLA WEB"; +"Setting_SettServer_Uwazi" = "UWAZI"; +"Setting_SettServer_No_Internet" = "No Internet connection. Try again when you are connected to the Internet."; +"Setting_SettServer_Server_URL_Incorrect" = "Error: The server URL is incorrect"; + +"Setting_Server_Uwazi_Server_URL" = "Enter the server URL"; +"Setting_Server_Uwazi_Access_Title" = "Log in if you have a username and password, or access the server as a public instance."; +"Setting_Server_Uwazi_Login" = "LOG IN"; +"Setting_Server_Uwazi_Access_Public" = "ACCESS AS PUBLIC INSTANCE"; +"Setting_Server_Uwazi_Login_Access" = "Log in to access the server"; +"Setting_Server_Uwazi_Username" = "Username"; +"Setting_Server_Uwazi_Password" = "Password"; +"Setting_Server_Uwazi_Two_Step_Title" = "Two-step verification"; +"Setting_Server_Uwazi_Two_Step_Message" = "This Uwazi instance requires two-step verification. Enter here the 6-digit code from your authenticator app."; +"Setting_Server_Uwazi_Authentication_Placeholder" = "Authentication code"; +"Setting_Server_Uwazi_Authentication_Verify" = "VERIFY"; +"Setting_Server_Uwazi_Language_Title" = "Uwazi language"; +"Setting_Server_Uwazi_Language_Message" = "This Uwazi collection is available in several languages. Please select in which language you would like to download templates."; +"Setting_Server_Uwazi_Language_Ok" = "OK"; +"Setting_Server_Uwazi_Language_Cancel" = "CANCEL"; +"Setting_Server_Uwazi_Connect_Server" = "Connected to server"; +"Setting_Server_Uwazi_Success_Message" = "You have successfully connected to the server and will be able to share your data."; + +"Uwazi_Home_Server_Title" = "Uwazi"; + + + "Settings_SettFeedback_AppBar" = "Feedback"; "Settings_SettFeedback_Expl" = "Tell us if you are experiencing a bug, have a request for a new feature, or have any other feedback.\ \ diff --git a/Tella/Utils/DebugLogging.swift b/Tella/Utils/DebugLogging.swift index 375ae6b8c..c63a6ecf8 100644 --- a/Tella/Utils/DebugLogging.swift +++ b/Tella/Utils/DebugLogging.swift @@ -38,6 +38,12 @@ func debugLog(_ debugText: String, level: DebugLevel = .debug, space: DebugSpace Logger.shared.log("\(function):\n\(debugText)\n", level: level, space: space) #endif } +func debugLog(_ object: Any, level: DebugLevel = .debug, space: DebugSpace = .app, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) { + #if DEBUG || STAGING + let debugInfos = "FileName: \(Logger.sourceFileName(filePath: filename)) \nFunction Name: \(funcName) -> \(object)" + Logger.shared.logDump(object, level: level, space: space,debugInfos: debugInfos) + #endif +} private class Logger { static let shared = Logger() @@ -59,6 +65,24 @@ private class Logger { print("\(debugText)") #endif } + func logDump(_ debugObject: Any, level: DebugLevel, space: DebugSpace, debugInfos: String) { + #if DEBUG || STAGING + guard isDebugAllowed(level: level, space: space) else { + return + } + let mirror = Mirror(reflecting: debugObject) + var description = "" + description += "{\n" + for child in mirror.children { + if let label = child.label { + description += " \(label): " + } + description += "\(child.value)\n" + } + description += "}" + print("\(debugInfos):\n\(description)\n") + #endif + } private func isDebugAllowed(level requestedLevel: DebugLevel, space: DebugSpace) -> Bool { guard let allowedLevel = debugLevel[space] else { @@ -66,5 +90,14 @@ private class Logger { } return allowedLevel.rawValue <= requestedLevel.rawValue } + + /// Extract the file name from the file path + /// + /// - Parameter filePath: Full file path in bundle + /// - Returns: File Name with extension + class func sourceFileName(filePath: String) -> String { + let components = filePath.components(separatedBy: "/") + return components.isEmpty ? "" : components.last! + } } diff --git a/Tella/Utils/Extensions/DateExtension.swift b/Tella/Utils/Extensions/DateExtension.swift index a34891808..a33f23b01 100644 --- a/Tella/Utils/Extensions/DateExtension.swift +++ b/Tella/Utils/Extensions/DateExtension.swift @@ -12,6 +12,7 @@ enum DateFormat : String { case dataBase = "yyyy-MM-dd'T'HH:mm:ssZ" case submittedReport = "dd.MM.yyyy, hh:mm a" case autoReportNameName = "yyyy.MM.dd - HH.mm" + case uwaziDate = "dd/MM/yyyy" } @@ -50,6 +51,10 @@ extension Date{ func getDate() -> String { return "\(Int(self.timeIntervalSince1970 * 1000))" } + + func getUnixTimestamp() -> Int { + return Int(self.timeIntervalSince1970) + } private func getTimeAgoSinceNow() -> String { diff --git a/Tella/Utils/Extensions/JSONDecoderExtension.swift b/Tella/Utils/Extensions/JSONDecoderExtension.swift new file mode 100644 index 000000000..9b4f642f2 --- /dev/null +++ b/Tella/Utils/Extensions/JSONDecoderExtension.swift @@ -0,0 +1,16 @@ +// +// JSONDecoderExtension.swift +// Tella +// +// Created by Robert Shrestha on 9/14/23. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +extension JSONDecoder { + func decode(_ type: T.Type, from dictionary: Any) throws -> T { + let data = try JSONSerialization.data(withJSONObject: dictionary) + return try decode(type, from: data) + } +} diff --git a/Tella/Utils/Extensions/StringExtension.swift b/Tella/Utils/Extensions/StringExtension.swift index b1ce2b612..8f3ca997a 100644 --- a/Tella/Utils/Extensions/StringExtension.swift +++ b/Tella/Utils/Extensions/StringExtension.swift @@ -116,7 +116,7 @@ extension String { case _ where type.conforms(to: .audio): return .audio case _ where type.conforms(to: .pdf) || type.conforms(to: .presentation) || type.conforms(to: .spreadsheet): - return .video + return .document default: return .other } diff --git a/Tella/Utils/Localizable/LocalizableHome.swift b/Tella/Utils/Localizable/LocalizableHome.swift index ab70e11f9..7cf6cea4e 100644 --- a/Tella/Utils/Localizable/LocalizableHome.swift +++ b/Tella/Utils/Localizable/LocalizableHome.swift @@ -30,4 +30,7 @@ enum LocalizableHome: String, LocalizableDelegate { case quickDeleteSwipeTitle = "Home_QuickDelete_Action_Title" case quickDeleteActionTitle = "Home_QuickDelete_Action_QuickDelete" case quickDeleteActionCancel = "Home_QuickDelete_Action_QuickDeleteDeactivated" + + case uwaziServerTitle = "Uwazi_Home_Server_Title" + } diff --git a/Tella/Utils/Localizable/LocalizableReport.swift b/Tella/Utils/Localizable/LocalizableReport.swift index 5b0961502..ec2590ce7 100644 --- a/Tella/Utils/Localizable/LocalizableReport.swift +++ b/Tella/Utils/Localizable/LocalizableReport.swift @@ -3,7 +3,7 @@ // Tella // // Created by Gustavo on 27/03/2023. -// Copyright © 2023 INTERNEWS. All rights reserved. +// Copyright © 2023 HORIZONTAL. All rights reserved. // import Foundation diff --git a/Tella/Utils/Localizable/LocalizableSettings.swift b/Tella/Utils/Localizable/LocalizableSettings.swift index deee93974..6920bfad7 100644 --- a/Tella/Utils/Localizable/LocalizableSettings.swift +++ b/Tella/Utils/Localizable/LocalizableSettings.swift @@ -82,6 +82,31 @@ enum LocalizableSettings: String, LocalizableDelegate { // Servers case settServersAppBar = "Settings_SettServers_AppBar" + case settServerSelectionTitle = "Setting_SettServer_Selection_Title" + case settServerSelectionMessage = "Setting_SettServer_Selection_Message" + case settServerTellaWeb = "Setting_SettServer_TellaWeb" + case settServerUwazi = "Setting_SettServer_Uwazi" + case settServerNoInternetConnection = "Setting_SettServer_No_Internet" + case settServerServerURLIncorrect = "Setting_SettServer_Server_URL_Incorrect" + + // Uwazi + case UwaziServerURL = "Setting_Server_Uwazi_Server_URL" + case UwaziAccessServerTitle = "Setting_Server_Uwazi_Access_Title" + case UwaziLogin = "Setting_Server_Uwazi_Login" + case UwaziPublicInstance = "Setting_Server_Uwazi_Access_Public" + case UwaziLoginAccess = "Setting_Server_Uwazi_Login_Access" + case UwaziUsername = "Setting_Server_Uwazi_Username" + case UwaziPassword = "Setting_Server_Uwazi_Password" + case UwaziTwoStepTitle = "Setting_Server_Uwazi_Two_Step_Title" + case UwaziTwoStepMessage = "Setting_Server_Uwazi_Two_Step_Message" + case UwaziAuthenticationPlaceholder = "Setting_Server_Uwazi_Authentication_Placeholder" + case UwaziAuthenticationVerify = "Setting_Server_Uwazi_Authentication_Verify" + case UwaziLanguageTitle = "Setting_Server_Uwazi_Language_Title" + case UwaziLanguageMessage = "Setting_Server_Uwazi_Language_Message" + case UwaziLanguageOk = "Setting_Server_Uwazi_Language_Ok" + case UwaziLanguageCancel = "Setting_Server_Uwazi_Language_Cancel" + case UwaziSuccess = "Setting_Server_Uwazi_Connect_Server" + case UwaziSuccessMessage = "Setting_Server_Uwazi_Success_Message" // About & Help diff --git a/Tella/Utils/Localizable/LocalizableUwazi.swift b/Tella/Utils/Localizable/LocalizableUwazi.swift new file mode 100644 index 000000000..5915365d6 --- /dev/null +++ b/Tella/Utils/Localizable/LocalizableUwazi.swift @@ -0,0 +1,56 @@ +// +// LocalizableUwazi.swift +// Tella +// +// Created by Gustavo on 23/08/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation + +enum LocalizableUwazi: String, LocalizableDelegate { + + case uwaziTitle = "Uwazi_AppBar" + + case uwaziServerEdit = "Uwazi_Server_Edit_SheetAction" + case uwaziServerDelete = "Uwazi_Server_Delete_SheetAction" + + case uwaziPageViewTemplate = "Uwazi_PageViewItem_Template" + case uwaziTemplateListExpl = "Uwazi_Template_TemplateList_Expl" + case uwaziTemplateListEmptyExpl = "Uwazi_Template_TemplateList_EmptyExpl" + + case uwaziAddTemplateTitle = "Uwazi_Template_AddTemplate_Title" + case uwaziAddTemplateExpl = "Uwazi_Template_AddTemplate_Expl" + case uwaziAddTemplateSecondExpl = "Uwazi_Template_AddTemplate_SecondExpl" + case uwaziAddTemplateSavedToast = "Uwazi_Template_AddTemplateSaved_Toast" + case uwaziAddTemplateEmptydExpl = "Uwazi_Template_AddTemplate_EmptyExpl" + case uwaziDeleteTemplateExpl = "Uwazi_DeleteTemplate_SheetExpl" + + case uwaziCreateEntitySheetExpl = "Uwazi_Template_CreateEntity_SheetAction" + case uwaziDeleteEntitySheetExpl = "Uwazi_Template_Delete_SheetAction" + + case uwaziEntityExitSheetTitle = "Uwazi_Entity_ExitEntity_SheetTitle" + case uwaziEntityExitSheetExpl = "Uwazi_Entity_ExitEntity_SheetExpl" + case uwaziEntityUnsopportedProperty = "Uwazi_Entity_Property_Unsopported" + case uwaziEntityActionNext = "Uwazi_Entity_Action_Next" + case uwaziEntityMandatoryExpl = "Uwazi_Entity_Mandatory_Expl" + case uwaziMultiFileWidgetPrimaryDocuments = "Uwazi_Entity_MultiFile_PrimaryDocument" + case uwaziMultiFileWidgetAttachManyPDFFiles = "Uwazi_Entity_MultiFile_AttachManyPDFFiles" + case uwaziMultiFileWidgetAttachManyPDFFilesSelectTitle = "Uwazi_Entity_MultiFile_AttachManyPDFFiles_SelectTitle" + case uwaziMultiFileWidgetSupportingFiles = "Uwazi_Entity_MultiFile_SupportingFiles" + case uwaziMultiFileWidgetSelectManyFiles = "Uwazi_Entity_MultiFile_SupportingFiles_HelpText" + + case uwaziEntitySubmitted = "Uwazi_Entity_Submitted" + case uwaziEntityFailedSubmission = "Uwazi_Entity_Submission_Failed" + case uwaziEntitySummaryDetailToolbarItem = "Uwazi_Entity_SummaryDetail_ToolbarItem" + case uwaziEntitySummaryDetailServerTitle = "Uwazi_Entity_SummaryDetail_ServerTitle" + case uwaziEntitySummaryDetailTemplateTitle = "Uwazi_Entity_SummaryDetail_TemplateTitle" + case uwaziEntitySummaryDetailEntityResponseTitle = "Uwazi_Entity_SummaryDetail_EntityResponse_Title" + case uwaziEntitySummaryDetailSubmitAction = "Uwazi_Entity_SummaryDetail_Submit_Action" + case uwaziEntitySelectFiles = "Uwazi_Entity_SelectFiles" + case uwaziEntitySelectFilesDropdownTitle = "Uwazi_Entity_SelectFiles_Dropdown_Title" + case uwaziEntitySelectFilesDropdownTitleSingle = "Uwazi_Entity_SelectFiles_Dropdown_TitleSingle" + case uwaziEntitySelectFilesDropdownHide = "Uwazi_Entity_SelectFiles_Dropdown_Hide" + case uwaziEntitySelectFilesDropdownShow = "Uwazi_Entity_SelectFiles_Dropdown_Show" + case uwaziEntitySelectDateTitle = "Uwazi_Entity_SelectDate_Title" +} diff --git a/Tella/Utils/Validator.swift b/Tella/Utils/Validator.swift index fc7b004fe..396a5293b 100644 --- a/Tella/Utils/Validator.swift +++ b/Tella/Utils/Validator.swift @@ -13,6 +13,7 @@ struct Regex { static let textRegex = "^.{1,}" static let usernameRegex = "^.{3,}" static let urlRegex = #"^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$"# + static let codeRegex = "^[0-9]{6,6}$" } func validateRegex(value: String, pattern:String) -> Bool { @@ -25,6 +26,14 @@ func validateRegex(value: String, pattern:String) -> Bool { } extension String { + + func codeValidator() -> Bool { + guard !self.isEmpty else { + return false + } + guard validateRegex(value: self, pattern: Regex.codeRegex) else { return false } + return true + } func passwordValidator() -> Bool { guard !self.isEmpty else { @@ -65,6 +74,5 @@ extension String { } return true } - } diff --git a/UwaziConstants.swift b/UwaziConstants.swift new file mode 100644 index 000000000..a50f7a24c --- /dev/null +++ b/UwaziConstants.swift @@ -0,0 +1,9 @@ +// +// UwaziConstants.swift +// Tella +// +// Created by Gustavo on 29/09/2023. +// Copyright © 2023 HORIZONTAL. All rights reserved. +// + +import Foundation