From aca23bedb8efee1e73f6c82011f714a9012f97c7 Mon Sep 17 00:00:00 2001 From: Huet Date: Wed, 3 Jan 2024 11:56:15 +0100 Subject: [PATCH] MAJ sources --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 25 + DansMaRue.xcodeproj/project.pbxproj | 241 ++-- .../xcschemes/xcschememanagement.plist | 24 - .../UserInterfaceState.xcuserstate | Bin 27418 -> 0 bytes .../Type/icon_10000.imageset/icon_10000.png | Bin 624 -> 0 bytes .../icon_10000.imageset/icon_10000@2x.png | Bin 1273 -> 0 bytes .../icon_10000.imageset/icon_10000@3x.png | Bin 1961 -> 0 bytes .../Type/icon_12000.imageset/icon_12005.png | Bin 1208 -> 0 bytes .../icon_12000.imageset/icon_12005@2x.png | Bin 3087 -> 0 bytes .../icon_12000.imageset/icon_12005@3x.png | Bin 5915 -> 0 bytes .../Type/icon_8000.imageset/icon_8000.png | Bin 326 -> 0 bytes .../Type/icon_8000.imageset/icon_8000@2x.png | Bin 641 -> 0 bytes .../Type/icon_8000.imageset/icon_8000@3x.png | Bin 881 -> 0 bytes .../Contents.json | 10 +- .../MicrosoftTeams-image.png | Bin 0 -> 920 bytes .../favorite_check.imageset/Contents.json | 12 +- .../favorite_check.imageset/etoile-1.png | Bin 3552 -> 0 bytes .../favorite_check.imageset/etoile-2.png | Bin 3552 -> 0 bytes .../favorite_check.imageset/etoile-3.png | Bin 3552 -> 0 bytes .../favorite_check 1.png | Bin 0 -> 3705 bytes .../favorite_check 2.png | Bin 0 -> 3705 bytes .../favorite_check.png | Bin 0 -> 3705 bytes .../favorite_plus.imageset/Contents.json | 12 +- .../etoilePlus (3)-1.png | Bin 8746 -> 0 bytes .../etoilePlus (3)-2.png | Bin 8746 -> 0 bytes .../etoilePlus (3)-3.png | Bin 8746 -> 0 bytes .../favorite_plus 1.png | Bin 0 -> 5869 bytes .../favorite_plus 2.png | Bin 0 -> 5869 bytes .../favorite_plus.imageset/favorite_plus.png | Bin 0 -> 5869 bytes .../Anomalie/follow.imageset/follow.png | Bin 2792 -> 3267 bytes .../Anomalie/follow.imageset/follow@2x.png | Bin 6403 -> 5379 bytes .../Anomalie/follow.imageset/follow@3x.png | Bin 10665 -> 8001 bytes .../icon_check_pink.png | Bin 735 -> 1668 bytes .../icon_check_pink@2x.png | Bin 1480 -> 1982 bytes .../icon_check_pink@3x.png | Bin 2058 -> 2273 bytes .../imgRemerciement.png | Bin 4334 -> 2405 bytes .../imgRemerciement@2x.png | Bin 9941 -> 3459 bytes .../imgRemerciement@3x.png | Bin 19440 -> 4936 bytes .../imgRemerciement2.png | Bin 1862 -> 1915 bytes .../imgRemerciement2@2x.png | Bin 4334 -> 2408 bytes .../imgRemerciement2@3x.png | Bin 6407 -> 2832 bytes .../responsable_quartier.imageset/120.png | Bin 14757 -> 9267 bytes .../responsable_quartier.imageset/180.png | Bin 25604 -> 16241 bytes .../responsable_quartier.imageset/80.png | Bin 8856 -> 5488 bytes .../Contents.json | 3 + .../Anomalie/thanks.imageset/Group 4134.png | Bin 3574 -> 2559 bytes .../thanks.imageset/Group 4134@2x.png | Bin 7164 -> 3888 bytes .../thanks.imageset/Group 4134@3x.png | Bin 11786 -> 5271 bytes .../Anomalie/unfollow.imageset/unfollow.png | Bin 2912 -> 3367 bytes .../unfollow.imageset/unfollow@2x.png | Bin 6565 -> 5498 bytes .../unfollow.imageset/unfollow@3x.png | Bin 10945 -> 8221 bytes .../Map/add_anomalie.imageset/Contents.json | 12 +- .../add_anomalie.imageset/add_anomalie 1.png | Bin 0 -> 2708 bytes .../add_anomalie.imageset/add_anomalie 2.png | Bin 0 -> 2708 bytes .../add_anomalie.imageset/add_anomalie.png | Bin 449 -> 2708 bytes .../add_anomalie.imageset/add_anomalie@2x.png | Bin 835 -> 0 bytes .../add_anomalie.imageset/add_anomalie@3x.png | Bin 1178 -> 0 bytes .../create_anomalie.png | Bin 2754 -> 3242 bytes .../create_anomalie@2x.png | Bin 6358 -> 5317 bytes .../create_anomalie@3x.png | Bin 10622 -> 7946 bytes .../map_menu_selected.png | Bin 700 -> 1589 bytes .../map_menu_selected@2x.png | Bin 1263 -> 1797 bytes .../map_menu_selected@3x.png | Bin 1879 -> 2023 bytes .../profil_menu_selected.png | Bin 762 -> 1605 bytes .../profil_menu_selected@2x.png | Bin 1465 -> 1823 bytes .../profil_menu_selected@3x.png | Bin 2147 -> 2076 bytes .../Assets.xcassets/Slider/Contents.json | 6 +- .../dot_red_ large.imageset}/Contents.json | 10 +- .../dot_red_ large.svg | 3 + .../dot_white_large.imageset}/Contents.json | 10 +- .../dot_white_large.svg | 3 + .../dot_white_small.imageset/Contents.json | 21 + .../dot_white_small.svg | 3 + DansMaRue/Constants/Constants.swift | 136 ++- .../Anomalie/AddAnomalyViewController.swift | 926 +++++++++----- .../AnomalyDetailViewController.swift | 901 +++++++++----- .../DescriptiveAnomalyViewController.swift | 143 ++- .../ModifyAddressViewController.swift | 75 +- .../Anomalie/PriorityViewController.swift | 35 +- .../ThanksAnomalyViewController.swift | 381 +++--- .../Anomalie/TypeAnomalieViewController.swift | 227 ++-- .../Controllers/CustomUIAlertController.swift | 114 ++ .../Controllers/MainViewController.swift | 192 +-- .../Map/BottomSheetViewController.swift | 1078 +++++++++++------ .../EquipementSearchTableViewController.swift | 45 +- .../Controllers/Map/MapViewController.swift | 374 +++--- .../Map/TypeContributionViewController.swift | 27 +- .../CompteParisienViewController.swift | 6 +- .../Profile/ProfileAboutViewController.swift | 29 +- .../ProfileActualitesViewController.swift | 146 ++- .../Profile/ProfileAidesViewController.swift | 59 +- .../Profile/ProfileCGUViewController.swift | 36 - .../Profile/ProfileDetailViewController.swift | 94 +- .../ProfileSettingsViewController.swift | 389 ++++-- .../Profile/ProfileViewController.swift | 290 +++-- .../WelcomePageContentViewController.swift | 73 +- .../Welcome/WelcomeSliderViewController.swift | 51 +- .../Welcome/WelcomeViewController.swift | 28 +- .../Extensions/UIButton+AnomalieDMR.swift | 4 +- DansMaRue/Extensions/UIColor+DMR.swift | 38 +- DansMaRue/ManageAddress.storyboard | 26 +- DansMaRue/ManageAddressViewController.swift | 86 +- DansMaRue/ManageFavorites.storyboard | 26 +- DansMaRue/ManageFavoritesViewController.swift | 94 +- DansMaRue/Model/Anomalie.swift | 21 +- DansMaRue/Model/User.swift | 30 +- DansMaRue/Services/RestApiManager.swift | 99 +- .../Anomalie/AddAnomaly.storyboard | 252 ++-- .../Anomalie/AnomalyDetail.storyboard | 467 +++---- .../Anomalie/DescriptiveAnomaly.storyboard | 39 +- .../Anomalie/PopupPhoto.storyboard | 29 +- .../Storyboards/Anomalie/Priority.storyboard | 30 +- .../Storyboards/Anomalie/Thanks.storyboard | 113 +- .../Anomalie/TypeAnomalie.storyboard | 80 +- .../Storyboards/CustomUIAlertView.storyboard | 131 ++ DansMaRue/Storyboards/LaunchScreen.storyboard | 81 +- DansMaRue/Storyboards/Main.storyboard | 4 +- DansMaRue/Storyboards/Map.storyboard | 223 ++-- DansMaRue/Storyboards/Profile.storyboard | 605 +++++---- DansMaRue/Storyboards/Welcome.storyboard | 109 +- DansMaRue/SupportingFiles/AppDelegate.swift | 227 ++-- DansMaRue/SupportingFiles/Info.plist | 140 +-- DansMaRue/UIFont.swift | 25 + DansMaRue/Utils/FloatingLabelInput.swift | 129 ++ DansMaRue/Utils/MapsUtils.swift | 57 +- DansMaRue/img/logo vert 16112018.png | Bin 187022 -> 0 bytes Podfile | 29 +- Podfile.lock | 258 ++-- Pods/Manifest.lock | 258 ++-- 130 files changed, 6087 insertions(+), 3873 deletions(-) delete mode 100644 .DS_Store create mode 100644 .gitignore delete mode 100644 DansMaRue.xcodeproj/xcuserdata/geoffroy.huet.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 DansMaRue.xcworkspace/xcuserdata/geoffroy.huet.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/icon_10000.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/icon_10000@2x.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/icon_10000@3x.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_12000.imageset/icon_12005.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_12000.imageset/icon_12005@2x.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_12000.imageset/icon_12005@3x.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000@2x.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000@3x.png rename DansMaRue/Assets.xcassets/Anomalie/{Type/icon_8000.imageset => arrow_swipe.imageset}/Contents.json (59%) create mode 100644 DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/MicrosoftTeams-image.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-1.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-2.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-3.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 1.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 2.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-1.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-2.png delete mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-3.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 1.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 2.png create mode 100644 DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus.png create mode 100644 DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie 1.png create mode 100644 DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie 2.png delete mode 100644 DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@2x.png delete mode 100644 DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@3x.png rename DansMaRue/Assets.xcassets/{Anomalie/Type/icon_12000.imageset => Slider/dot_red_ large.imageset}/Contents.json (58%) create mode 100644 DansMaRue/Assets.xcassets/Slider/dot_red_ large.imageset/dot_red_ large.svg rename DansMaRue/Assets.xcassets/{Anomalie/Type/icon_10000.imageset => Slider/dot_white_large.imageset}/Contents.json (58%) create mode 100644 DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/dot_white_large.svg create mode 100644 DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/Contents.json create mode 100644 DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/dot_white_small.svg create mode 100644 DansMaRue/Controllers/CustomUIAlertController.swift delete mode 100644 DansMaRue/Controllers/Profile/ProfileCGUViewController.swift create mode 100644 DansMaRue/Storyboards/CustomUIAlertView.storyboard create mode 100644 DansMaRue/UIFont.swift create mode 100644 DansMaRue/Utils/FloatingLabelInput.swift delete mode 100644 DansMaRue/img/logo vert 16112018.png diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index a3753e5c483e950fad298ed5f7c246a2d6db11b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOG?B*5PdbyV!+LS8<%|oWo{6LxD>>>fRczJW)jqh%Ur=TcqlL9!dF$T%wQIS z3lXV;?pNuq*PjRJ?gAjozFq)x05cXvQD?-od9-VigQrBXF-~!bbM&~v(a1o5u}Rl{ zfRgK8;{luNe@2BCHTTltp5E=<^ND>4p_I*PU%ofr=R6C|oo;1?A503_x-lK=n! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f6f044 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +DansMaRue/.DS_Store +R57-DansMaRue-iOS/DansMaRue.xcodeproj/project.xcworkspace/xcshareddata/ +R57-DansMaRue-iOS/DansMaRue.xcodeproj/project.xcworkspace/xcuserdata/ +R57-DansMaRue-iOS/DansMaRue.xcodeproj/xcuserdata/* +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +.DS_Store +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + diff --git a/DansMaRue.xcodeproj/project.pbxproj b/DansMaRue.xcodeproj/project.pbxproj index 8af8862..bef8edf 100644 --- a/DansMaRue.xcodeproj/project.pbxproj +++ b/DansMaRue.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 03F3C417C6C9DF06A8AF0C27 /* Pods_DansMaRue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5372234C0655F8443E128683 /* Pods_DansMaRue.framework */; }; 2814F5CE1EC1FFAE00156DFE /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2814F5CD1EC1FFAE00156DFE /* User.swift */; }; 2814F5D21EC9CC8E00156DFE /* ProfileDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2814F5D11EC9CC8E00156DFE /* ProfileDetailViewController.swift */; }; 2830EFF62034697A008E6B18 /* BottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2830EFF52034697A008E6B18 /* BottomSheetView.swift */; }; @@ -50,7 +51,10 @@ 28FE2C7D1FA8B57200C86D4D /* getEquipementsOne.json in Resources */ = {isa = PBXBuildFile; fileRef = 28FE2C7C1FA8B57200C86D4D /* getEquipementsOne.json */; }; 4822FD7C2050184200184932 /* MyNotificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4822FD7B2050184200184932 /* MyNotificationDelegate.swift */; }; 48BA5DF7203DC26C004F9490 /* VersionsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48BA5DF6203DC26B004F9490 /* VersionsUtils.swift */; }; - 6AAF02BD22CB05E198086FA0 /* Pods_DansMaRueTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E014747F2A8DEF09932FFEC /* Pods_DansMaRueTests.framework */; }; + 5E2090D62A4049080041B7B7 /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E2090D52A4049080041B7B7 /* UIFont.swift */; }; + 5E7245DA2ABDD139009145C8 /* CustomUIAlertView.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E7245D92ABDD139009145C8 /* CustomUIAlertView.storyboard */; }; + 5E7245DC2ABDD3B2009145C8 /* CustomUIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7245DB2ABDD3B2009145C8 /* CustomUIAlertController.swift */; }; + 5EDFCB5F2ABC7BB300866E92 /* FloatingLabelInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EDFCB5E2ABC7BB300866E92 /* FloatingLabelInput.swift */; }; 910172CF2302F12A00892887 /* ManageFavorites.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 910172CE2302F12A00892887 /* ManageFavorites.storyboard */; }; 910172D12302F26300892887 /* ManageFavoritesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 910172D02302F26300892887 /* ManageFavoritesViewController.swift */; }; 910172D323030FBE00892887 /* TypeFavoritesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 910172D223030FBE00892887 /* TypeFavoritesTableViewCell.swift */; }; @@ -66,7 +70,7 @@ 919023BD234383E300A0E8E2 /* MessageTypeAno.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 919023BC234383E300A0E8E2 /* MessageTypeAno.storyboard */; }; 919023BF2343846900A0E8E2 /* MessageTypeAnoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919023BE2343846900A0E8E2 /* MessageTypeAnoViewController.swift */; }; 919C62A021ECBEBA00FC9C61 /* ramen_mobile.png in Resources */ = {isa = PBXBuildFile; fileRef = 919C629F21ECBEBA00FC9C61 /* ramen_mobile.png */; }; - 9CE533E17D9B18486A08A7A6 /* Pods_DansMaRue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8980F8BE717E1116B830107E /* Pods_DansMaRue.framework */; }; + C8378F10CEA74A892A9B369C /* Pods_DansMaRueTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40F1B0F6C216523E55F5C476 /* Pods_DansMaRueTests.framework */; }; CD0508BB1E8124E90013334A /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD0508BA1E8124E90013334A /* UIDevice.swift */; }; CD0508BD1E826F030013334A /* AddAnomaly.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CD0508BC1E826F030013334A /* AddAnomaly.storyboard */; }; CD052AB01E7AED1F00571E6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD052AAF1E7AED1F00571E6C /* Assets.xcassets */; }; @@ -89,7 +93,6 @@ CDC60C6D1EA66B83000E80A5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDC60C6C1EA66B83000E80A5 /* Main.storyboard */; }; E7A62A511EA8E22C0023A4D3 /* AnomalyDetail.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7A62A501EA8E22C0023A4D3 /* AnomalyDetail.storyboard */; }; E7A62A531EA8E24D0023A4D3 /* AnomalyDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A62A521EA8E24D0023A4D3 /* AnomalyDetailViewController.swift */; }; - E7A62A551EB36D0B0023A4D3 /* ProfileCGUViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A62A541EB36D0B0023A4D3 /* ProfileCGUViewController.swift */; }; E7A62A571EB36D340023A4D3 /* ProfileAboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A62A561EB36D340023A4D3 /* ProfileAboutViewController.swift */; }; E7D3112A1E7C407500B2F0F6 /* Montserrat-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = E7D311251E7C407500B2F0F6 /* Montserrat-Bold.otf */; }; E7D3112B1E7C407500B2F0F6 /* Montserrat-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = E7D311261E7C407500B2F0F6 /* Montserrat-Light.otf */; }; @@ -114,7 +117,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0A6B345BE2531A9036AC9BDF /* Pods-DansMaRueTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.debug.xcconfig"; sourceTree = ""; }; + 06F042F364D47D1CBEA1DD46 /* Pods-DansMaRueTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.debug.xcconfig"; path = "Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.debug.xcconfig"; sourceTree = ""; }; + 109E0C2E8FC4143B393180DB /* Pods-DansMaRue.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.release.xcconfig"; path = "Target Support Files/Pods-DansMaRue/Pods-DansMaRue.release.xcconfig"; sourceTree = ""; }; + 12E82EB4E199AB09BE8DEFF0 /* Pods-DansMaRue.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.debug.xcconfig"; path = "Target Support Files/Pods-DansMaRue/Pods-DansMaRue.debug.xcconfig"; sourceTree = ""; }; 2814F5CD1EC1FFAE00156DFE /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = Model/User.swift; sourceTree = ""; }; 2814F5D11EC9CC8E00156DFE /* ProfileDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileDetailViewController.swift; path = Controllers/Profile/ProfileDetailViewController.swift; sourceTree = ""; }; 2830EFF52034697A008E6B18 /* BottomSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BottomSheetView.swift; path = Controllers/Map/BottomSheetView.swift; sourceTree = ""; }; @@ -158,14 +163,15 @@ 28FE2C781FA73CD700C86D4D /* TypeContribution.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = TypeContribution.storyboard; path = Storyboards/TypeContribution.storyboard; sourceTree = ""; }; 28FE2C7A1FA73DB900C86D4D /* TypeContributionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TypeContributionViewController.swift; path = Controllers/Map/TypeContributionViewController.swift; sourceTree = ""; }; 28FE2C7C1FA8B57200C86D4D /* getEquipementsOne.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = getEquipementsOne.json; path = Services/getEquipementsOne.json; sourceTree = ""; }; + 40F1B0F6C216523E55F5C476 /* Pods_DansMaRueTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DansMaRueTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4822FD7B2050184200184932 /* MyNotificationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyNotificationDelegate.swift; sourceTree = ""; }; 48BA5DF6203DC26B004F9490 /* VersionsUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VersionsUtils.swift; path = Utils/VersionsUtils.swift; sourceTree = ""; }; - 4D0E6427873BDC408B820D63 /* Pods-DansMaRue.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRue/Pods-DansMaRue.debug.xcconfig"; sourceTree = ""; }; - 5A59C084EB229C729ABB8B8B /* Pods-DansMaRueTests.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.staging.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.staging.xcconfig"; sourceTree = ""; }; - 5E014747F2A8DEF09932FFEC /* Pods_DansMaRueTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DansMaRueTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6C4ECCEB5272A1BB61F05431 /* Pods-DansMaRue.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.release.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRue/Pods-DansMaRue.release.xcconfig"; sourceTree = ""; }; - 8980F8BE717E1116B830107E /* Pods_DansMaRue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DansMaRue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8F84B15D8DBA2950D5A2756D /* Pods-DansMaRue.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.staging.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRue/Pods-DansMaRue.staging.xcconfig"; sourceTree = ""; }; + 4DCB5D8FAD3B90D6EC00DF3C /* Pods-DansMaRueTests.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.staging.xcconfig"; path = "Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.staging.xcconfig"; sourceTree = ""; }; + 5372234C0655F8443E128683 /* Pods_DansMaRue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DansMaRue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E2090D52A4049080041B7B7 /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; + 5E7245D92ABDD139009145C8 /* CustomUIAlertView.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = CustomUIAlertView.storyboard; path = Storyboards/CustomUIAlertView.storyboard; sourceTree = ""; }; + 5E7245DB2ABDD3B2009145C8 /* CustomUIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CustomUIAlertController.swift; path = Controllers/CustomUIAlertController.swift; sourceTree = ""; }; + 5EDFCB5E2ABC7BB300866E92 /* FloatingLabelInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FloatingLabelInput.swift; path = Utils/FloatingLabelInput.swift; sourceTree = ""; }; 910172CE2302F12A00892887 /* ManageFavorites.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ManageFavorites.storyboard; sourceTree = ""; }; 910172D02302F26300892887 /* ManageFavoritesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageFavoritesViewController.swift; sourceTree = ""; }; 910172D223030FBE00892887 /* TypeFavoritesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeFavoritesTableViewCell.swift; sourceTree = ""; }; @@ -181,7 +187,8 @@ 919023BC234383E300A0E8E2 /* MessageTypeAno.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MessageTypeAno.storyboard; sourceTree = ""; }; 919023BE2343846900A0E8E2 /* MessageTypeAnoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTypeAnoViewController.swift; sourceTree = ""; }; 919C629F21ECBEBA00FC9C61 /* ramen_mobile.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ramen_mobile.png; sourceTree = ""; }; - BCAA5B9EF47F2D99361C3992 /* Pods-DansMaRueTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.release.xcconfig"; sourceTree = ""; }; + 95CC8D0E7D287776AA0DCC98 /* Pods-DansMaRueTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRueTests.release.xcconfig"; path = "Target Support Files/Pods-DansMaRueTests/Pods-DansMaRueTests.release.xcconfig"; sourceTree = ""; }; + A7C8035B04718387FD0900C3 /* Pods-DansMaRue.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DansMaRue.staging.xcconfig"; path = "Target Support Files/Pods-DansMaRue/Pods-DansMaRue.staging.xcconfig"; sourceTree = ""; }; CD0508BA1E8124E90013334A /* UIDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIDevice.swift; path = Extensions/UIDevice.swift; sourceTree = ""; }; CD0508BC1E826F030013334A /* AddAnomaly.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = AddAnomaly.storyboard; path = Storyboards/Anomalie/AddAnomaly.storyboard; sourceTree = ""; }; CD052AA51E7AED1F00571E6C /* DansMaRue.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DansMaRue.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -208,7 +215,6 @@ CDC60C6C1EA66B83000E80A5 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Storyboards/Main.storyboard; sourceTree = ""; }; E7A62A501EA8E22C0023A4D3 /* AnomalyDetail.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = AnomalyDetail.storyboard; path = Storyboards/Anomalie/AnomalyDetail.storyboard; sourceTree = ""; }; E7A62A521EA8E24D0023A4D3 /* AnomalyDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnomalyDetailViewController.swift; path = Controllers/Anomalie/AnomalyDetailViewController.swift; sourceTree = ""; }; - E7A62A541EB36D0B0023A4D3 /* ProfileCGUViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileCGUViewController.swift; path = Controllers/Profile/ProfileCGUViewController.swift; sourceTree = ""; }; E7A62A561EB36D340023A4D3 /* ProfileAboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileAboutViewController.swift; path = Controllers/Profile/ProfileAboutViewController.swift; sourceTree = ""; }; E7D311251E7C407500B2F0F6 /* Montserrat-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Montserrat-Bold.otf"; path = "Fonts/Montserrat-Bold.otf"; sourceTree = ""; }; E7D311261E7C407500B2F0F6 /* Montserrat-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Montserrat-Light.otf"; path = "Fonts/Montserrat-Light.otf"; sourceTree = ""; }; @@ -227,7 +233,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9CE533E17D9B18486A08A7A6 /* Pods_DansMaRue.framework in Frameworks */, + 03F3C417C6C9DF06A8AF0C27 /* Pods_DansMaRue.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -235,7 +241,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6AAF02BD22CB05E198086FA0 /* Pods_DansMaRueTests.framework in Frameworks */, + C8378F10CEA74A892A9B369C /* Pods_DansMaRueTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -263,6 +269,7 @@ 288C59691EB3836500A9EC5B /* String+DMR.swift */, 28D77ADB1F161B1600FFDDE0 /* PaddingLabel+DMR.swift */, 28DD7FC31F8B739E00EF8993 /* UIImage+DMR.swift */, + 5E2090D52A4049080041B7B7 /* UIFont.swift */, ); name = Extensions; sourceTree = ""; @@ -360,17 +367,26 @@ path = img; sourceTree = ""; }; - BF5924E65EAC55368B9BF765 /* Pods */ = { + BAD938BB09DAD746A035EDA6 /* Pods */ = { isa = PBXGroup; children = ( - 4D0E6427873BDC408B820D63 /* Pods-DansMaRue.debug.xcconfig */, - 6C4ECCEB5272A1BB61F05431 /* Pods-DansMaRue.release.xcconfig */, - 0A6B345BE2531A9036AC9BDF /* Pods-DansMaRueTests.debug.xcconfig */, - BCAA5B9EF47F2D99361C3992 /* Pods-DansMaRueTests.release.xcconfig */, - 8F84B15D8DBA2950D5A2756D /* Pods-DansMaRue.staging.xcconfig */, - 5A59C084EB229C729ABB8B8B /* Pods-DansMaRueTests.staging.xcconfig */, + 12E82EB4E199AB09BE8DEFF0 /* Pods-DansMaRue.debug.xcconfig */, + A7C8035B04718387FD0900C3 /* Pods-DansMaRue.staging.xcconfig */, + 109E0C2E8FC4143B393180DB /* Pods-DansMaRue.release.xcconfig */, + 06F042F364D47D1CBEA1DD46 /* Pods-DansMaRueTests.debug.xcconfig */, + 4DCB5D8FAD3B90D6EC00DF3C /* Pods-DansMaRueTests.staging.xcconfig */, + 95CC8D0E7D287776AA0DCC98 /* Pods-DansMaRueTests.release.xcconfig */, ); - name = Pods; + path = Pods; + sourceTree = ""; + }; + BE4EF99076C547E8D43BC79C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5372234C0655F8443E128683 /* Pods_DansMaRue.framework */, + 40F1B0F6C216523E55F5C476 /* Pods_DansMaRueTests.framework */, + ); + name = Frameworks; sourceTree = ""; }; CD0508B71E8124180013334A /* Utils */ = { @@ -381,6 +397,7 @@ 28DD78A01E8C51B900633D2B /* ReachabilityUtils.swift */, 288C59721EBA37FB00A9EC5B /* DateUtils.swift */, 28B30FAC1F9F971E0011831A /* ContextManager.swift */, + 5EDFCB5E2ABC7BB300866E92 /* FloatingLabelInput.swift */, ); name = Utils; sourceTree = ""; @@ -391,9 +408,9 @@ CD052AA71E7AED1F00571E6C /* DansMaRue */, CD052ABC1E7AED1F00571E6C /* DansMaRueTests */, CD052AA61E7AED1F00571E6C /* Products */, - BF5924E65EAC55368B9BF765 /* Pods */, - F301A058DCE46FEBA110893D /* Frameworks */, 28535CC31FD5B9E300D17E99 /* Recovered References */, + BAD938BB09DAD746A035EDA6 /* Pods */, + BE4EF99076C547E8D43BC79C /* Frameworks */, ); sourceTree = ""; usesTabs = 0; @@ -448,6 +465,7 @@ CD052AD71E7AF6E100571E6C /* Profile.storyboard */, CDC60C681EA655CC000E80A5 /* CompteParisien.storyboard */, 28FE2C781FA73CD700C86D4D /* TypeContribution.storyboard */, + 5E7245D92ABDD139009145C8 /* CustomUIAlertView.storyboard */, ); name = Storyboards; sourceTree = ""; @@ -470,6 +488,7 @@ 28DD78A91E8D07A400633D2B /* Anomalie */, 28DD78A21E8D076C00633D2B /* Welcome */, CD052AD91E7AF72B00571E6C /* MainViewController.swift */, + 5E7245DB2ABDD3B2009145C8 /* CustomUIAlertController.swift */, ); name = Controllers; sourceTree = ""; @@ -495,7 +514,6 @@ E7E5AAC31EA0FB2100BD2608 /* ProfileSettingsViewController.swift */, E7E5AAC51EA0FDDC00BD2608 /* ProfilePreferencesViewController.swift */, CDC60C6A1EA65619000E80A5 /* CompteParisienViewController.swift */, - E7A62A541EB36D0B0023A4D3 /* ProfileCGUViewController.swift */, E7A62A561EB36D340023A4D3 /* ProfileAboutViewController.swift */, 2814F5D11EC9CC8E00156DFE /* ProfileDetailViewController.swift */, ); @@ -514,15 +532,6 @@ name = Map; sourceTree = ""; }; - F301A058DCE46FEBA110893D /* Frameworks */ = { - isa = PBXGroup; - children = ( - 8980F8BE717E1116B830107E /* Pods_DansMaRue.framework */, - 5E014747F2A8DEF09932FFEC /* Pods_DansMaRueTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -530,13 +539,14 @@ isa = PBXNativeTarget; buildConfigurationList = CD052AC21E7AED1F00571E6C /* Build configuration list for PBXNativeTarget "DansMaRue" */; buildPhases = ( - EE594E599E9E4DF30818D416 /* [CP] Check Pods Manifest.lock */, + BAE6C04B359651DC6E438738 /* [CP] Check Pods Manifest.lock */, + 5E1C45BC2A3B6DC700737BFA /* ShellScript */, CD052AA11E7AED1E00571E6C /* Sources */, CD052AA21E7AED1E00571E6C /* Frameworks */, CD052AA31E7AED1E00571E6C /* Resources */, - BDEC07101AC65D4DC23F9FBE /* [CP] Embed Pods Frameworks */, - 054F17B96F4BE719F82F2110 /* [CP] Copy Pods Resources */, CDE0AA281E9E46F9006EA9FB /* ShellScript */, + ABF501BB90D7926F02D59133 /* [CP] Embed Pods Frameworks */, + 67DAB7FE97541D3B2C891ED4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -551,7 +561,7 @@ isa = PBXNativeTarget; buildConfigurationList = CD052AC51E7AED1F00571E6C /* Build configuration list for PBXNativeTarget "DansMaRueTests" */; buildPhases = ( - 5AEFD07159E4B4318B99685A /* [CP] Check Pods Manifest.lock */, + 3ABF7053BAB130B2633ED6EB /* [CP] Check Pods Manifest.lock */, CD052AB51E7AED1F00571E6C /* Sources */, CD052AB61E7AED1F00571E6C /* Frameworks */, CD052AB71E7AED1F00571E6C /* Resources */, @@ -636,6 +646,7 @@ 289FCA131E9AD46F00318373 /* DescriptiveAnomaly.storyboard in Resources */, CD052AD81E7AF6E100571E6C /* Profile.storyboard in Resources */, 28DD78B21E8D0CE300633D2B /* TypeAnomalie.storyboard in Resources */, + 5E7245DA2ABDD139009145C8 /* CustomUIAlertView.storyboard in Resources */, 28CBD7231E9027CB00BBFB87 /* PopupPhoto.storyboard in Resources */, E7A62A511EA8E22C0023A4D3 /* AnomalyDetail.storyboard in Resources */, 916883BE247BBE37000D373E /* GoogleService-Info.plist in Resources */, @@ -664,58 +675,83 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 054F17B96F4BE719F82F2110 /* [CP] Copy Pods Resources */ = { + 3ABF7053BAB130B2633ED6EB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-resources.sh", - "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", - "${PODS_ROOT}/GooglePlaces/Frameworks/GooglePlaces.framework/Resources/GooglePlaces.bundle", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GooglePlaces.bundle", + "$(DERIVED_FILE_DIR)/Pods-DansMaRueTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 5AEFD07159E4B4318B99685A /* [CP] Check Pods Manifest.lock */ = { + 5E1C45BC2A3B6DC700737BFA /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-DansMaRueTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${INFOPLIST_FILE}\")\n\nbuildNumber=$(($buildNumber + 1))\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${INFOPLIST_FILE}\"\n"; + }; + 67DAB7FE97541D3B2C891ED4 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-resources.sh", + "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", + "${PODS_ROOT}/GooglePlaces/Frameworks/GooglePlaces.framework/Resources/GooglePlaces.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GooglePlaces.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-resources.sh\"\n"; showEnvVarsInLog = 0; }; - BDEC07101AC65D4DC23F9FBE /* [CP] Embed Pods Frameworks */ = { + ABF501BB90D7926F02D59133 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseSessions/FirebaseSessions.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework.dSYM", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/8F3E82C3-52DF-39E5-809D-3389B6CBCAD4.bcsymbolmap", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/0DC8DAD8-C6D7-343B-ACBF-79B48068D7CC.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/PromisesSwift/Promises.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", "${BUILT_PRODUCTS_DIR}/TTGSnackbar/TTGSnackbar.framework", @@ -723,13 +759,17 @@ ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseSessions.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework", - "${DWARF_DSYM_FOLDER_PATH}/Mapbox.framework.dSYM", - "${BUILT_PRODUCTS_DIR}/8F3E82C3-52DF-39E5-809D-3389B6CBCAD4.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/0DC8DAD8-C6D7-343B-ACBF-79B48068D7CC.bcsymbolmap", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Promises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TTGSnackbar.framework", @@ -740,36 +780,40 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DansMaRue/Pods-DansMaRue-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - CDE0AA281E9E46F9006EA9FB /* ShellScript */ = { + BAE6C04B359651DC6E438738 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-DansMaRue-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Fabric/run\" de2a9cb6ce619d541840b0491f6568489833545f 2c6f683bae3205f23bbfd95a8ef5e6702603c404850f628b5c77cea053e2581b"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - EE594E599E9E4DF30818D416 /* [CP] Check Pods Manifest.lock */ = { + CDE0AA281E9E46F9006EA9FB /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-DansMaRue-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "\"${PODS_ROOT}/Fabric/run\" de2a9cb6ce619d541840b0491f6568489833545f 2c6f683bae3205f23bbfd95a8ef5e6702603c404850f628b5c77cea053e2581b\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -786,6 +830,8 @@ 28DD78A81E8D076C00633D2B /* WelcomeViewController.swift in Sources */, 910172D12302F26300892887 /* ManageFavoritesViewController.swift in Sources */, 28DD78A11E8C51B900633D2B /* ReachabilityUtils.swift in Sources */, + 5E2090D62A4049080041B7B7 /* UIFont.swift in Sources */, + 5E7245DC2ABDD3B2009145C8 /* CustomUIAlertController.swift in Sources */, 2814F5D21EC9CC8E00156DFE /* ProfileDetailViewController.swift in Sources */, 910172D72304547100892887 /* ManageAddressViewController.swift in Sources */, 919023BF2343846900A0E8E2 /* MessageTypeAnoViewController.swift in Sources */, @@ -803,7 +849,6 @@ 28577FD01E8AB542006F7478 /* UIButton+DMR.swift in Sources */, E7E5AAC21EA0F0E500BD2608 /* ProfileViewController.swift in Sources */, 28DD78A61E8D076C00633D2B /* WelcomePageContentViewController.swift in Sources */, - E7A62A551EB36D0B0023A4D3 /* ProfileCGUViewController.swift in Sources */, CD052ADA1E7AF72B00571E6C /* MainViewController.swift in Sources */, 2814F5CE1EC1FFAE00156DFE /* User.swift in Sources */, 28DD78991E8B0D2600633D2B /* UIView+DMR.swift in Sources */, @@ -834,6 +879,7 @@ 28DD7FC41F8B739E00EF8993 /* UIImage+DMR.swift in Sources */, 48BA5DF7203DC26C004F9490 /* VersionsUtils.swift in Sources */, 4822FD7C2050184200184932 /* MyNotificationDelegate.swift in Sources */, + 5EDFCB5F2ABC7BB300866E92 /* FloatingLabelInput.swift in Sources */, E7E5AABF1EA0ECB100BD2608 /* BottomSheetViewController.swift in Sources */, CD0508BB1E8124E90013334A /* UIDevice.swift in Sources */, E7E5AAC61EA0FDDC00BD2608 /* ProfilePreferencesViewController.swift in Sources */, @@ -897,6 +943,7 @@ ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + EXCLUDED_ARCHS = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -911,7 +958,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.1; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -922,23 +969,26 @@ }; 288C596F1EB8908400A9EC5B /* Staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8F84B15D8DBA2950D5A2756D /* Pods-DansMaRue.staging.xcconfig */; + baseConfigurationReference = A7C8035B04718387FD0900C3 /* Pods-DansMaRue.staging.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = DansMaRue/DansMaRue.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 168; - DEVELOPMENT_TEAM = WTEMWM5569; + CURRENT_PROJECT_VERSION = 199; + DEVELOPMENT_TEAM = WTFMWM5569; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WTEMWM5569; + EXCLUDED_ARCHS = ""; INFOPLIST_FILE = DansMaRue/SupportingFiles/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DENVIRONMENT_STAGING"; PRODUCT_BUNDLE_IDENTIFIER = com.paris.signalement; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "a8529573-15d0-4d2c-886d-524d953e8430"; PROVISIONING_PROFILE_SPECIFIER = DansMaRue_Dev; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DansMaRue_Dev; SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 4.2; }; @@ -946,7 +996,7 @@ }; 288C59701EB8908400A9EC5B /* Staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5A59C084EB229C729ABB8B8B /* Pods-DansMaRueTests.staging.xcconfig */; + baseConfigurationReference = 4DCB5D8FAD3B90D6EC00DF3C /* Pods-DansMaRueTests.staging.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -996,6 +1046,7 @@ ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + EXCLUDED_ARCHS = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -1010,7 +1061,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.1; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -1055,6 +1106,7 @@ ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + EXCLUDED_ARCHS = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -1063,7 +1115,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.1; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -1073,23 +1125,26 @@ }; CD052AC31E7AED1F00571E6C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4D0E6427873BDC408B820D63 /* Pods-DansMaRue.debug.xcconfig */; + baseConfigurationReference = 12E82EB4E199AB09BE8DEFF0 /* Pods-DansMaRue.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = DansMaRue/DansMaRue.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 168; - DEVELOPMENT_TEAM = WTEMWM5569; + CURRENT_PROJECT_VERSION = 198; + DEVELOPMENT_TEAM = WTFMWM5569; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WTEMWM5569; + EXCLUDED_ARCHS = ""; INFOPLIST_FILE = DansMaRue/SupportingFiles/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DENVIRONMENT_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.paris.signalement; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "a8529573-15d0-4d2c-886d-524d953e8430"; PROVISIONING_PROFILE_SPECIFIER = DansMaRue_Dev; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DansMaRue_Dev; SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 4.2; }; @@ -1097,23 +1152,27 @@ }; CD052AC41E7AED1F00571E6C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6C4ECCEB5272A1BB61F05431 /* Pods-DansMaRue.release.xcconfig */; + baseConfigurationReference = 109E0C2E8FC4143B393180DB /* Pods-DansMaRue.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = DansMaRue/DansMaRue.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = WTEMWM5569; + CURRENT_PROJECT_VERSION = ""; + DEVELOPMENT_TEAM = WTFMWM5569; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = WTEMWM5569; + EXCLUDED_ARCHS = ""; GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = DansMaRue/SupportingFiles/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DENVIRONMENT_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.paris.signalement; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "e143baa2-8a93-4844-8fd3-331e72330f17"; PROVISIONING_PROFILE_SPECIFIER = DansMaRue_Dist_v2; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = DansMaRue_Dev; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 4.2; @@ -1122,7 +1181,7 @@ }; CD052AC61E7AED1F00571E6C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0A6B345BE2531A9036AC9BDF /* Pods-DansMaRueTests.debug.xcconfig */; + baseConfigurationReference = 06F042F364D47D1CBEA1DD46 /* Pods-DansMaRueTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -1138,7 +1197,7 @@ }; CD052AC71E7AED1F00571E6C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BCAA5B9EF47F2D99361C3992 /* Pods-DansMaRueTests.release.xcconfig */; + baseConfigurationReference = 95CC8D0E7D287776AA0DCC98 /* Pods-DansMaRueTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; diff --git a/DansMaRue.xcodeproj/xcuserdata/geoffroy.huet.xcuserdatad/xcschemes/xcschememanagement.plist b/DansMaRue.xcodeproj/xcuserdata/geoffroy.huet.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 8fa143e..0000000 --- a/DansMaRue.xcodeproj/xcuserdata/geoffroy.huet.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - SchemeUserState - - DansMaRue-Dev.xcscheme_^#shared#^_ - - orderHint - 1 - - DansMaRue-Re7.xcscheme_^#shared#^_ - - orderHint - 2 - - DansMaRue.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/DansMaRue.xcworkspace/xcuserdata/geoffroy.huet.xcuserdatad/UserInterfaceState.xcuserstate b/DansMaRue.xcworkspace/xcuserdata/geoffroy.huet.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index d0c6d0647aa760567adaca26202cc68ed273b793..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27418 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/icon_10000.png b/DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/icon_10000.png deleted file mode 100644 index 323f3ee99ee21473664bd188de1f79455f5a5fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_12000.imageset/icon_12005.png b/DansMaRue/Assets.xcassets/Anomalie/Type/icon_12000.imageset/icon_12005.png deleted file mode 100644 index be77e5e196d8bb7807f94278cf3d2a81ebede59b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000.png b/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000.png deleted file mode 100644 index 326f482faf641a0a941dcee1ed93c556f3b3e130..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmV-M0lEH(P)Px$07*naR7ef&RZ9|rFbqwL%F-*~N#qC}#{@?q$(xtgG!%eeH#T&Dhghc#v>OI2F|i6%83E6ya(6Q0w$JT=y+`uGCMOKVsIZN< z_s$d4VwEXiZvs?&j-kznpaM=smj>B3misPe#5&hmfU;(h88izfWl3=*L{dX^As@|2 zPDyWzX7W%F=w&zUn5#><#!R#`v{n!uQF%@{^zqWeEVuv?rn)Vm(#Qzh2 Y0=6KDQ&$UD_W%F@07*qoM6N<$f}#Y6-v9sr diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000@2x.png b/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/icon_8000@2x.png deleted file mode 100644 index 433ef91179e32ff44f633f2c415a9d48f28311a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 641 zcmV-{0)G98P)Px%J4r-ARA>e5SkZ9;ArJ+VIX`0uxemN^UR+ukRHFKzD{YUD$z1e$*8y!*IZaz_tph-G`#=RRYZ@uzO6uL~9cCQJ5otx`uyi?OvM#zv+PT*Hp{&1yQNHSySgYJN>jDUYIpK2ik(>77i z2}ozD7LrUv^id~Z+BelgCRQA>R0}!Cxr(awJ8+e#l}&CQK&pk5)7%eH$fb87ofXGC z_zu_#y7VsOVB4e$J^_<6$kMxzjTwu)^-=;)D!2A7v>|dHRDznm3*nQi+J%hUxC4uj zjbTGTt592M7Y%?un|SlbDfqU{(bm`QY<)BU`UAhBK=A53Z;}$nHFMa&_AHt-NemPx&D@jB_RCodHn^AI`Fc5}C+;JY8o7BBQdIQEeK25U zXB@g4phFzkMPdcb%+G@jT4`7MED7m1IEMoSKmY_l00cfNfn>~zKc!bI3YbyMMPS@r zlDM1suiI^4dGBJQ04mb-;^1cY1Hha%Y&UnuZ%Lm2IW<^cpIJL>^Jff)Lo6zvZQ)GQ zq62zLmuqg2X{}3Vi$l81BKUpdGCH8dCD(5v8eSAGSsA)(brl^DMUByW>c+(M{Tjn; zlIHGCzv$1p=04w4i671#H}gE-ak)d9E($kW(`jh$+n(pQ8T%S~m;vI71VDOB-0`vo zbJ~~+)Ne}vd}g+69Mw@lio~D6iB{Q#c&Wd=8rDcbmi&-?gUj zu+reLk1Eer^G9}bI2_X)5IgM$>A2y%eGckrH%h)Ja5$#<5NkF>8g4X+dc}TDuZpJc z#(6Y293x5(sw>YusXmYOZFRTms$WzVKIgCK)kc-?A4A_=$W5O?p&_%0XTIZzTN9b!t|{tGhzDL(@M0gysKF{1}5 zfXwb<(#BM&1MirjD0V=CqSyiEH5rPcK!=AF6h#3L|BE4-8%8LK4H#Xg!j=V!Viz<{ zhoVRjBuPYqXJMOgp*YL>HUNsEK&t9JP!!u3K~Z#26rFH;ZtFDOX;2isk=YiQ@GfTC zkXYUTNFfVA0HhF5%up1^0>p6wWLGGP(hmehQJ@1wQGJjTilR6t`EC(}EejMyVWdJ) zWH?Y1r7l7?D2f6gD2l@4z)%#SC^{&L!t_y@ioKpEG)GL|Wu2nyLG-}*CbaolIohsh zZvkm8RJ)nnAw{C+pm+3Q(HAL?zpdDx=Xbr|_we^$mWbYiILzF=T+(XBaHtjg(r4L- zn4hEcpt=F2UgXx{y{Xvquo~kvtGdqk1_2NN0T2Lzk4xY$lumnOn5EB%00000NkvXX Hu0mjfQ$ULb diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/Contents.json b/DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/Contents.json similarity index 59% rename from DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/Contents.json rename to DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/Contents.json index 5606cd7..9005cdd 100644 --- a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_8000.imageset/Contents.json +++ b/DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/Contents.json @@ -1,23 +1,21 @@ { "images" : [ { + "filename" : "MicrosoftTeams-image.png", "idiom" : "universal", - "filename" : "icon_8000.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon_8000@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon_8000@3x.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/MicrosoftTeams-image.png b/DansMaRue/Assets.xcassets/Anomalie/arrow_swipe.imageset/MicrosoftTeams-image.png new file mode 100644 index 0000000000000000000000000000000000000000..13dd0c55c9ff164fa0fcb376fea80a09c01e7449 GIT binary patch literal 920 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 S8UODe+uGcYde_YR(tiOs_i^q3 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-2.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-2.png deleted file mode 100644 index f58c5f08da1e90d03f2f27f4c4c5fb4ede22112b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 S8UODe+uGcYde_YR(tiOs_i^q3 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-3.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/etoile-3.png deleted file mode 100644 index f58c5f08da1e90d03f2f27f4c4c5fb4ede22112b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3552 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 S8UODe+uGcYde_YR(tiOs_i^q3 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 1.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 1.png new file mode 100644 index 0000000000000000000000000000000000000000..8d18f07b2c535399e3f66d0c8e4d378131e3411c GIT binary patch literal 3705 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 Am;e9( literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 2.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check 2.png new file mode 100644 index 0000000000000000000000000000000000000000..8d18f07b2c535399e3f66d0c8e4d378131e3411c GIT binary patch literal 3705 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 Am;e9( literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_check.imageset/favorite_check.png new file mode 100644 index 0000000000000000000000000000000000000000..8d18f07b2c535399e3f66d0c8e4d378131e3411c GIT binary patch literal 3705 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 Am;e9( literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/Contents.json b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/Contents.json index 047b77b..b9ab93c 100644 --- a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/Contents.json +++ b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { + "filename" : "favorite_plus.png", "idiom" : "universal", - "filename" : "etoilePlus (3)-3.png", "scale" : "1x" }, { + "filename" : "favorite_plus 1.png", "idiom" : "universal", - "filename" : "etoilePlus (3)-1.png", "scale" : "2x" }, { + "filename" : "favorite_plus 2.png", "idiom" : "universal", - "filename" : "etoilePlus (3)-2.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-1.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-1.png deleted file mode 100644 index c0ea2eec060d77841665039bcd9851dc11c5baf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8746 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 Z99yTP>jW;RL-#s>k)Ek;r8YM5zW|FKsU`pb diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-2.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-2.png deleted file mode 100644 index c0ea2eec060d77841665039bcd9851dc11c5baf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8746 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 Z99yTP>jW;RL-#s>k)Ek;r8YM5zW|FKsU`pb diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-3.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/etoilePlus (3)-3.png deleted file mode 100644 index c0ea2eec060d77841665039bcd9851dc11c5baf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8746 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 Z99yTP>jW;RL-#s>k)Ek;r8YM5zW|FKsU`pb diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 1.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 1.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa280de97b671b87eb47defe8bd5322ff698c82 GIT binary patch literal 5869 zcmbVQ2{@Ep-@iwcJzGVgX^^b5S;rb<%f5xIV-`)!5@X+&G%ArimWYy&BqFktQnHgI zOUZ-Enz2OT8$CT;@B3Zv_kHj8-q&^JKKD7l^WVY304+HJe-F)0RZ4JF-F@k z?;g92eJ}I(V@9AF^Tt6ncJc!NF2UUf0&Zjr0{|PCXy-_CG&e(GDI`Sf_H6jYE9#wjmaF*br~5Iu4?v z4b}`oF$IwDGz>V1cshmj@$Ya5X4Q4GKppz*SI6Fcbm-{&hi^+I(>YlnvVOmoDa$7UV3A zMnyrPfq{XFfk;J)uNM@quC5M+A)p9^0u!O&7fhyMf)vPp5`RlT6AjgzdWY6Ntn9#!>x!eSRv(VWD^*JPA*x z`7yEZ-&pEd3XS4-mhwM{{yqIq3YeiaH~+2UU)n+<{Z`>eGYDY1@hc$z676RfOvOWO z@O~73Uo74rfN7@0t~XSazAqj_qxjlUC_aC0l-1uRgZ1@yH%1+N!W@Gol6U=(`G-|_ zG=_%Pf*@cBqykJu0gkX^ZXH4u1w+We5GWYzFQ_>MMk|zL2E&n5mF=)aVRwe9FJ91f+=9|O3DgKa1|v5 zPn@!{f{Gd(jwBG2)e!1`xyxLSreOVddGPmo-2ZdEr7w}0hZvv#Sk7*C?h*)PO!Q-J zZSb%BvBjVN)$<{Oe{u$e!S3dQ76iLX0z3}#Ynb>SN8m59foJhdq5lgn{(|{Y2(&pu4I2mow1+?&r_Be@OGc;mq2x+x=a%m=C|p9-hpsb-v7^wj^+a z3;_IoCg_uPLD}=J!8Dh_;GOXG236$Y`A2x1MDVpk&pc1UQ@c3g5Bha}>@((;d?t@M zFOxVBS4F(uE1WCD@@J~zvHZJ2od?0O$|=-|vN)8*QyUokpm0Z(JZ3mWYWT2;MAzDQ zo%>F5n|PvOT8^Z*q4eg>JClsY(8a)}nx@dEy70x!3=yQU7`m9}8}jFptobw{81!ADtp@%~mHL`Ao=l}!j#+$NP$76^_x<*DXlIUjxQu6(6$(@oOXZXtq zU57RJ$KZOtP6h_g6K&7+?v8p?k3c~%N|g;8sDS8ZhV~P9rT}z4@vtQMvCiV z^_KT0y|7g}P-QMew2XQ&*^Vbz#ynA%mYz`u-c}N?H|iEBZ)ubt6m+$Vo_tGg9j7 z>qjig!vzh`2wFKgeP*!|g<_{)czb(03or!wDok+i4MuOfq97!WC)=B|H#>g_#jD)n z{qR0z%Dpjc!vdg4(R-*hF2n{&b7vMi`vyMUlQBUT3y7^DIxv_B*RgF7#}VYQXQrPu z59Le!+7woZrUZF>q^(`=j6j+9Q0ZmBMTBX~ zP3LOzSl}aHO8r+u*5ccKV==qY&yQxsYzOD0+ER66zCZ%LxJOH1HxgW{Z zb!_A`*BNrNbZ(f9gZ*kKkn_BkW6|!Tj>YtM!P~@l5XYOMh`WuvFdO|JgV z2B=D4B>DP6U0q$%DI--~Wp+-Nte7P3?mUX9kT@n|hIX)XaEOH2INhIDzt>^h^gdD} z3e)naGmID!v3*fINg9io)i^+{8nN6}=(l2ZW81cncgd0) zBtj$Hb+qq=+#B!UvD-gNbGo13923hKIt|?nuXAk#Jd7ui6*6&8e3v*^@{R9tU22*9 z`j+MzDLb4E+*0IYAKmQMSX^-|4if0CTv~&ixOO?Vg*wF_nI$L0GxJvYh#4_Pq}N>R z#u!Ko%9Y5)pn}tTZ(uh4Wp)D#tR~%_S@Sx{IE;uwV7*IJ3Jp*vFbQ-N=AyGpUxi8b(Jm- zXH+nB`r~R_7s?jfyI=M@Cso<6T#QLauNW&%K9h4Q&VxYZ9tM(IM(=;g`m)n=VUvH> zOpXQkur@*oTv=PwZtIN9On!!5Ny|H-%DvzseSn8?|4k)LG(uUJ7j-hKby}B9SSYNy znr(ET5c@r43o2gPx}2}G{WLgTZEG~5ys2rXaiMX++l(jx;7|zs=g)U7U#q^M2q(l< zN&v#(+l7TQO+eV5*0H|(1;lY{2Zuw(HZ+&FIa`B;cS2UGwy8~Eh|q!Ts=$EmlDaQh z+ST>m3NPE`l+8ChDKIJ=sc$VLpXt2W=-O37ux3mzJo`a30e~P)af6{%hlH0GCL~|S zw@zg3?P0v&*?Ts0`jz|2h3`90Q;)N9q;}~{zUWa+*P?0{EgTnjfzew1!pt< zuw7z2Yug7-3CMY}_qseL!|*g>OYwEE8CF@)Al+z{!ts-gi=ZhG|>RU|)s9MjUXFT3&>Hg179%os^gmvRQ((h==c9p68uoMWAgV3C!TT?RUn(&2us z%TpcB4?YcREY3=8YwsWoPQ2{s9P*ykh$9ESqP@{Ef*)qz^BiKMe>KrG70*+ahE>HyOuEN4OP`*9 zI~YJE{&+>#1w*{Ai639%M`13Ay5tM`0a&g{mO zpPmwek!pC>5~qr`?#}dQ9T~%vis8di(d?I)7o}!w;W(fJ!1~LVzN2XyH8VM(fAq{~ zoCeR$D%tD17bVs1iSE<(Lb7_Lc8|%%FFXX(+eEd8>sJbpp_`$K9t3&WGiCno({rnw z?j6n0JM<@2%@@SI@miQ)YhpFTzj`EGEGm+9sZiRw+~L#))zY$uF;YcBGEZk9AoLyY zoJ@{uef6s2>?7XD8T*;kw5d1Zx;>6Pv2^su3T>)C3hADyu3W2W}-=oVeQ0vW`Ps)2*~Ivck9*g$y_HeU-#}s`$P9Hyzh4mVdN&^ z^-dp{3cA(u;rML+wU7sl(!hoBLKTD6qEqM-ub?3TFDAAoLqKd9wfxj^;bmCVdpKiI zJNIF6X=y3uwLmQOw)m=fu8PofccWD6wHAz){JX2-rLHb^A(QRK^tX>3Z68Ynbcw&V zQ$T&AFpM{xFKMwQM(2pVU~w(nquZYSaeqe-_o!F}Ml&PDB!f;CmNJ!yx)`SY9w|J- zmcX@IM0yVGIFx>9QKx=qqSURa0{VJq`O!!GMz-x5_q;ZLY)E~1z>pD$duwx(eJgS? zY~9JV&nmI$VW>>BRfcLEsVXhle5@*sqk-o8YBEN}=dFhh?UNPIzIetTQrlEpyJ>BD z%1F1QI=AA(+@g}W`jX=b0f_lva(d>Fo1@x#jMR5`&1|*LSNFP=yM3Qqx^}cW;BmO6 zD$SE1pJD5Z<6TYo^uE>-x3lNviGhz}=;o(gqeTB3;Ps+Zv z@kYKdwU&?tblF>;UwFP+(_PXOT6J&krn#E-J;9XZ{(6tGQT?%tZl8H+?#J&6NwUYV zp5naRFnraidg0tS|K9kzoJxu2`2ZbW@U}CL+p%YsRX)SZ`%Ev%gH;5&U8xJB$s!>u zy55s0{TpCNXM5Hjd+X4r*Ltn_{Bd5m$h;FYv*f&YN?H2HrdD*-s4XpPY0t~o_tM!d z>M9m1>!0%MyQ)OFI(WTvu}}h32W0bj>_bL26%T0>1j(k8Xa^;rn1W{ z;$KY4TtP!7+OKRK-Jhah&~d1gT4dOSd|7OJt{{cT*`Osr%|M4Adi+rBhI}nq-n7fs sqM7^nA>u~EEuG_?e94~wZu_BAN6j5M9=xHv`-ilNfd#r$&m-o)0CO%PU;qFB literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 2.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus 2.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa280de97b671b87eb47defe8bd5322ff698c82 GIT binary patch literal 5869 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 literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus.png b/DansMaRue/Assets.xcassets/Anomalie/favorite_plus.imageset/favorite_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa280de97b671b87eb47defe8bd5322ff698c82 GIT binary patch literal 5869 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 literal 0 HcmV?d00001 diff --git a/DansMaRue/Assets.xcassets/Anomalie/follow.imageset/follow.png b/DansMaRue/Assets.xcassets/Anomalie/follow.imageset/follow.png index 4469a1d8d16478841c9f52d2dc631ebddca148cd..29e03f1112a1dd16a9875927b6b249f5db6edd45 100644 GIT binary patch literal 3267 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 ZZcEJVITPmtBht?dXNRr!6?Oqf{t4{HWWWFb delta 2784 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 E4=|HYuK)l5 literal 10665 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 delta 711 zcmV;&0yzDI4c`TjB!2;OQb$4nuFf3k0007+NklfhN z+;}m1^x)Nts6kXLViOU(d9xQSwDwXc3ZA@r5h|LZLHxm3MNgjUK@b$;DVq{25hIv1 z#$e(;-^^yiW;dH=X&neV@6CJP_h#OEGhxtWWYt8NgMI=Ti+=&KePp_`Y5oluer5n^ z9a%b}C70b`$i_LohfWSu0$Zj5VFDQ2?6k0G5Py^1t*w<`H+B@Cty)&()Ip%_Nt^-( zN&5BMjl0SSRU%5Lx#Ux;9XAC8U|Ds1hPM9LGq?Z|0gy#{@qancSE~*ufh~3rbgZnQ=mlIt z5VmX0i!vU@c(A$D=JV-%$9KXMRiOPP+P=;lKw^Gel{$7B^Lj9qm#q=oA1 z_ujzCr?2t%iolcvquKi(>5{!D6dN%(;)UJ$2cHC1s}I!N!aT{d>nFgeu(oi|#F05L z1@ZlFPo_yC-&E8O&~-aciPBBI>sQzy-Uafpzd*;r$FTz|P=4g>aNXAzqY4&(TcXHd tNID^Wr8dowZc(Y%)1zNfD^@@2{2x256VdZ4pzHtu002ovPDHLkV1hjaNZkMc diff --git a/DansMaRue/Assets.xcassets/Anomalie/icon_check_pink.imageset/icon_check_pink@2x.png b/DansMaRue/Assets.xcassets/Anomalie/icon_check_pink.imageset/icon_check_pink@2x.png index 986008aac76f020a6766af29490f0072a0982287..ed92748f9548edfe980e849dbe91195fde0ac3d4 100644 GIT binary patch literal 1982 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

!r2xGj0M7L!1PQU{cg$(iD&)y*RdCC zYzZSUd#>H#wH!?d3}<2(>ujKmvns<{ttT)lINm^z7Q4re<@ep9b%a2x#WGZuC_xpHM9hTA zaRr8B$`z;#N6~4hfuStsiu<8T36^LAQI;ui1^Npp$Qz0fh(82VByMIn8v(4g*oa~p zVeQ3Y*y~GN%UBr>2nO0E-oKxr)fzd*Y_Wm^K4)zjoRO|oVk)IlB9mfM>KY7qhMgA( zJ4t8g)MDTvsl`I!Nmx-*GD?sVf<#G)oL15jl|m_(nB^#KmSYrwlHU6|hAj8w!R&qN z|Gd9}vw%7ztbdxvQ=Og!!u1v>lU)em2sZXI54VJe4yzL3(OIEharlKvUwGZOfnb zz%;F+ga)MlB`>C7JYyCd1V?L%fw%s*@UQ>4}aXnwy(XES^yD3tJCf{&~xq%9nCt1KJO52(PUi>seZMG4Em0jH@;NRt4}$4sh}pGcWr)iY|DzwmFs8NI+)7*Z;q|ILBhOY& zy3Zg_m;HTd*J#I!w&c`v=R*^qu#u9Q$bg_|e^XpE8)`hucE+UAojz-!2Cxa49*%w> zY@ao`P-Fyh5AA%g?qtG}Fp>Y>&?m=J2CCx%!bHB8lH`Z_2e!opgSGHsIX&WfNi`;&0gs@gXO!9Ke=W}IRwTRZHhae+aEMZsGk!Q>f@`{=ZscqQtQKC zrjq;ph62CL3^?tZfk$^9kYK@qJ{nvf*6CC7gpdFC^;s`A6W6}8jj_;$YmR}SyvoDa zk8A^<)E3@3XI*@B_`Tlk((|P>xjNKq&IRan`KW=sz31 zk$>CqZ|?Ki&Af!7yau@ci$Psom*X{pIs{{{CxfLx#n%7O-9jM zX^zObgTWJfUfZ-R9z@cdz3d|XQYPwT+zXn|jirYk8VnuC ztp*7(p4(bmpHtDpz1}u|@6c0+`?l|gmcOziWkLFDF_GK{Ri&qzm+T*|wH!nqyTUFw z8V9=$?rr$~;jFFMf2j}bIb|%IH0Q26Z!2P(j?v6TU(9 delta 2044 zcmV84+uOan-MilHT_mckPTNDH-YAc98z^w%KZ+$?%{ zx=lbs?1e{w;D6Jg)_KrVC0Pm;6cB&1sHrW_l7c%K%Hk|-7xXp}o#W=W1*75pY!s_f zm{2f4QMGj?AnfBV^gv-$^HvLc)rkPm*1Aa7SKew!EtNN*sM>Zv53&NLA+_q#rnq|_ z3&db+-9Y%)d^AzK7MAW54Yjp1oRfZoBQ_zz<9dL8VSmqlU3B$or>BBLa~h|QOs@&E z66?G+T+DLpP5iA_-5k_DxaqcC*MKgBHr6W?IK-dH61R<&RGy~?x8Zo>{7BD$8$DbB z((2k)=>a&%g=V;E#5b}8S5b#%wLkn#t~%vB25G_8Eu_$wWeZ1$Aj}pUcsvi2Ms3Orx>n>jsrSEQRK zn9d=iNBl9rb`uNpDX$TB87yuYp8%w(XErmSU?o7AvLKHoaD^F^57yBn&P)p<7qAu(7RfCx5M$*&LIYxGA+fm5^LQBY<~ zgW8U5o*@xZDj-cAd5j0=jL#(gK;W!}3CEN$A$jR;@|+e`H?5Gm;$2Mn-=)k3$|<(O zF?An3b^^vP4Vg)Iu|iE!AP$Hy;Zp8uDSy4R6Pz2L9EU5fyysm(0s|28?D1|;@U-`u zN>_5i;Fu=qw3IyvjlO2)l}kL~a10Q*LIK6xQBf{v0Ez>Lc`zFpgKEnMO%dV5p)eql zeL3Y3ism^c4raq1R32Td=x_{3Mi=wH2bBhn5MmGFX|=DrSn1$!-(oiLiK5x(e18^~ z5{`*NL6QxVg$Ygud8ys0uDoZ+E|d{AQ`nIh5azv8!!$^qrX}1saNLC|siJ0GskvNd zzK;*q3bUqZkh@s^9%MDhn$D_6l7Lg$;=>W8kO851koO?V3f0ThvF|~47Tlre@_o2nSUC;bTT+ikTaZQO9m&dBm>g(?_&8v@+lk;Hta#v zXZMM^&GQP$w{Q#q)Ti(!qBFeh`m9?ZcJgi^`5F#05jHXg$zXKU&R{QjxkBDlbTEQb;fH;*g7R|%kp?c|}G?AM>orbB)m!{>HctB3oN5_YPT7O5M0Z4xK ziH1goWHmokD!)`FNY5Z)i-X?4N6nQ45c?q?b&)k8y%X zE;z);Ai1J=pOec%hHQ}h2FI{HiA4gCs?~qYkfMczW7g#a$&YY&_v!kr|stTM8x7r{)EHU>hzk`<}^UYPEUf*t%4PwW}9=~B;lnjB_vw!)mFSv(mT9KSz@@s-V zp2KbBUnaiGN0+<-nT_)?qGx#1<1S`?kC|7Z_$0<~o7&$|kRKrm21sDYF(N)G!!KB( z9sGz=+!rhXo#(}_H&m^&i;c2^8YKk>~#z$Xbwb!RlG_^jeP&U&RHz{L4eHs*V4E&=a$3w@`T#=0Q#h z{yDd~W^`IWx}Qn`Mdi+*GCY+%L1j;?Gs+S4vtk;LXBy{e7}NP0v}vQU^LGyQ+%RN{ a^zT1b^!L?lIw@WN0000 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 TnA^|~00000NkvXXu0mjfZbspO diff --git a/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement.imageset/imgRemerciement@3x.png b/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement.imageset/imgRemerciement@3x.png index cbe55c0ab74a5eb6bee3503f6b72f50b85a920eb..5b89f18ae48e81a41a15156fde4aa56d68d5089a 100644 GIT binary patch literal 4936 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�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 Hu0mjf9F@Rk diff --git a/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement2.imageset/imgRemerciement2.png b/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement2.imageset/imgRemerciement2.png index cf8b2bfc6383d6dbf79d29fec8beeb614651dd9c..1d0e56e85e987e8bb7bf357fb7fd9eb1fe162a6a 100644 GIT binary patch literal 1915 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement2.imageset/imgRemerciement2@2x.png b/DansMaRue/Assets.xcassets/Anomalie/imgRemerciement2.imageset/imgRemerciement2@2x.png index 52b856909162959973d8d1c505a2582c455d227d..916a83c0d226144914c5570b94444aa9490dbf89 100644 GIT binary patch literal 2408 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 literal 6407 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 VaZuu!&lmsz002ovPDHLkV1jM+Qk?(* diff --git a/DansMaRue/Assets.xcassets/Anomalie/responsable_quartier.imageset/120.png b/DansMaRue/Assets.xcassets/Anomalie/responsable_quartier.imageset/120.png index bc80fd5b7c2799884ce990edce5a5bafddd160c9..f88afc232762b4630c83e9e85ca1037399389fd7 100644 GIT binary patch literal 9267 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 VYhR@H!yW(t002ovPDHLkV1iB!fWrU) literal 14757 zcmV;WIaEWps_3VYqviHDIIdSf? zJbL1*gq|idoC}!uqGzYp?mc)^Mos)de%pUo0(_qeZU(b#`h5m?9`53vD4~3i@jdyX zVrglCXUg>cdlE{`f6YN)Tm_-=E8xezlD9%VxpwBfRIm4?{4!^Jj08*(hJ}Vf28hJz zL-7y)KmDwmyDNn2E`5eik};EikZ@?E(ElQ+OPC^=BulIXO&MYL

#3!W`D5lZ|D_{merUADH|ID1j5*N>TiDMTtC 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 K`9s_^CAbU96;w_;F21F96(=E)0y)y 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/responsable_quartier.imageset/80.png b/DansMaRue/Assets.xcassets/Anomalie/responsable_quartier.imageset/80.png index ac56b81100bb1a6a6a6e1775c6a9471651e33c4c..c7512e09fede76e98a5dc13a7ee5b9a0043728ef 100644 GIT binary patch literal 5488 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 HxW)bl-H3jI diff --git a/DansMaRue/Assets.xcassets/Anomalie/thanks.imageset/Group 4134@2x.png b/DansMaRue/Assets.xcassets/Anomalie/thanks.imageset/Group 4134@2x.png index c037187c46da87d98a2d8867a0541cd2cc0edf31..e0795bf70a490eb945336447fbbb33f72c5a3812 100644 GIT binary patch literal 3888 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

`>3I z%FKfcwa9{9F%SMYP{;)pIhEBcepc2&dRpkp+&6~r-pQ4K@D@v7zfW7`4Lx-nkc)Uo zkG%`RBR0`jE&#RqC4AtJ0+-!q$UaI*28=7U$0pPQ`0Tv^Z-tRKP>PfHukj%I)Q+7_ zh`#~D+YQ5mvd%TC6naZm$$J6kB@s59X;^Uflnx)=YOec52tmsZC}z4{e+C7>LJ)S^ zan^`=^;fmo!!%s&c4&^>l2M6l!o{p<|Lu{YF0mYA5K?MVZnNDqtal^`mNk66)~rPh z&YM5=+A(e=ZVEDf)~`tkk^qEZ_dr~p;=@u5u0qAsHi)|CdS3p3!#$)VUSw>#thQ{L z!2!hy1K}y`(^=sd)o#n|Z{KsYo*r;l-qEcwQmw^(g;nKg7nxQapGBA+{CWI8WxoE~ zDfs`-rMWaZefbMj@3f5`HM2DBs_}Rd>syxi%*VQ{SLTl?;IpA&=_`_>X3l=t$^HWdtRDF&?U=mHiBljhNHp7{iSnI+# zS`dh)eHedK#MVM^(ET;i&3qS`k!;9`Yg@3)mv|UfVL7COtj^E3-+J-Yc30?FXll|M zh%TYP8E+w98#oaJ57+_t7ZF_4*C_{7Je-82b7!M`vpUafknI*(ihmZ{Nq-d3J}tGF zOgv7=S5e_x-MO%6a1wMSD=Bk$W|5mATHR0_kcT)_yMB=lIEnp8Lz_S6f9Vu3z(JB?5J4iQ1M_^sf^SWdVz6jm8hT zv1X}H=u=3!x2^u^d_u7^FJV@ z|F2y9nQBN@C2YehB&GPf)hi2=C2Pe7^q^c~T= z{I8?#QfWUFHL=Q$#%&Jm{*B$-EIZowN~HM-{rgyf&R`&2+HMnbxh{S@7gLp5{;*T` z`sS;TiTYa>c0^~0CVq>eV>VO#I3n@SSlx%mhlW1}3xAo771wcny~{Vx?=dPnC<9Kv zHC&rAMYzu5{;_?1`Cw2EVJCEW=QqJB+!5HtDYOsq?doVQr}xaWiem^>JjE1*bfmuC z4vq-aZyH%O8rFO+Hh~0q5ixNlF#DBc5FT4CLplNZ-!}LxE9vG3grXW-ohDvv0#IWW zoLqGT$#h!yE-IDHJN*hVCarXdVg_j6I35|a8TXae{B7CzlK-o6dP!XR?RW1|fp?Cj zjdrup2u^>|0^14MX&Z+xvz3lKv%@5f`Z7SayzS48(VRHb^}gHFtLHQA_lJ`bR57c5 mTF(z+ru0K^C9&cDp1|BaHuL0cv?B014}>zYF>bivjsGu6gOV8l diff --git a/DansMaRue/Assets.xcassets/Anomalie/thanks.imageset/Group 4134@3x.png b/DansMaRue/Assets.xcassets/Anomalie/thanks.imageset/Group 4134@3x.png index 510bd3f10cca5f2951a5264f6d6fe8a55b1aae73..a0098ce7e296328e265ae2ab1f448d238be78d83 100644 GIT binary patch literal 5271 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 literal 11786 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 diff --git a/DansMaRue/Assets.xcassets/Anomalie/unfollow.imageset/unfollow.png b/DansMaRue/Assets.xcassets/Anomalie/unfollow.imageset/unfollow.png index 6e3d2d4e5328613fb5fa5a29f75fde102fcf8678..202f1134019fcd7f0244a6d9bbfdd6eb998ebb42 100644 GIT binary patch literal 3367 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

jr{ikqEDL(uns|5rDZBdC`WF9dn#(B7N^V~@ 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 Bwo(89 diff --git a/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/Contents.json b/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/Contents.json index 12fae25..ad2e964 100644 --- a/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/Contents.json +++ b/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { - "idiom" : "universal", "filename" : "add_anomalie.png", + "idiom" : "universal", "scale" : "1x" }, { + "filename" : "add_anomalie 1.png", "idiom" : "universal", - "filename" : "add_anomalie@2x.png", "scale" : "2x" }, { + "filename" : "add_anomalie 2.png", "idiom" : "universal", - "filename" : "add_anomalie@3x.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie 1.png b/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie 1.png new file mode 100644 index 0000000000000000000000000000000000000000..24354c00ea8dacda8cd48bb412dacff20f471f22 GIT binary patch literal 2708 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 diff --git a/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@2x.png b/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@2x.png deleted file mode 100644 index 86263a6d03699744a917ca5d84c5ddbc8bf74821..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835 zcmV-J1HAl+P)Px%{YgYYRA>e5SwVKwFc6*TV^42@-3w4oKzf2^NwXo`fSiEd0D6M5N?C>zke+~Y zf!f?4Y+&bMMv3|oBRex=l})4f@@$Pp|Ie33vg`mH%3%ba_6T53`BgDFg(Elzq7eWd z1Hx&*C%6Nk6(BC5fv;sf=X#!!ML<M;=gmcOet+7O_w$|EtxhJ?%M~yg8{n+huhnRp0ZyH% zd%?DdlU3dfct;y2+d7OU0f6+O11Fyu;iCmiEnvOtU||7w`63p9OW9d1fS<6Ysa5i) z-3(lM0pWO%=F4t1a2a+Xe4Yxlz1yUG#Mb4P-ks81DQx`=#DiH1CAZDMr5BJp$V_-@ z0R;3b6X#wSBKW!2Y2%};L^A-FFmH6pyQlJ)t^!|SA)7kxgd zHv0@P3Q{vofeo>FFr~QzcHsltcxdWm6(C@nU1=79yRS4Gvg@>QT}O#F z$acLdCksBHoNwD6?uq|is^;T|dWM@UDYO#@{hsnu`p1qkklTdQ?YFmZ z&$e87zdZ)o1qit0I5seiL~@@q?<4M`U*-RV@Snn?GaU#HBk*)b;4d)<0HkfCjClY6 N002ovPDHLkV1j8-gBbt- diff --git a/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@3x.png b/DansMaRue/Assets.xcassets/Map/add_anomalie.imageset/add_anomalie@3x.png deleted file mode 100644 index 15013f66abd20c2bb33e2963d62160c917149a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1178 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 P+y>NiB{eb%LvZ_IR>`f3y+byJY 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 literal 10622 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 delta 676 zcmV;V0$csH47>%9B!2;OQb$4nuFf3k0007ZNkl7ax^uvjmW|3DqRso3g!cQ}!gO?heQIIcs+*ROsxk>6IRN9lK0OhaGiJoy8_7NX0lFh8kr_F~3z4r{_)2#FB{tbLONGo?7{=K=spy3# zLvUY87N@-fz^r9=aCAzJF{^FGe`!kGpHHV6MD8C!lWjq>^PwTWCsk|fJu-#HZ1s6t z-{k=3@sFfJHk~v1dbi?nWm{cNP=N1A^$k3P=bnW#*9@Xy)Hd*)+LJ=TUEck`7tMf~ z=9>YWhJS%(;$+N|(rn7}T1#ZT>7!Y!)}}J(BcN?^6OxX0Y7HXWtYgj~v^a_v5<;Gk zqVwo6pJfrpXu%rsz)5wo2`S?6i@}|2a5Mo9wX5YGfl`N_uY_1e4Z?&>NVcem}7j@xkw(+RO_zuT4FwYBYb zKQU%31Q4<%LFIp!J|Zl3DCp|0xWWL>61?X49X6x<@V;z_hL!9W8sVDM-n7L|1-!D4B<3>=%&1cs&|Mk9-rn-0-h zE`WLz4WLddjSHm1iI%vnByD%N-H)S`jdT$<7eU%_l4cw>hN95Sg+Xtc7-oXrii|J# z#bOb|P#J=V$K%$x-KuB}1nG9W2^&RF6b=!%o|Fwffy?^hNef<}3!0=Fk|Lv~MZR8X zGFS{oIzb_(X3ffaMkXj2k>FK=wAxHcX`m=%acYwmO*ubMG950*F@9M$cv%2`FN;Bitdb-$E`f4} zDZz=?yTStQu+=+omxB^<2kmmxq8N5kCH0g0y^7Fe=E3BC@qg}Ltx2#B`RIMyF{{%| zAf`&v;n5~D^|J=NkQqfKG@UaHFPOE!VuG0jK*TcB(tRs1VKyEC(C9zp#RN=O!bY6e zKzRdH>%S!*0T~cx=}wR#{>n4GHl@rb;jnj@!)()nAKA7CWZ3I8*wh{yobe)pOxy1F zmWLu6$GZ4q@1t`z^f=y>pR&%fcOH zl`TugQu{uOSc}nU z7snR7GtWtU(pg$oH?nH^x%`6N>;3b)*_QB~@_CL>>-d%2!A*SA( z@q&QP1uFZ#D)J#sb1eg7@12-oNsTmYIMcFf;ATP2x|8yujn8%*0iC{U9jhHZWLI#| zj@5ivb7k(Os@|)qAC@rFf7)LuU#{2;l3nXgbZ_lF#=f<%=ltnQM+&;eFJHLeJ6PP= zKWg!v-ZnbGTs>|Dw>>+S6o04gINCn+LH9$;l$rZZwY&B}J#S1q@Q34-bw9T}Vg6D1 MebwH>E1z%u6Eab9UH||9 delta 1243 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 delta 1864 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 F+@BOo6MX;x delta 738 zcmV<80v-Lu4EhC-B!2;OQb$4nuFf3k0008CNkl{Vt@4W*WqeK|IT25Y>6C=FM6w~~nhcdiefRAceFs6S{D0#d05=xokVZX($YoLA ze;(Vq7qrSh`S^pFeEYpB=T|C~y zUooJ{+uO))s()69^l7lEi+UphZGvWKDiQiwJl-vOve8rz!$M`m)Cpq3>J1hmP|0K& zta%bX82H66hJ>b0YOs6?*&bB$kcnS}iybYe9sLZ7Ik$P2#c;-c~JDgGPHYAaG!~@@f^2h6#AV`52_3{eCZa_ z|Is{5i+`wIp+&eDfudWi&>w$<8zOpNZ8c^c?F%Q+XY~fW2-Nb?lm}*R4suQADH);Y zmuoVnfa9*AB187 Ugc?=&I{*Lx07*qoM6N<$g0^sAg8%>k diff --git a/DansMaRue/Assets.xcassets/Menu/profil_menu_selected.imageset/profil_menu_selected@2x.png b/DansMaRue/Assets.xcassets/Menu/profil_menu_selected.imageset/profil_menu_selected@2x.png index 6b465665a3bdf6dd9bfbf5ca4eb393f758f2ae7a..d7c631ba0dfd7f6c388b9f68cbe0ccd502857d16 100644 GIT binary patch literal 1823 zcmbVNYitx%6kb{Zp{=E*3PDT8!DxBR&SM|lnRabpAKk?+EZgN}Lc;9K-QBS}4`*h( z+aj^hKxjjNAZSEdAW{`9sDTCnNllO@Aj;Dy#?+vwjR=HiB>uoV+wG2{t zh)Y%hVl&$@j}?EmoSIflvhLzzGLVS}@YiSP6!rknDv*YbqaQLT+!?7W{Ie zaa~s!98aZE=9I-Ot1+CUX&NUeoT4y@z_cbwXVaLZO&MZv1C3J!MHgfVF&Np1oYb8t z^mI^ysN`rREgL2nFg(pFIB6yfNf{u|<#0+;O=OJo91apd1d^^nESbY9aaotOxcoQN zx#jx|z|aN)IU5i3MHF);G~Lq(Wn=^LK(rQaQUD$Tnw(TQ;AwwH>(3DN)eO=BW#$343k#IMlz&*Izcf6F$fLF zybx^~4z**XB@9p5LNFwK9~9;d&+6>If_aXK%Bsjh)q=>z0Io_Xk^t~%c3ODo2n^Du;sBEVLtYHRG&!oLSQX5S z!D#)rWTJR&+_J9O?oeG=U?}%SEnMQQ^-7~}S z6<6MxryfNL-8;_Ryj9^$tUp_D29L&PB*t7>S@+4S*FW-Eo`ul;`Jlls>j;{-MhsOH1*A?*2=o?v%a# zcv1SBpDPAV%Thg<(^(ghk?GM3|q1g6$FSdD>Y@2egt~Kf{zP0X5cj@x=PtAJ| zZJo7mcHR-~+Xdwl(b}@zqxDM9sE)I>2kQA?^Rf$}!qXd;J6>{MeC=3X(S)zolyudF z+`;z#N%~Kn#nZ&%!jo%OPxJkDzUNc(g)IcE|gB`|j>)nLjXL m_qaVRq4PIx;%#3Tp{}8dJ*&xQOWTZJnBOzUy?55qm45>A#dYfd delta 1447 zcmV;Y1z7r@4!H}EB!2;OQb$4nuFf3k000GZNklxp zfa3@R-Ar8sM?fe7Q4x@eK=4Jd7E-o4Il#3J-tnn_IpOZhh?g3 z`@K{zH2{ZlXn{iu{Fhq*AIiB^^;-piCt%yKdY z>!X1bC#6Bz=6~wNsf?iHlGLL*RB^-ru}-$MD?oI`I!<;;N&ktKKzFZFhH^B}7Ua|K z+_J|>P6;!?H~uWf>reAt*>(vsHS6SQ055mzg8zw^Bf9-H7HmAw@hO8$>`Cc4!Wjd+ z4msWiw$V9{(X$ZmeLXG4e|>&+|J?sC3aHM>cE)ejUo1;sO zvl9fW(NzLZN@;+jtfF85 z+f&HCwtrE4kNWeW7bp*#^PgO?f0||)%NpX#+TVJ7b^^`m&5+}dQt#Z=faNI7;KHv1*}Eolnlbz(jlbpIMC_9s*{zJ;TRC#97ElpD zq|2>f)-PXHagJn`EGwD0fk~@A-Hpr0i}sk$P&WNhS37?gXkAQ$&BH z+OYVO&XnfHl20YD&wZC_PN1rv=seYq#h=&%Hp)^ePSSr7oDhXHRd6 zNH0*;M|U#<`nykeak``zGm84izWPMHK;`g@4NQFse04l3mcDwcj^I3}Va3PLL4Vx~ zR2*ruRP@1e%0p0RIiu!baaPGZ#YXO|=qR2Kn_K zq`VUm4^r$#_vo&Z`zModJZl`#=$bc8vVpeF=)C2u_-+cG_sI>;IUpskiS6lS^peNfbbkv_?C^Bq zSa8R0xBLsQ$f*OHFHP!55_S);I=EySM`Dh~TIk1iFRIZL=$VoY_ZsmHV)q>;1xV*x znN~;M_n;o_npD7?pX8-rW-SF>=ALIcPwG*NPa!1$Zp{=0x>YK_3K|T*M#CEbzBM4g zstaC0$z`0rm@HQ~4X?pp)*x^whZZ=r!2hlV{sG7Z` B$lm|} diff --git a/DansMaRue/Assets.xcassets/Menu/profil_menu_selected.imageset/profil_menu_selected@3x.png b/DansMaRue/Assets.xcassets/Menu/profil_menu_selected.imageset/profil_menu_selected@3x.png index 7592398e70a460e4e868abe5e71ab0363b7af780..8d1767edd44d320dd4c3c73e2ef20202c8fa3608 100644 GIT binary patch literal 2076 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 + + diff --git a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/Contents.json b/DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/Contents.json similarity index 58% rename from DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/Contents.json rename to DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/Contents.json index c4b809e..04d11b9 100644 --- a/DansMaRue/Assets.xcassets/Anomalie/Type/icon_10000.imageset/Contents.json +++ b/DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/Contents.json @@ -1,23 +1,21 @@ { "images" : [ { + "filename" : "dot_white_large.svg", "idiom" : "universal", - "filename" : "icon_10000.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon_10000@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon_10000@3x.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/dot_white_large.svg b/DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/dot_white_large.svg new file mode 100644 index 0000000..0d3790a --- /dev/null +++ b/DansMaRue/Assets.xcassets/Slider/dot_white_large.imageset/dot_white_large.svg @@ -0,0 +1,3 @@ + + + diff --git a/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/Contents.json b/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/Contents.json new file mode 100644 index 0000000..b1620f8 --- /dev/null +++ b/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "dot_white_small.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/dot_white_small.svg b/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/dot_white_small.svg new file mode 100644 index 0000000..b482d40 --- /dev/null +++ b/DansMaRue/Assets.xcassets/Slider/dot_white_small.imageset/dot_white_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/DansMaRue/Constants/Constants.swift b/DansMaRue/Constants/Constants.swift index b64f720..c5360de 100644 --- a/DansMaRue/Constants/Constants.swift +++ b/DansMaRue/Constants/Constants.swift @@ -8,7 +8,7 @@ import UIKit -func env(dev development: T, stg staging: T, prod production:T) -> T { +func env(dev development: T, stg staging: T, prod production: T) -> T { var v: T! #if ENVIRONMENT_DEBUG @@ -22,33 +22,37 @@ func env(dev development: T, stg staging: T, prod production:T) -> T { return v } -struct Constants { - - +enum Constants { static let fontDmr = "Montserrat" static let prefix75 = "75" - struct Services { - + enum Services { static let langPays = "FR/fr" - static let emailServiceFait = ["@paris.fr", "@derichebourg.com"] + static let emailServiceFait = [""] static let apiBaseUrl = env(dev: "", stg: "", prod: "") - static let apiUrl = "" - + static let apiUrl = "signalement/api" + + static let tokenWS = env(dev: "", + stg: "", + prod: "") + + static let headerKeyTokenWS = "" + static let apiBaseUrlEquipement = env(dev: "", - stg: "", - prod: "") + stg: "", + prod: "") + / static let authBaseUrl = env(dev: "", stg: "", prod: "") static let solenUrl = env(dev: "", - stg: "", - prod: "") + stg: "", + prod: "") static let authorization = env( dev: "", @@ -64,17 +68,28 @@ struct Constants { static let urlDisplayProfile = "" static let urlDeleteAccount = "" - + static let urlCGU = "" + static let urlConfidentialité = "" + } + + enum Authentification { + static let clientID = "" + static let authorizationEndpoint = URL(string: "")! + static let tokenEndpoint = URL(string: "")! + static let userInfoEndpoint = URL(string: "")! + static let logoutEndpoint = "" + static let RedirectURI = "" + static let userDefault = "" } - struct Maps { + enum Maps { static let parisLatitude = 48.856614 static let parisLongitude = 2.3522219 static let zoomLevel: Float = 12.0 - static let zoomLevel_50m : Float = 17.0 + static let zoomLevel_50m: Float = 17.0 } - struct Key { + enum Key { static let categorieVersion = "categorieVersion" static let categorieList = "categorieList" static let categorieItems = "categorieItems" @@ -102,6 +117,7 @@ struct Constants { static let email = "email" static let password = "password" static let isAgent = "isAgent" + static let uid = "uid" static let hasAlreadyBeenConnected = "hasAlreadyBeenConnected" // Constant equipement @@ -109,10 +125,9 @@ struct Constants { static let typeEquipementList = "typeEquipementList" static let separatorAdresseCoordonate = "***" - } - struct NoticationKey { + enum NoticationKey { static let typeAnomalieChanged = "TypeAnomalieChanged" static let photo1Changed = "Photo1Changed" static let photo2Changed = "Photo2Changed" @@ -124,7 +139,7 @@ struct Constants { static let pushNotification = "pushNotification" } - struct Image { + enum Image { static let noImage = "no_image" static let createAnomalie = "create_anomalie" static let searchAnomalie = "search_anomalie" @@ -174,8 +189,7 @@ struct Constants { static let iconEspacePublic = "TypeEspacePublic" } - struct AlertBoxTitle { - + enum AlertBoxTitle { static let adresseInvalide = "Adresse invalide" static let searchAnomaly = "Rechercher une anomalie" static let locationDisabled = "Localisation désactivée" @@ -197,10 +211,10 @@ struct Constants { static let complementAdresseFacultatif = "Complément d'adresse (facultatif)" static let complementAdresse = "Complément d'adresse" static let information = "Information" + static let messageServiceFait = "Message service fait" } - struct AlertBoxMessage { - + enum AlertBoxMessage { static let adresseInvalide = "Vous êtes actuellement géolocalisé en dehors de Paris. L’application DansMaRue permet de signaler des anomalies uniquement dans Paris." static let locationDisabled = "Pour utiliser le suivi, veuillez activer le GPS dans Paramètres > \nConfidentialité > Services de localisation." static let followMalfunction = "Vous suivez maintenant cette anomalie." @@ -213,7 +227,7 @@ struct Constants { static let noConnexion = "Vous n'avez aucune connexion." static let anomalieResolue = "Vous signalez cette anomalie comme résolue." static let erreurChargementTypes = "Suite à un problème réseau, les données nécessaires à la création de votre demande n'ont pas pu être récupérées.\n Merci de recommencer ultérieurement" - static let errorSaveLabel = "\nL\'application DansMaRue est actuellement en maintenance.\n\nMerci d\'essayer ultérieurement.\n\n Un brouillon a été sauvegardé dans votre espace" + static let errorSaveLabel = "\nNous rencontrons des difficultés à enregistrer cette anomalie. \n\nMerci d\'essayer ultérieurement.\n\n Un brouillon a été sauvegardé dans votre espace" static let grantPhoto = "Veuillez accorder l'autorisation d'utiliser la caméra pour pouvoir prendre des photos." static let solvedMalfunction = "Vous déclarez ce signalement comme résolu." static let optinAutorisation = "Autorisez vous l'application à transmettre vos données de localisation et votre identifiant de téléphone" @@ -226,29 +240,16 @@ struct Constants { static let searchAnomaly = "Renseigner ci-dessous le numéro exact de l’anomalie" } - struct LabelMessage { - + enum LabelMessage { static let addAnomaly = "Ajouter une autre anomalie" - static let searchAnomaly = "Rechercher" - static let showAnomaly = "Voir les anomalies signalées" + static let searchAnomaly = "Rechercher une anomalie par numéro d'identification" + static let showAnomaly = "Voir les anomalies déjà signalées" static let preciserPosition = "Préciser la position de l'anomalie" static let otherAnomalieLabel = "Autres anomalies autour de moi" static let otherAnomalieEquipementLabel = "Autres anomalies dans l'équipement" static let noDraft = "Vous n'avez pas de brouillon" static let noNotSolved = "Vous n'avez pas encore signalé d'anomalie" static let noSolved = "Vous n'avez pas encore d'anomalie résolue" - static let cgu = "Conditions générales d'utilisation" - static let cguText1 = "L’application DansMaRue Paris fonctionne uniquement à Paris. Elle utilise certaines fonctionnalités de votre smartphone (GPS et connexion 3G/4G/5G) qui nécessitent une bonne connexion." - static let cguText2 = "Afin d’assurer la remontée rapide, précise, fiable d’une anomalie sur l’espace public et faciliter sa prise en charge par les services municipaux et leurs partenaires, il est demandé à l’utilisateur de :" - static let cguText3="

" - static let cguText4="Le dispositif DansMaRue a pour objectif de faciliter la communication entre les Parisien-nes, la Ville de Paris et ses partenaires et prestataires." - static let cguText5="Les informations transmises par les utilisateurs via le dispositif doivent être considérées comme des documents de travail qui aideront la Ville de Paris et ses partenaires et prestataires à organiser leur activité. Ils déterminent au cas par cas les actions à mettre en place." - static let cguText6="La Ville de Paris et ses partenaires et prestataires s’engagent, dans un délai d’un mois, à prendre les mesures appropriées et à informer tout contributeur qui aura laissé ses coordonnées." - static let cguText7="Pour des raisons de confidentialité et de respect des données personnelles, les photos incluses dans les déclarations d’anomalies comportant une personne identifiable seront supprimées. Les utilisateurs sont donc invités à centrer leurs photos sur les anomalies constatées tout en apportant des précisions utiles dans la zone description. Tout manquement à ces règles d’usage peut empêcher le traitement d’une anomalie ou provoquer son rejet." - static let cguText8="Les informations de la zone « Description » susceptibles de porter atteinte à des personnes physiques ou morales seront supprimées." - static let cguText9="Si une anomalie comporte une photo d’une personne identifiable, celle-ci fera l’objet d’une suppression. Dans ce cas, si la description de l’anomalie n’est pas assez précise, il se peut qu’elle ne puisse pas être traitée. Les utilisateurs sont donc invités à centrer leur photo sur l’anomalie constatée en évitant d’y inclure des personnes." - static let cguText10="Pour toute question ou remarque, vous pouvez écrire à dansmarue_app@paris.fr" - static let cguText11="Les informations ne sont pas traitées de manière instantanée. Les situations présentant un caractère dangereux et nécessitant la mise en œuvre de mesures de protection rapides doivent continuer à faire l’objet d’une déclaration auprès des services d’urgence." static let about = "À Propos" static let aboutText = "L'application DansMaRue PARIS est un service de la Ville de Paris qui fonctionne uniquement à Paris. Elle utilise certaines fonctionnalités de votre smartphone (GPS et connexion 3G/4G/5G) qui nécessitent une bonne connexion. Si vous rencontrez des difficultés techniques liées à l'usage de l'application, n'hésitez pas à nous en informer via l'adresse mail DansMaRue_App@paris.fr\n\nLes informations ne sont pas traitées de manière instantanée. Les situations présentant un caractère dangereux et nécessitant la mise en oeuvre de mesures de protection rapides doivent continuer à faire l'objet d'une déclaration auprès des services d'urgence." static let monProfil = "Mon profil" @@ -257,12 +258,15 @@ struct Constants { static let voirProfile = "Voir mon profil complet" static let suppressionCompteMonParis = "Supprimer mon compte \"MonParis\"" static let mesAnomalies = "Mes anomalies" - static let type = "Type" + static let type = "Type (obligatoire)" + static let typeBackButton = "Type" static let select = "Sélectionner" static let photo = "Photo (obligatoire)" static let ajouter = "Ajouter" + static let optionnelDetailsTitle = "Précisions facultatives" + static let requiredDetailsTitle = "Informations obligatoires" static let description = "Description" - static let saisirDetail = "Saisir plus de détails" + static let saisirDetail = "Saisir plus de détails en 250 caractères maximum" static let priority = "Priorité" static let anomalieSolved = "Cette anomalie a été clôturée" static let anomalieInProgress = "Cette anomalie est en cours de résolution" @@ -292,11 +296,15 @@ struct Constants { static let reduceBottomSheet = "Réduire la liste des anomalies" // Favoris - static let addAdresseFavorite = "Ajout une adresses aux favoris" - static let removeAdresseFavorite = "Supprimer une adresses aux favoris" + static let addAdresseFavorite = "Ajouter %@ aux favoris" + static let removeAdresseFavorite = "Supprimer %@ des favoris" + + // Favoris Type + static let addTypeFavorite = "Ajouter %@ aux types favoris" + static let removeTypeFavorite = "Supprimer %@ des types favoris" } - struct TitleButton { + enum TitleButton { static let deconnecter = "Se déconnecter" static let connecter = "Se connecter" static let publier = "Publier" @@ -305,21 +313,21 @@ struct Constants { static let choisirAlbum = "Choisir dans l'album" static let feliciter = "Féliciter" static let monCompte = "Mon Paris" - static let declarerCommeResolue = "Déclarer comme résolue" + static let declarerCommeResolue = "Déclarer résolue" static let allow = "Autoriser" static let refuse = "Refuser" + static let close = "Fermer" } - struct PlaceHolder { + enum PlaceHolder { static let saisirAdresse = "Où est située l'anomalie ?" static let password = "Mot de passe" static let mail = "Mail" static let email = "Email" - + static let searchType = "Chercher un type" } - struct StoryBoard { - + enum StoryBoard { static let compteParisien = "CompteParisien" static let addAnomaly = "AddAnomaly" static let detailAnomaly = "AnomalyDetail" @@ -337,8 +345,7 @@ struct Constants { static let messageTypeAno = "MessageTypeAno" } - struct ViewControllerIdentifier { - + enum ViewControllerIdentifier { static let compteParisien = "CompteParisien" static let addAnomaly = "AddAnomalyViewController" static let detailAnomaly = "AnomalyDetailViewController" @@ -347,7 +354,6 @@ struct Constants { static let profileAbout = "ProfileAboutViewController" static let profileActualites = "ProfileActualitesViewController" static let profileAide = "ProfileAidesViewController" - static let profileCgu = "ProfileCGUViewController" static let profileDetail = "ProfileDetailViewController" static let profile = "ProfileViewController" static let modifyAddress = "modifyAddress" @@ -357,22 +363,34 @@ struct Constants { static let welcome = "WelcomeSliderViewController" static let typeContribution = "TypeContributionViewController" static let messageTypeAno = "MessageTypeAnoViewController" - } - struct TabBarTitle { + + enum TabBarTitle { static let carte = "Carte" static let monEspace = "Mon espace" - } - struct ProfilTableView { + enum ProfilTableView { static let profil = 0 static let anomalies = 1 static let actualites = 2 static let aides = 3 static let preferences = 4 static let cgu = 5 - static let aPropos = 6 + static let confidentialite = 6 + static let aPropos = 7 } + enum AccessibilityHint { + static let searchBarHint = "Saisissez l'adresse de l'anomalie, champ à autocomplétion activable à partir de la saisie du premier caractère" + static let searchBarTypeHint = "Saisissez le type de l'anomalie, champ à autocomplétion activable à partir de la saisie de trois caractères" + } + + enum AccessibilityLabel { + static let favoriteAdressButton = "Vos adresses favorites" + static let favoriteTypesButton = "Vos types favorites" + static let backButton = "Retour" + static let editAddress = "Modifier l'adresse" + static let typeTitle = "Type" + } } diff --git a/DansMaRue/Controllers/Anomalie/AddAnomalyViewController.swift b/DansMaRue/Controllers/Anomalie/AddAnomalyViewController.swift index bf0dcc1..097ec5c 100644 --- a/DansMaRue/Controllers/Anomalie/AddAnomalyViewController.swift +++ b/DansMaRue/Controllers/Anomalie/AddAnomalyViewController.swift @@ -1,4 +1,4 @@ - // +// // AddAnomalyViewController.swift // DansMaRue // @@ -6,25 +6,31 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit +import AppAuth +import AVFoundation import GoogleMaps import GooglePlaces import SwiftyJSON +import UIKit - -class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource { - +class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource, OIDAuthStateChangeDelegate { + func didChange(_ state: OIDAuthState) { + setAuthState(state) + } + // MARK: - Constantes - struct RowId { + + enum RowId { static let map = 0 static let typeAnomalie = 2 static let photos = 3 - static let description = 6 - static let priorite = 7 - static let btnPublier = 8 + static let description = 5 + static let priorite = 6 + static let btnPublier = 7 } - //MARK: - Properties + // MARK: - Properties + var adressModified = "" var currentAnomalie: Anomalie? @@ -36,41 +42,52 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV var complement = "" var complementTexte = "" var numAdresse = "" + private var authState: OIDAuthState? + typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + let redirectURI: String = Constants.Authentification.RedirectURI + let authStateKey: String = "authState" - var vSpinner : UIView? + var vSpinner: UIView? + var isPublicationSoumise = false + var isFirstPicture: Bool = true - //MARK: - IBoutlets + // MARK: - IBoutlets + @IBOutlet var tableViewAddAnomaly: UITableView! - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - + isPublicationSoumise = false + navigationItem.titleView?.isAccessibilityElement = true + navigationItem.leftBarButtonItem?.accessibilityLabel = Constants.TitleButton.close if #available(iOS 13.0, *) { - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.backgroundColor = UIColor.pinkButtonDmr() - appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white])! - self.navigationController?.navigationBar.standardAppearance = appearance; - self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance - } + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = UIColor.pinkButtonDmr() + appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white])! + self.navigationController?.navigationBar.standardAppearance = appearance + self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance + } tableViewAddAnomaly.delegate = self tableViewAddAnomaly.dataSource = self - + tableViewAddAnomaly.estimatedRowHeight = 116 + tableViewAddAnomaly.rowHeight = UITableView.automaticDimension if let location = MapsUtils.userLocation() { if currentAnomalie == nil { - if self.typeContribution == .outdoor { - currentAnomalie = Anomalie(address: MapsUtils.fullAddress(), latitude: location.latitude, longitude: location.longitude, categorieId: nil, descriptive: nil, priorityId: Priority.genant.rawValue, photo1: nil, photo2: nil, anomalieStatus: .Nouveau, mailUser: "", number: "" ) + if typeContribution == .outdoor { + currentAnomalie = Anomalie(address: MapsUtils.fullAddress(), latitude: location.latitude, longitude: location.longitude, categorieId: nil, descriptive: nil, priorityId: Priority.genant.rawValue, photo1: nil, photo2: nil, anomalieStatus: .Nouveau, mailUser: "", number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) if let myAddress = currentAnomalie?.address { currentAnomalie?.streetName = MapsUtils.addressLabel currentAnomalie?.postalCode = MapsUtils.getPostalCode(address: myAddress) } } else if let equipement = ContextManager.shared.equipementSelected { showAlertMessagePhoto(equipement: equipement) - self.selectedEquipement = equipement + selectedEquipement = equipement - currentAnomalie = AnomalieEquipement(address: equipement.adresse, latitude: equipement.latitude, longitude: equipement.longitude, categorieId: nil, descriptive: nil, priorityId: Priority.genant.rawValue, photo1: nil, photo2: nil, anomalieStatus: .Nouveau, mailUser: "", number: "" ) + currentAnomalie = AnomalieEquipement(address: equipement.adresse, latitude: equipement.latitude, longitude: equipement.longitude, categorieId: nil, descriptive: nil, priorityId: Priority.genant.rawValue, photo1: nil, photo2: nil, anomalieStatus: .Nouveau, mailUser: "", number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) currentAnomalie?.postalCode = MapsUtils.getPostalCode(address: equipement.adresse) @@ -80,7 +97,7 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV } } else { // Vérifie si l'anomalie contient une adresse valide - if self.typeContribution == .outdoor && (currentAnomalie?.postalCode.isEmpty)! { + if typeContribution == .outdoor && (currentAnomalie?.postalCode.isEmpty)! { // Essaye de convertir l'adresse à partir des coordonnees if Reach().connectionStatus() { let location = CLLocationCoordinate2D(latitude: CLLocationDegrees((currentAnomalie?.latitude)!), longitude: CLLocationDegrees((currentAnomalie?.longitude)!)) @@ -93,192 +110,368 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV } if let addressFound = response { self.changeAddress(newAddress: addressFound.firstResult()!) - } } } } } } - } - - //MARK: - IBActions + + // MARK: - IBActions + @IBAction func cancel(_ sender: UIBarButtonItem) { if currentAnomalie?.anomalieStatus == .Brouillon { // Message d'avertissement let alertController = UIAlertController(title: Constants.AlertBoxTitle.attention, message: Constants.AlertBoxMessage.attention, preferredStyle: .alert) // Create Non button - let NonAction = UIAlertAction(title: Constants.AlertBoxTitle.non, style: .default) { (action:UIAlertAction!) in + let NonAction = UIAlertAction(title: Constants.AlertBoxTitle.non, style: .default) { (_: UIAlertAction!) in AnomalieBrouillon.shared.remove(anomalie: self.currentAnomalie!) self.close() } alertController.addAction(NonAction) // Create Oui button - let OuiAction = UIAlertAction(title: Constants.AlertBoxTitle.oui, style: .default) { (action:UIAlertAction!) in + let OuiAction = UIAlertAction(title: Constants.AlertBoxTitle.oui, style: .default) { (_: UIAlertAction!) in self.currentAnomalie?.saveToDraft() self.close() } alertController.addAction(OuiAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) + present(alertController, animated: true, completion: nil) } else { - self.close() + close() } - + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } + override func viewDidAppear(_ animated: Bool) { + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + tableViewAddAnomaly.layoutIfNeeded() + tableViewAddAnomaly.reloadData() + if isPublicationSoumise && User.shared.isLogged { + checkAddreseAndPublish() + } + } + func close() { - _ = self.navigationController?.popViewController(animated: true) + _ = navigationController?.popViewController(animated: true) UserDefaults.standard.removeObject(forKey: "descriptiveAno") UserDefaults.standard.removeObject(forKey: "counter") } @IBAction func editAddress(_ sender: UIButton_EditAddress) { - let modifyAddress = UIStoryboard(name: Constants.StoryBoard.addAnomaly, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.modifyAddress) as! ModifyAddressViewController modifyAddress.delegate = self - self.navigationController?.pushViewController(modifyAddress, animated: true) - self.navigationController?.navigationBar.backgroundColor = UIColor.pinkDmr() - + navigationController?.pushViewController(modifyAddress, animated: true) + navigationController?.navigationBar.backgroundColor = UIColor.pinkDmr() } @IBAction func publier(_ sender: UIButton_PublierAnomalie) { + isPublicationSoumise = true + if !User.shared.isLogged { + connectToMonParis() + } else { + checkAddreseAndPublish() + } + } + + func connectToMonParis() { + let authorizationEndpoint = Constants.Authentification.authorizationEndpoint + let tokenEndpoint = Constants.Authentification.tokenEndpoint + let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) - //On vérifie que l'adresse est bien dans paris avant la publication - if(!(currentAnomalie?.postalCode.hasPrefix(Constants.prefix75))!){ - - //message alerte + doAuthWithAutoCodeExchange(configuration: configuration, + clientID: Constants.Authentification.clientID, + clientSecret: nil) + } + + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: redirectURI) else { + print("Error creating URL for : \(redirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + print("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + self.userInfo() + } else { + print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func userInfo() { + let userInfoEndpoint = Constants.Authentification.userInfoEndpoint + print("Performing userinfo request") + let currentAccessToken: String? = authState?.lastTokenResponse?.accessToken + + authState?.performAction { accessToken, _, error in + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + print("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + print("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + print("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userInfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + print("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + print("Non-HTTP response") + return + } + + guard let data = data else { + print("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + print("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + print("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json as? [String: Any], let uid = json["uid"] as? String, let validatedAccount = json["validatedAccount"] as? String { + print("Success: \(json)") + + if validatedAccount != "true" { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) + + let okAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default) { _ in + // nothing + } + + alertController.addAction(okAction) + + self.present(alertController, animated: true, completion: nil) + } else { + User.shared.uid = uid + UserDefaults.standard.set(uid, forKey: Constants.Key.uid) + RestApiManager.sharedInstance.getIdentityStore(guid: User.shared.uid!) { + (_: Bool) in + if User.shared.isLogged { + self.currentAnomalie?.mailUser = User.shared.email! + } + } + } + } + } + } + + task.resume() + } + } + + func setAuthState(_ authState: OIDAuthState?) { + if self.authState == authState { + return + } + self.authState = authState + self.authState?.stateChangeDelegate = self + var data: Data? = nil + + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + if let userDefaults = UserDefaults(suiteName: Constants.Authentification.userDefault) { + userDefaults.set(data, forKey: authStateKey) + userDefaults.synchronize() + } + } + + func checkAddreseAndPublish() { + // On vérifie que l'adresse est bien dans paris avant la publication + if !(currentAnomalie?.postalCode.hasPrefix(Constants.prefix75))! { + // message alerte let alertController = UIAlertController(title: Constants.AlertBoxTitle.adresseInvalide, message: Constants.AlertBoxMessage.adresseInvalide, preferredStyle: .alert) // Create OK button - let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in - + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in } alertController.addAction(OKAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) - } - else { - //Vérification du n° obligatoire ou non de l'adresse - //Récupération du 1er caractere de la rue pour vérifier si c'est un n° - let trimmedAddress = self.currentAnomalie?.address.trimmingCharacters(in: .whitespaces) + present(alertController, animated: true, completion: nil) + } else { + // Vérification du n° obligatoire ou non de l'adresse + // Récupération du 1er caractere de la rue pour vérifier si c'est un n° + let trimmedAddress = currentAnomalie?.address.trimmingCharacters(in: .whitespaces) let first = trimmedAddress![trimmedAddress!.startIndex] let str = String(first) - //Si l'adresse ne commence pas par un n°, affichage de la popup d'ajout de n° + // Si l'adresse ne commence pas par un n°, affichage de la popup d'ajout de n° if Int(str) == nil && !(trimmedAddress?.lowercased().starts(with: "pont"))! { showAlertNumber() } else { - //Sinon publication de l'ano + // Sinon publication de l'ano publicationAnomalie() } } } - //Affichage de la popup de numéro obligatoire + // Affichage de la popup de numéro obligatoire func showAlertNumber() { - //Affichage de la popup pour le n° de rue obligatoire - //message alerte - let alertController = UIAlertController(title: Constants.AlertBoxTitle.adresseInvalide, message: Constants.AlertBoxMessage.numRueObligatoire, preferredStyle: .alert) + // Affichage de la popup pour le n° de rue obligatoire + // message alerte + let alertController = UIAlertController(title: Constants.AlertBoxTitle.adresseInvalide, message: Constants.AlertBoxMessage.numRueObligatoire, preferredStyle: .alert) - - //Textfield pour la saisie du numéro - alertController.addTextField { [weak self] (textField) in + // Textfield pour la saisie du numéro + alertController.addTextField { [weak self] textField in textField.keyboardType = .numberPad textField.text = self?.numAdresse textField.delegate = self - } + } - // Boutton complément d'adresse var titreAlertComplement = Constants.AlertBoxTitle.complementAdresseFacultatif if complement != "" { titreAlertComplement = Constants.AlertBoxTitle.complementAdresse + " : " + complementTexte } - let ajoutComplementAction = UIAlertAction.init(title: titreAlertComplement, style: .default, handler: { (action: UIAlertAction!) in - //Affichage alert selection complément - let alert = UIAlertController(title: "Complément d'adresse", message: "\n\n\n\n\n\n\n\n\n", preferredStyle: UIAlertController.Style.alert); - alert.isModalInPopover = true; - let pickerFrame: CGRect = CGRect(x: 5, y: 70, width: 250, height: 140); - let picker: UIPickerView = UIPickerView(frame: pickerFrame); - picker.delegate = self; - picker.dataSource = self; - alert.view.addSubview(picker); + let ajoutComplementAction = UIAlertAction(title: titreAlertComplement, style: .default, handler: { (_: UIAlertAction!) in + // Affichage alert selection complément + let alert = UIAlertController(title: "Complément d'adresse", message: "\n\n\n\n\n\n\n\n\n", preferredStyle: UIAlertController.Style.alert) + alert.isModalInPopover = true + let pickerFrame: CGRect = .init(x: 5, y: 70, width: 250, height: 140) + let picker: UIPickerView = .init(frame: pickerFrame) + picker.delegate = self + picker.dataSource = self + alert.view.addSubview(picker) - let OKAction = UIAlertAction.init(title: Constants.AlertBoxTitle.ok, style: .default, handler: { (action: UIAlertAction!) in - print("ok") - self.showAlertNumber() + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default, handler: { (_: UIAlertAction!) in + print("ok") + self.showAlertNumber() }) alert.addAction(OKAction) - self.present(alert, animated: true, completion: nil); - }) - alertController.addAction(ajoutComplementAction) + self.present(alert, animated: true, completion: nil) + }) + alertController.addAction(ajoutComplementAction) - - // Boutton publier - let OKAction = UIAlertAction.init(title: Constants.AlertBoxTitle.publier, style: .default, handler: { (action: UIAlertAction!) in - let textField = alertController.textFields![0] as UITextField + // Boutton publier + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.publier, style: .default, handler: { (_: UIAlertAction!) in + let textField = alertController.textFields![0] as UITextField - //On ajoute le numéro si il est inférieur à 4 chiffres - if textField.text != "" && textField.text!.count < 4 && textField.text != "0" && textField.text != "00" && textField.text != "000" { + // On ajoute le numéro si il est inférieur à 4 chiffres + if textField.text != "", textField.text!.count < 4, textField.text != "0", textField.text != "00", textField.text != "000" { if self.complement != "" { - //Si un complément d'adresse est renseigné + // Si un complément d'adresse est renseigné self.currentAnomalie?.address = textField.text! + self.complement + " " + (self.currentAnomalie?.address)! } else { self.currentAnomalie?.address = textField.text! + " " + (self.currentAnomalie?.address)! } - //Affichage du spinner de chargement + // Affichage du spinner de chargement self.showSpinner(onView: self.view) - //MAJ des coordonnées via la nouvelle adresse + // MAJ des coordonnées via la nouvelle adresse MapsUtils.getCoordinateFromAddress(adresse: textField.text! + self.currentAnomalie!.streetName + " " + self.currentAnomalie!.postalCode) { (coordinate: CLLocationCoordinate2D) in self.currentAnomalie?.latitude = coordinate.latitude self.currentAnomalie?.longitude = coordinate.longitude - //Utilisation des nouvelles coordonnées pour récupérer le code postal (DMR-1785) - //Suite à l'ajout d'un n°, si une adresse est à cheval sur 2 arrondissements, le nouveau numéro necessite une vérification du CP + // Utilisation des nouvelles coordonnées pour récupérer le code postal (DMR-1785) + // Suite à l'ajout d'un n°, si une adresse est à cheval sur 2 arrondissements, le nouveau numéro necessite une vérification du CP MapsUtils.getAddressFromCoordinate(lat: self.currentAnomalie!.latitude, long: self.currentAnomalie!.longitude) { (address: GMSAddress) in - //MAJ du CP dans l'adresse + // MAJ du CP dans l'adresse self.currentAnomalie?.address = (self.currentAnomalie?.address.replacingOccurrences(of: self.currentAnomalie!.postalCode, with: address.postalCode!))! - //MAJ du CP + // MAJ du CP self.currentAnomalie?.postalCode = address.postalCode ?? "" - //Fin du chargement + // Fin du chargement self.removeSpinner() self.publicationAnomalie() } } - } else { - //Fin du chargement + } else { + // Fin du chargement self.removeSpinner() - self.present(alertController, animated: true, completion:nil) - } - }) - alertController.addAction(OKAction) + self.present(alertController, animated: true, completion: nil) + } + }) + alertController.addAction(OKAction) - - // Boutton annuler - let cancelAction = UIAlertAction.init(title: Constants.AlertBoxTitle.annuler, style: UIAlertAction.Style.cancel, handler:{ (action: UIAlertAction!) in + // Boutton annuler + let cancelAction = UIAlertAction(title: Constants.AlertBoxTitle.annuler, style: UIAlertAction.Style.cancel, handler: { (_: UIAlertAction!) in self.numAdresse = "" self.complement = "" - }) - alertController.addAction(cancelAction) + }) + alertController.addAction(cancelAction) - // Present Dialog message - self.present(alertController, animated: true, completion:nil) + // Present Dialog message + present(alertController, animated: true, completion: nil) } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } - func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int)-> Int { + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return choixComplement.count } @@ -293,16 +486,16 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, - replacementString string: String) -> Bool { - + replacementString string: String) -> Bool + { let isNumOk = textField.text!.count <= 2 || (textField.text!.count == 3 && string == "") if isNumOk { - //Enregistrement du n° + // Enregistrement du n° numAdresse = textField.text! + string } - //Limitation des adresse à 3 chiffres - Prise en compte de la suppression de texte (DMR-1728) + // Limitation des adresse à 3 chiffres - Prise en compte de la suppression de texte (DMR-1728) return isNumOk } @@ -310,24 +503,24 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV let mailAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.thanks, bundle: nil) let mailAnomalyVC = mailAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.mail) as! ThanksAnomalyViewController mailAnomalyVC.modalPresentationStyle = .overFullScreen - mailAnomalyVC.currentAnomaly = self.currentAnomalie + mailAnomalyVC.currentAnomaly = currentAnomalie if User.shared.isLogged { // Enregistrement de l'anomalies et des photos. - self.currentAnomalie?.mailUser = User.shared.email! + currentAnomalie?.mailUser = User.shared.email! mailAnomalyVC.status = .saveIncident } else { mailAnomalyVC.status = .showMail } - mailAnomalyVC.typeContribution = self.typeContribution + mailAnomalyVC.typeContribution = typeContribution mailAnomalyVC.closeDelegate = self - self.present(mailAnomalyVC, animated: true, completion: nil) + present(mailAnomalyVC, animated: true, completion: nil) } /// Methode permettant de récuperer le n° d'une adresse /// /// - Parameter street: l'adresse complete - func getStreetNumber(adresse : String) -> String { + func getStreetNumber(adresse: String) -> String { var number = "" var hasValue = false @@ -335,14 +528,13 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV for char in adresse { let str = String(char) // Checks if the char is a number - if (Int(str) != nil){ + if Int(str) != nil { // If it is it appends it to number - number+=str + number += str // Here we set the hasValue to true, beacause the street number will come in one order hasValue = true - } - else{ - if(hasValue){ + } else { + if hasValue { break } } @@ -350,10 +542,10 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV return number } - func showSpinner(onView : UIView) { - let spinnerView = UIView.init(frame: onView.bounds) - spinnerView.backgroundColor = UIColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5) - let ai = UIActivityIndicatorView.init(style: .whiteLarge) + func showSpinner(onView: UIView) { + let spinnerView = UIView(frame: onView.bounds) + spinnerView.backgroundColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5) + let ai = UIActivityIndicatorView(style: .whiteLarge) ai.startAnimating() ai.center = spinnerView.center @@ -372,26 +564,24 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV } } - //MARK: - Other functions - func changeTypeAnomalie(newType:TypeAnomalie) { + // MARK: - Other functions + + func changeTypeAnomalie(newType: TypeAnomalie) { currentAnomalie?.categorieId = newType.categorieId currentAnomalie?.alias = newType.alias currentAnomalie?.anomalieStatus = .Brouillon - self.currentAnomalie?.saveToDraft() + currentAnomalie?.saveToDraft() tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.typeAnomalie, section: 0)], with: .none) tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.btnPublier, section: 0)], with: .none) - } - func changeDescriptive (descriptive: String) { + func changeDescriptive(descriptive: String) { currentAnomalie?.descriptive = descriptive currentAnomalie?.anomalieStatus = .Brouillon - self.currentAnomalie?.saveToDraft() + currentAnomalie?.saveToDraft() - tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.description, section: 0)], with: .none) - } func changePhoto(newPhoto: UIImage, isFirstPhoto: Bool) { @@ -401,61 +591,45 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV currentAnomalie?.photo2 = newPhoto } currentAnomalie?.anomalieStatus = .Brouillon - self.currentAnomalie?.saveToDraft() + currentAnomalie?.saveToDraft() - tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.photos, section: 0)], with: .none) - tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.btnPublier, section: 0)], with: .none) - + tableViewAddAnomaly.reloadData() } - func changePriority(newPriority: Priority){ + func changePriority(newPriority: Priority) { currentAnomalie?.priorityId = newPriority.rawValue currentAnomalie?.anomalieStatus = .Brouillon - self.currentAnomalie?.saveToDraft() + currentAnomalie?.saveToDraft() - - self.tableViewAddAnomaly.reloadData() + tableViewAddAnomaly.reloadData() } @objc func takePhoto(_ sender: UITapGestureRecognizer) { - let popupPhoto = UIStoryboard(name: Constants.StoryBoard.popupPhoto, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.popupPhoto) as! PopupPhotoViewController - - self.addChild(popupPhoto) - self.view.addSubview(popupPhoto.view) - popupPhoto.delegate = self - popupPhoto.isFirstPhoto = true - popupPhoto.didMove(toParent: self) + isFirstPicture = true + showPhotoPicker() } @objc func takePhoto2(_ sender: UITapGestureRecognizer) { - let popupPhoto = UIStoryboard(name: Constants.StoryBoard.popupPhoto, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.popupPhoto) as! PopupPhotoViewController - - self.addChild(popupPhoto) - - self.view.addSubview(popupPhoto.view) - popupPhoto.delegate = self - popupPhoto.isFirstPhoto = false - popupPhoto.didMove(toParent: self) + isFirstPicture = false + showPhotoPicker() } - - func changeAddress(newAddress: GMSAddress) { + func changeAddress(newAddress: GMSAddress) { currentAnomalie?.address = MapsUtils.fullAddress(gmsAddress: newAddress) currentAnomalie?.latitude = newAddress.coordinate.latitude currentAnomalie?.longitude = newAddress.coordinate.longitude currentAnomalie?.streetName = newAddress.thoroughfare ?? "" currentAnomalie?.postalCode = newAddress.postalCode ?? "" - currentAnomalie?.locality = newAddress.locality ?? "" + currentAnomalie?.locality = (newAddress.locality == "Paris" ? newAddress.locality?.uppercased() : newAddress.locality) ?? "" currentAnomalie?.anomalieStatus = .Brouillon - self.currentAnomalie?.saveToDraft() + currentAnomalie?.saveToDraft() - tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.map, section: 0)], with: .none) } func changeEquipement(newEquipement: Equipement) { if let anomalieEquipement = currentAnomalie as? AnomalieEquipement { - self.selectedEquipement = newEquipement + selectedEquipement = newEquipement anomalieEquipement.equipementId = newEquipement.equipementId anomalieEquipement.address = newEquipement.adresse anomalieEquipement.postalCode = MapsUtils.getPostalCode(address: newEquipement.adresse) @@ -471,16 +645,15 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV func isValidAnomalie() -> Bool { if let anomalie = currentAnomalie as? AnomalieEquipement { return !anomalie.categorieId.isEmpty && anomalie.photo1 != nil && !anomalie.address.isEmpty - && !anomalie.equipementId.isEmpty && Reach().connectionStatus() + && !anomalie.equipementId.isEmpty } else if let anomalie = currentAnomalie { return !anomalie.categorieId.isEmpty && anomalie.photo1 != nil && !anomalie.address.isEmpty - && !anomalie.postalCode.isEmpty && Reach().connectionStatus() + && !anomalie.postalCode.isEmpty } return false } - @objc func btnDeletePhoto(sender:UIButton!) - { + @objc func btnDeletePhoto(sender: UIButton!) { if sender.tag == 100 { // Suppression de l'image 1 currentAnomalie?.photo1 = currentAnomalie?.photo2 @@ -493,7 +666,6 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.photos, section: 0)], with: .none) tableViewAddAnomaly.reloadRows(at: [IndexPath(row: RowId.btnPublier, section: 0)], with: .none) - } /// Affichage d'un message d'alerte en fonction du message paramétré pour le TypeEquipement. @@ -504,63 +676,39 @@ class AddAnomalyViewController: UIViewController, UITextFieldDelegate, UIPickerV if !typeEquipement.msgPhoto.isEmpty { let alertController = UIAlertController(title: Constants.AlertBoxTitle.attention, message: typeEquipement.msgPhoto, preferredStyle: .alert) // Create OK button - let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in - + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in } alertController.addAction(OKAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) + present(alertController, animated: true, completion: nil) } } } + + private func showPhotoPicker() { + let alert = UIAlertController(title: "Ajouter une image", + message: "", + preferredStyle: .actionSheet) + alert.addAction(UIAlertAction(title: "Prendre une photo", + style: .default, + handler: { _ in self.useCamera() })) + alert.addAction(UIAlertAction(title: "Choisir dans l'album", + style: .default, + handler: { _ in self.searchPhoto() })) + alert.addAction(UIAlertAction(title: "Annuler", + style: .cancel, + handler: { _ in alert.dismiss(animated: true) })) + present(alert, animated: true, completion: nil) + } } // MARK: - Extension UITableViewDataSource + extension AddAnomalyViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 9 - } - - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - if (indexPath.section, indexPath.row) == (0,RowId.map) { - return 210 - } else if (indexPath.section, indexPath.row) == (0,1) { - return 0 - } else if (indexPath.section, indexPath.row) == (0,RowId.photos) { - return 160 - } else if (indexPath.section, indexPath.row) == (0,4) { - return 0 - } else if (indexPath.section, indexPath.row) == (0,5) { - return 40 - } else if (indexPath.section, indexPath.row) == (0,8) { - return 95 - } else { - //return UITableViewAutomaticDimension - return 65 - } - } - - func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - if (indexPath.section, indexPath.row) == (0,RowId.map) { - return 210 - } else if (indexPath.section, indexPath.row) == (0,1) { - return 0 - } else if (indexPath.section, indexPath.row) == (0,RowId.photos) { - return 160 - } else if (indexPath.section, indexPath.row) == (0,4) { - return 0 - } else if (indexPath.section, indexPath.row) == (0,5) { - return 40 - } else if (indexPath.section, indexPath.row) == (0,8) { - return 95 - } else { - //return UITableViewAutomaticDimension - return 56 - } + return 8 } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 0 : Map + Adresse // 1 : Separator @@ -572,24 +720,36 @@ extension AddAnomalyViewController: UITableViewDataSource { // 7 : Priorite // 8 : Btn Publier - var cell:UITableViewCell + var cell: UITableViewCell switch (indexPath.section, indexPath.row) { - case (0,RowId.map): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "mapCell")! as UITableViewCell - - var addressLabel = cell.viewWithTag(2) as! UILabel - var boroughLabel = cell.viewWithTag(3) as! UILabel + case (0, RowId.map): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "mapCell")! as UITableViewCell + cell.isAccessibilityElement = false + let mapView = cell.viewWithTag(1) as! GMSMapView + mapView.isAccessibilityElement = false + let addressLabel = cell.viewWithTag(2) as! UILabel + let boroughLabel = cell.viewWithTag(3) as! UILabel + let editAddressButton = cell.viewWithTag(4) as! UIButton + editAddressButton.isAccessibilityElement = true + editAddressButton.accessibilityTraits = .button + editAddressButton.accessibilityLabel = Constants.AccessibilityLabel.editAddress + applyDynamicType(label: addressLabel, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: boroughLabel, fontName: "Montserrat-Regular", size: 14.0) + addressLabel.isAccessibilityElement = true + addressLabel.accessibilityTraits = .staticText + boroughLabel.isAccessibilityElement = true + boroughLabel.accessibilityTraits = .staticText - if self.typeContribution == .outdoor { - //affichage des coordonnées GPS dans l'encart modification de l'adresse quand on est offline - if ((currentAnomalie?.streetName.isEmpty)! && (currentAnomalie?.postalCode.isEmpty)!){ - + if typeContribution == .outdoor { + // affichage des coordonnées GPS dans l'encart modification de l'adresse quand on est offline + if (currentAnomalie?.streetName.isEmpty)! && (currentAnomalie?.postalCode.isEmpty)! { if let latitude = currentAnomalie?.latitude, let longitude = currentAnomalie?.longitude { MapsUtils.addressLabel = "lat : \(latitude), lgt : \(longitude)" addressLabel.text = "lat : \(latitude)" + addressLabel.accessibilityLabel = "latitude : \(latitude)" boroughLabel.text = "lgt : \(longitude)" - + boroughLabel.accessibilityLabel = "longitude : \(longitude)" MapsUtils.getAddressFromCoordinate(lat: latitude, long: longitude) { (address: GMSAddress) in @@ -598,25 +758,25 @@ extension AddAnomalyViewController: UITableViewDataSource { self.currentAnomalie?.streetName = address.thoroughfare ?? "" self.currentAnomalie?.locality = address.locality ?? "" } - } } else { let streetName = currentAnomalie?.streetName let postalCode = currentAnomalie?.postalCode addressLabel.text = streetName + addressLabel.accessibilityLabel = streetName boroughLabel.text = MapsUtils.boroughLabel(postalCode: postalCode!) - + boroughLabel.accessibilityLabel = MapsUtils.boroughLabel(postalCode: postalCode!) } - } else if self.typeContribution == .indoor { - addressLabel.text = self.selectedEquipement?.name - boroughLabel.text = self.selectedEquipement?.adresse + } else if typeContribution == .indoor { + addressLabel.text = selectedEquipement?.name + addressLabel.accessibilityLabel = selectedEquipement?.name + boroughLabel.text = selectedEquipement?.adresse + boroughLabel.accessibilityLabel = selectedEquipement?.adresse } - var mapView = cell.viewWithTag(1) as! GMSMapView;() - // MARK: - Private functions + func loadMapContainer(location: CLLocationCoordinate2D) { - let camera = GMSCameraPosition.camera(withLatitude: location.latitude, longitude: location.longitude, zoom: Float(Constants.Maps.zoomLevel_50m)) mapView.camera = camera @@ -624,7 +784,7 @@ extension AddAnomalyViewController: UITableViewDataSource { mapView.mapType = GMSMapViewType.terrain // Permet de décaler les donnees Google (Logo, icone, ...) - let mapInsets = UIEdgeInsets.init(top: 0.0, left: 0.0, bottom: 30.0, right: 0.0) + let mapInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 30.0, right: 0.0) mapView.padding = mapInsets mapView.isMyLocationEnabled = true @@ -636,44 +796,69 @@ extension AddAnomalyViewController: UITableViewDataSource { } loadMapContainer(location: CLLocationCoordinate2D(latitude: (currentAnomalie?.latitude)!, longitude: (currentAnomalie?.longitude)!)) - - - case (0,RowId.typeAnomalie): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_type")! as UITableViewCell - + case (0, 1): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_obligatoires")! as UITableViewCell + let titleLabel = cell.viewWithTag(1) as! UILabel + titleLabel.text = Constants.LabelMessage.requiredDetailsTitle + + titleLabel.isAccessibilityElement = true + titleLabel.accessibilityTraits = .header + titleLabel.textColor = UIColor.greyDmr() + applyDynamicType(label: titleLabel, fontName: "Montserrat-Regular", size: 16.0) + case (0, RowId.typeAnomalie): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_type")! as UITableViewCell + cell.isAccessibilityElement = false let typeLabel = cell.viewWithTag(2) as! UILabel typeLabel.text = Constants.LabelMessage.type - let typeSubtitleLabel = cell.viewWithTag(3) as! UILabel + typeLabel.isAccessibilityElement = true + typeLabel.accessibilityTraits = .staticText + typeLabel.accessibilityLabel = Constants.LabelMessage.type + let typeSubtitleLabel = cell.viewWithTag(3) as! UILabel + typeSubtitleLabel.isAccessibilityElement = true + typeSubtitleLabel.accessibilityTraits = .staticText + applyDynamicType(label: typeSubtitleLabel, fontName: "Montserrat-Regular", size: 12.0) + applyDynamicType(label: typeLabel, fontName: "Montserrat-Regular", size: 16.0) let iconCheckType = cell.viewWithTag(1) as! UIImageView + iconCheckType.isAccessibilityElement = true if let selectTypeAnomalie = getSelectedTypeAnomalie() { typeSubtitleLabel.text = selectTypeAnomalie.alias + typeSubtitleLabel.accessibilityLabel = selectTypeAnomalie.alias typeSubtitleLabel.textColor = UIColor.pinkDmr() - - let imageCheckType : UIImage = UIImage(named: Constants.Image.iconCheckPink)! + let imageCheckType = UIImage(named: Constants.Image.iconCheckPink)! iconCheckType.image = imageCheckType - + iconCheckType.accessibilityLabel = "Type d'anomalie Renseigné" } else { typeSubtitleLabel.text = Constants.LabelMessage.select typeSubtitleLabel.textColor = UIColor.greyDmr() - - let imageCheckType : UIImage = UIImage(named: Constants.Image.iconCheckGrey)! + let imageCheckType = UIImage(named: Constants.Image.iconCheckGrey)! iconCheckType.image = imageCheckType + iconCheckType.accessibilityLabel = "Type d'anomalie Non renseigné" } cell.accessoryType = .disclosureIndicator - - case (0,RowId.photos): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_photo")! as UITableViewCell - + case (0, RowId.photos): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_photo")! as UITableViewCell + cell.isAccessibilityElement = false let photoLabel = cell.viewWithTag(1) as! UILabel photoLabel.text = Constants.LabelMessage.photo + photoLabel.isAccessibilityElement = true + photoLabel.accessibilityTraits = .staticText let photoSubtitleLabel = cell.viewWithTag(2) as! UILabel photoSubtitleLabel.text = Constants.LabelMessage.ajouter + photoSubtitleLabel.isAccessibilityElement = true + photoSubtitleLabel.accessibilityTraits = .staticText + photoSubtitleLabel.textColor = UIColor.greyDmr() + applyDynamicType(label: photoSubtitleLabel, fontName: "Montserrat-Regular", size: 12.0) + applyDynamicType(label: photoLabel, fontName: "Montserrat-Regular", size: 16.0) let iconCheckPhoto = cell.viewWithTag(3) as! UIImageView - + iconCheckPhoto.isAccessibilityElement = true + let addPhoto = cell.viewWithTag(4) as! UIImageView + addPhoto.isAccessibilityElement = true + addPhoto.accessibilityTraits = .button + addPhoto.accessibilityLabel = "Ajouter une photo" let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(takePhoto(_:))) addPhoto.addGestureRecognizer(gestureRecognizer) @@ -685,27 +870,35 @@ extension AddAnomalyViewController: UITableViewDataSource { deleteBtn2.removeFromSuperview() } - if let imageAno = currentAnomalie?.photo1 { addPhoto.image = imageAno - let imageCheckPhoto : UIImage = UIImage(named: Constants.Image.iconCheckPink)! + let imageCheckPhoto = UIImage(named: Constants.Image.iconCheckPink)! iconCheckPhoto.image = imageCheckPhoto + iconCheckPhoto.accessibilityLabel = "renseigné" if cell.viewWithTag(100) == nil { // Ajout du bouton de suppression de la photo - cell.addSubview(addDeleteBtn(x: addPhoto.frame.origin.x, y: addPhoto.frame.origin.y, tag: 100)) + let deleteIcon = addDeleteBtn(x: addPhoto.frame.origin.x, y: addPhoto.frame.origin.y, tag: 100) + cell.addSubview(deleteIcon) + deleteIcon.topAnchor.constraint(equalTo: addPhoto.topAnchor, constant: -5).isActive = true + deleteIcon.leadingAnchor.constraint(equalTo: addPhoto.leadingAnchor, constant: -5).isActive = true + deleteIcon.heightAnchor.constraint(equalToConstant: 20).isActive = true + deleteIcon.widthAnchor.constraint(equalToConstant: 20).isActive = true } } else { - let imageCamera : UIImage = UIImage(named: Constants.Image.iconCamera)! + let imageCamera = UIImage(named: Constants.Image.iconCamera)! addPhoto.image = imageCamera - let imageCheckPhoto : UIImage = UIImage(named: Constants.Image.iconCheckGrey)! + let imageCheckPhoto = UIImage(named: Constants.Image.iconCheckGrey)! iconCheckPhoto.image = imageCheckPhoto + iconCheckPhoto.accessibilityLabel = "Non renseigné" } let addPhoto2 = cell.viewWithTag(5) as! UIImageView - + addPhoto2.isAccessibilityElement = true + addPhoto2.accessibilityTraits = .button + addPhoto2.accessibilityLabel = "Ajouter une deuxieme photo" if currentAnomalie?.photo1 == nil { addPhoto2.isHidden = true } else { @@ -719,58 +912,92 @@ extension AddAnomalyViewController: UITableViewDataSource { if cell.viewWithTag(200) == nil { // Ajout du bouton de suppression de la photo - cell.addSubview(addDeleteBtn(x: addPhoto2.frame.origin.x, y: addPhoto2.frame.origin.y, tag: 200)) + let deleteIcon = addDeleteBtn(x: addPhoto2.frame.origin.x, y: addPhoto2.frame.origin.y, tag: 200) + cell.addSubview(deleteIcon) + deleteIcon.topAnchor.constraint(equalTo: addPhoto2.topAnchor, constant: -5).isActive = true + deleteIcon.leadingAnchor.constraint(equalTo: addPhoto2.leadingAnchor, constant: -5).isActive = true + deleteIcon.heightAnchor.constraint(equalToConstant: 20).isActive = true + deleteIcon.widthAnchor.constraint(equalToConstant: 20).isActive = true } } else { - let imageCamera : UIImage = UIImage(named: Constants.Image.iconCamera)! + let imageCamera = UIImage(named: Constants.Image.iconCamera)! addPhoto2.image = imageCamera } - } - case (0,5): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_optionnels")! as UITableViewCell - - case (0,RowId.description): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_description")! as UITableViewCell + case (0, 4): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_optionnels")! as UITableViewCell + let titleLabel = cell.viewWithTag(1) as! UILabel + titleLabel.text = Constants.LabelMessage.optionnelDetailsTitle + + titleLabel.isAccessibilityElement = true + titleLabel.accessibilityTraits = .header + titleLabel.textColor = UIColor.greyDmr() + applyDynamicType(label: titleLabel, fontName: "Montserrat-Regular", size: 16.0) + case (0, RowId.description): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_description")! as UITableViewCell + cell.isAccessibilityElement = false cell.accessoryType = .disclosureIndicator let descritpiveLabel = cell.viewWithTag(2) as! UILabel + descritpiveLabel.isAccessibilityElement = true + descritpiveLabel.accessibilityTraits = .staticText descritpiveLabel.text = Constants.LabelMessage.description let descriptiveSubtitleLabel = cell.viewWithTag(3) as! UILabel - + descriptiveSubtitleLabel.isAccessibilityElement = true + descriptiveSubtitleLabel.accessibilityTraits = .staticText + applyDynamicType(label: descritpiveLabel, fontName: "Montserrat-Regular", size: 16.0) + applyDynamicType(label: descriptiveSubtitleLabel, fontName: "Montserrat-Regular", size: 12.0) + let iconCheckType = cell.viewWithTag(1) as! UIImageView - if (currentAnomalie?.descriptive ?? "").isEmpty{ + iconCheckType.isAccessibilityElement = true + descriptiveSubtitleLabel.isAccessibilityElement = true + if (currentAnomalie?.descriptive ?? "").isEmpty { descriptiveSubtitleLabel.text = Constants.LabelMessage.saisirDetail + descriptiveSubtitleLabel.accessibilityLabel = Constants.LabelMessage.saisirDetail descriptiveSubtitleLabel.textColor = UIColor.greyDmr() - let imageCheckType : UIImage = UIImage(named: Constants.Image.iconCheckGrey)! + let imageCheckType = UIImage(named: Constants.Image.iconCheckGrey)! iconCheckType.image = imageCheckType + iconCheckType.accessibilityLabel = "non renseigné" } else { - let imageCheckType : UIImage = UIImage(named: Constants.Image.iconCheckPink)! + let imageCheckType = UIImage(named: Constants.Image.iconCheckPink)! iconCheckType.image = imageCheckType descriptiveSubtitleLabel.text = currentAnomalie?.descriptive + descriptiveSubtitleLabel.accessibilityLabel = currentAnomalie?.descriptive descriptiveSubtitleLabel.textColor = UIColor.greyDmr() + iconCheckType.accessibilityLabel = "renseigné" } - case (0,RowId.priorite): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_priorite")! as UITableViewCell + case (0, RowId.priorite): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_priorite")! as UITableViewCell + cell.isAccessibilityElement = false cell.accessoryType = .disclosureIndicator let labelPriority = cell.viewWithTag(2) as! UILabel + labelPriority.isAccessibilityElement = true + labelPriority.accessibilityTraits = .staticText labelPriority.text = Constants.LabelMessage.priority let typeSubtitleLabel = cell.viewWithTag(3) as! UILabel - + typeSubtitleLabel.isAccessibilityElement = true + typeSubtitleLabel.accessibilityTraits = .staticText + applyDynamicType(label: labelPriority, fontName: "Montserrat-Regular", size: 16.0) + applyDynamicType(label: typeSubtitleLabel, fontName: "Montserrat-Regular", size: 12.0) let iconCheckType = cell.viewWithTag(1) as! UIImageView typeSubtitleLabel.text = Priority(rawValue: (currentAnomalie?.priorityId)!)?.description typeSubtitleLabel.textColor = UIColor.pinkDmr() - let imageCheckType : UIImage = UIImage(named: Constants.Image.iconCheckPink)! + let imageCheckType = UIImage(named: Constants.Image.iconCheckPink)! iconCheckType.image = imageCheckType + iconCheckType.isAccessibilityElement = true + iconCheckType.accessibilityLabel = "renseigné" - case (0,RowId.btnPublier): - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_btn_publier")! as UITableViewCell - + case (0, RowId.btnPublier): + cell = tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_btn_publier")! as UITableViewCell + cell.isAccessibilityElement = false let btnPublier = cell.viewWithTag(1) as! UIButton + btnPublier.isAccessibilityElement = true + btnPublier.accessibilityTraits = .button + applyDynamicType(label: btnPublier.titleLabel!, fontName: "Montserrat-Regular", size: 22.0) if isValidAnomalie() { btnPublier.isEnabled = true btnPublier.backgroundColor = UIColor.pinkButtonDmr() @@ -779,7 +1006,7 @@ extension AddAnomalyViewController: UITableViewDataSource { btnPublier.backgroundColor = UIColor.lightGreyDmr() } default: - cell = self.tableViewAddAnomaly.dequeueReusableCell(withIdentifier: "cell_separator\(indexPath.row)")! as UITableViewCell + cell = UITableViewCell() } return cell @@ -788,24 +1015,24 @@ extension AddAnomalyViewController: UITableViewDataSource { func addDeleteBtn(x: CGFloat, y: CGFloat, tag: Int) -> UIButton { let deleteImg = UIImage(named: Constants.Image.iconExit) - let deleteBtn = UIButton(frame: CGRect(x:x - 5, y:y - 5, width:20, height:20)) + let deleteBtn = UIButton(frame: CGRect(x: x - 5, y: y - 5, width: 20, height: 20)) deleteBtn.backgroundColor = .black deleteBtn.layer.cornerRadius = 0.5 * deleteBtn.bounds.size.width deleteBtn.layer.borderWidth = 0 deleteBtn.setImage(deleteImg, for: .normal) deleteBtn.tintColor = .white deleteBtn.tag = tag - deleteBtn.addTarget(self, action: #selector(self.btnDeletePhoto(sender:)), for: .touchUpInside) + deleteBtn.addTarget(self, action: #selector(btnDeletePhoto(sender:)), for: .touchUpInside) deleteBtn.accessibilityLabel = Constants.LabelMessage.deletePhoto return deleteBtn } - + func getSelectedTypeAnomalie() -> TypeAnomalie? { var selectTypeAnomalie: TypeAnomalie? if let categId = currentAnomalie?.categorieId { - if self.typeContribution == .indoor { - guard let typeEquipementId = ContextManager.shared.typeEquipementSelected?.typeEquipementId else { return nil} + if typeContribution == .indoor { + guard let typeEquipementId = ContextManager.shared.typeEquipementSelected?.typeEquipementId else { return nil } selectTypeAnomalie = ReferalManager.shared.getTypeAnomalie(forTypeEquipementId: typeEquipementId, catagorieId: categId) } else { selectTypeAnomalie = ReferalManager.shared.getTypeAnomalie(withId: categId) @@ -813,34 +1040,38 @@ extension AddAnomalyViewController: UITableViewDataSource { } return selectTypeAnomalie } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } } -//MARK: - UITableView Delegate +// MARK: - UITableView Delegate + extension AddAnomalyViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if indexPath.row == RowId.priorite { let priorityListVC = UIStoryboard(name: Constants.StoryBoard.priority, bundle: nil).instantiateInitialViewController() as! PriorityViewController priorityListVC.delegate = self - self.navigationController?.pushViewController(priorityListVC, animated: true) + navigationController?.pushViewController(priorityListVC, animated: true) } if indexPath.row == RowId.typeAnomalie { let typeVC = UIStoryboard(name: Constants.StoryBoard.typeAnomalie, bundle: nil).instantiateInitialViewController() as! TypeAnomalieViewController typeVC.delegate = self - self.navigationController?.pushViewController(typeVC, animated: true) + navigationController?.pushViewController(typeVC, animated: true) } if indexPath.row == RowId.description { let descriptiveVC = UIStoryboard(name: Constants.StoryBoard.description, bundle: nil).instantiateInitialViewController() as! DescriptiveAnomalyViewController descriptiveVC.delegate = self descriptiveVC.defaultDescriptive = currentAnomalie?.descriptive - self.navigationController?.pushViewController(descriptiveVC, animated: true) + navigationController?.pushViewController(descriptiveVC, animated: true) } - } } extension AddAnomalyViewController: CloseDelegate { - func displayMap() { _ = navigationController?.popViewController(animated: true) } @@ -849,18 +1080,133 @@ extension AddAnomalyViewController: CloseDelegate { let thanksAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.thanks, bundle: nil) let thanksAnomalyVC = thanksAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.thanks) as! ThanksAnomalyViewController thanksAnomalyVC.modalPresentationStyle = .overFullScreen - thanksAnomalyVC.currentAnomaly = self.currentAnomalie + thanksAnomalyVC.currentAnomaly = currentAnomalie thanksAnomalyVC.status = .showThanks - self.present(thanksAnomalyVC, animated: true, completion: nil) + present(thanksAnomalyVC, animated: true, completion: nil) thanksAnomalyVC.closeDelegate = self } - } // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { +private func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { guard let input = input else { return nil } - return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)}) + return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value) }) } +extension AddAnomalyViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func useCamera() { + print(" prise de photo ...") + if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) { + checkCameraStatus() + } else { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: "Device has no camera", preferredStyle: .alert) + let defaultAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default, handler: { _ in + print("Device has no camera") + }) + alertController.addAction(defaultAction) + present(alertController, animated: true, completion: nil) + } + } + + func searchPhoto() { + // UIImagePickerController is a view controller that lets a user pick media from their photo library. + let imagePickerController = UIImagePickerController() + + // Only allow photos to be picked, not taken. + imagePickerController.sourceType = .photoLibrary + imagePickerController.allowsEditing = false + + // Make sure ViewController is notified when the user picks an image. + imagePickerController.delegate = self + present(imagePickerController, animated: true, completion: nil) + } + + /// Methode permettant de verifier les authorisations sur l'utilisation de la caméra + /// + func checkCameraStatus() { + let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video))) + switch authStatus { + case .authorized: showCameraPicker() + case .denied: alertPromptToAllowCameraAccessViaSettings() + case .notDetermined: permissionPrimeCameraAccess() + default: permissionPrimeCameraAccess() + } + } + + /// Affiche une alerte pour demander la modification des authorisations pour l'utilisation de la caméra + /// + func alertPromptToAllowCameraAccessViaSettings() { + let alert = UIAlertController(title: Constants.AlertBoxTitle.grantPhoto, message: Constants.AlertBoxMessage.grantPhoto, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: Constants.AlertBoxTitle.parametres, style: .cancel) { _ in + UIApplication.shared.openURL(URL(string: UIApplication.openSettingsURLString)!) + }) + let cancelAction = UIAlertAction(title: Constants.AlertBoxTitle.annuler, style: UIAlertAction.Style.default, handler: nil) + alert.addAction(cancelAction) + + present(alert, animated: true, completion: nil) + } + + /// Permet de demander l'authorisation d'utiliser la camera pour la prise de photo + /// + func permissionPrimeCameraAccess() { + if AVCaptureDevice.devices(for: AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video))).count > 0 { + AVCaptureDevice.requestAccess(for: AVMediaType(rawValue: convertFromAVMediaType(AVMediaType.video)), completionHandler: { [weak self] _ in + DispatchQueue.main.async { + self?.checkCameraStatus() // try again + } + }) + } + } + + /// Ouverture de l'appareil photo + /// + func showCameraPicker() { + let imagePickerController = UIImagePickerController() + imagePickerController.delegate = self + imagePickerController.sourceType = UIImagePickerController.SourceType.camera + imagePickerController.allowsEditing = false + present(imagePickerController, animated: true, completion: nil) + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + // Dismiss the picker if the user canceled. + dismiss(animated: true, completion: nil) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + // Local variable inserted by Swift 4.2 migrator. + let info = convertFromUIImagePickerControllerInfoKeyDictionary(info) + + // The info dictionary may contain multiple representations of the image. You want to use the original. + if let selectedImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage { + // Dismiss the picker. + dismiss(animated: true, completion: nil) + // removeAnimate() + + // Compress and resize Image + let imageResize = selectedImage.resizeWithWidth(width: Constants.Image.maxWith) + let compressData = imageResize!.jpegData(compressionQuality: Constants.Image.compressionQuality) + let compressedImage = UIImage(data: compressData!) + + changePhoto(newPhoto: compressedImage!, isFirstPhoto: isFirstPicture) + } else { + fatalError("Expected a dictionary containing an image, but was provided the following: \(info)") + } + } +} + +// Helper function inserted by Swift 4.2 migrator. +private func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] { + return Dictionary(uniqueKeysWithValues: input.map { key, value in (key.rawValue, value) }) +} + +// Helper function inserted by Swift 4.2 migrator. +private func convertFromAVMediaType(_ input: AVMediaType) -> String { + return input.rawValue +} + +// Helper function inserted by Swift 4.2 migrator. +private func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String { + return input.rawValue +} diff --git a/DansMaRue/Controllers/Anomalie/AnomalyDetailViewController.swift b/DansMaRue/Controllers/Anomalie/AnomalyDetailViewController.swift index 07e7901..50b28d5 100644 --- a/DansMaRue/Controllers/Anomalie/AnomalyDetailViewController.swift +++ b/DansMaRue/Controllers/Anomalie/AnomalyDetailViewController.swift @@ -6,11 +6,11 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit +import AppAuth +import SDWebImage import SwiftyJSON import TTGSnackbar -import SDWebImage - +import UIKit protocol CustomNavigationDelegate: NSObjectProtocol { func displayAddAnomaly(anomalySelected: Anomalie) @@ -22,259 +22,126 @@ enum AnomalieDetailStatus { case undefined } -class AnomalyDetailViewController: UIViewController { - - //MARK: - Properties +enum UpdateAnomalieStateStatus { + case success + case failure + case nothing +} + +class AnomalyDetailViewController: UIViewController, OIDAuthStateChangeDelegate, UIPickerViewDelegate, UIPickerViewDataSource { + // MARK: - Properties + var selectedAnomaly: Anomalie? = nil var currentAnomalyState = AnomalieDetailStatus.notsolved weak var customNavigationDelegate: CustomNavigationDelegate? var numberPhotoSelect = 1 + var idMessageServiceFait: String = "" + + // MARK: - IBOutlets + + @IBOutlet var detailsTableView: UITableView! - //MARK: - IBOutlets - @IBOutlet weak var anomalyMainImageView: UIImageView! - @IBOutlet weak var anomalyCloseButton: UIButton! - @IBOutlet weak var anomalyEditButton: UIButton! - @IBOutlet weak var anomalyStreetLabel: UILabel! - @IBOutlet weak var anomalyStreetBisLabel: UILabel! - @IBOutlet weak var timelineLabel: UILabel! - @IBOutlet weak var mainTitleLabel: UILabel! - @IBOutlet weak var anomalyDescriptionLabel: UILabel! - @IBOutlet weak var concernedLabel: UILabel! - @IBOutlet weak var greetingsLabel: UILabel! - @IBOutlet weak var anomalySolvedLabel: UIButton! - @IBOutlet weak var anomalyInProgressLabel: UILabel! @IBOutlet var detailView: UIView! - @IBOutlet var previousButton: UIButton! - @IBOutlet var nextButton: UIButton! - @IBOutlet var solvedButton: UIButton_Solved! - @IBOutlet var followButton: UIButton! - @IBOutlet var congratsButton: UIButton_Congrats! - - let imgGreetingsButton = UIImage(named:Constants.Image.thumbsUp) - let imgFollow = UIImage(named:Constants.Image.follow) - let imgUnfollow = UIImage(named:Constants.Image.unfollow) - let imgFollowDisabled = UIImage(named:Constants.Image.followDisabled) - let imgChevronPrevious = UIImage(named:Constants.Image.iconBackChevron) - let imgChevronNext = UIImage(named:Constants.Image.iconChevron) - - //MARK: - View lifecycle + + let imgGreetingsButton = UIImage(named: Constants.Image.thumbsUp) + let imgFollow = UIImage(named: Constants.Image.follow) + let imgUnfollow = UIImage(named: Constants.Image.unfollow) + let imgFollowDisabled = UIImage(named: Constants.Image.followDisabled) + let imgChevronPrevious = UIImage(named: Constants.Image.iconBackChevron) + let imgChevronNext = UIImage(named: Constants.Image.iconChevron) + + private var authState: OIDAuthState? + typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + let redirectURI: String = Constants.Authentification.RedirectURI + let authStateKey: String = "authState" + private var updateAnomlieState: UpdateAnomalieStateStatus = .nothing + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - + self.detailsTableView.delegate = self + self.detailsTableView.dataSource = self + self.detailsTableView.estimatedRowHeight = 186 + self.detailsTableView.rowHeight = UITableView.automaticDimension self.numberPhotoSelect = 1 - - let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) - let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) - - swipeRight.direction = UISwipeGestureRecognizer.Direction.right - swipeLeft.direction = UISwipeGestureRecognizer.Direction.left - - self.view.addGestureRecognizer(swipeRight) - self.view.addGestureRecognizer(swipeLeft) - - - let tintedImageBack = imgChevronPrevious?.withRenderingMode(.alwaysTemplate) - self.previousButton.setImage(tintedImageBack, for: .normal) - self.previousButton.tintColor = .white - - let tintedImageNext = imgChevronNext?.withRenderingMode(.alwaysTemplate) - self.nextButton.setImage(tintedImageNext, for: .normal) - self.nextButton.tintColor = .white - - self.followButton.setImage(self.imgFollowDisabled, for: .disabled) - self.showOrHideSubviews() - self.replaceAnomalyValues() } override func viewDidAppear(_ animated: Bool) { - - //Permet de de dessiner la ligne grise au dessus des félicitations/personnes concernées - let doYourPath = UIBezierPath(rect: CGRect(x: 0, y: concernedLabel.frame.origin.y - 10, width: detailView.frame.size.width - 20, height: 1)) - let layer = CAShapeLayer() - layer.path = doYourPath.cgPath - layer.fillColor = UIColor.lightGreyDmr().cgColor - self.detailView.layer.addSublayer(layer) - + self.detailsTableView.layoutIfNeeded() + self.detailsTableView.reloadData() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: nil) } - - //MARK: - UI management - func showOrHideSubviews() { - switch currentAnomalyState { - case .notsolved: - self.anomalyCloseButton.isHidden = false - self.anomalyEditButton.isHidden = true - - self.anomalySolvedLabel.isHidden = true - self.greetingsLabel.isHidden = true - self.congratsButton.isHidden = true - self.concernedLabel.isHidden = selectedAnomaly?.source == .ramen - - - self.anomalyInProgressLabel.isHidden = false - self.anomalyInProgressLabel.text = Constants.LabelMessage.anomalieInProgress - self.anomalyInProgressLabel.layer.masksToBounds = true - self.anomalyInProgressLabel.layer.cornerRadius = 20 - - self.anomalyInProgressLabel.backgroundColor = UIColor.orangeDmr() - - if (selectedAnomaly?.resolvedAuthorization)! { - self.solvedButton.isHidden = false - } else { - self.solvedButton.isEnabled = false - self.solvedButton.backgroundColor = UIColor.greyDmr() - } - - - //Suivi d'une anomalie - if let anomalie = self.selectedAnomaly, !anomalie.isIncidentFollowedByUser { - self.followButton.isHidden = false - self.followButton.isEnabled = anomalie.source == .dmr - self.followButton.setImage(self.imgFollow, for: .normal) - self.followButton.accessibilityLabel = Constants.LabelMessage.followAnomaly - } else { - self.followButton.isHidden = false - self.followButton.isEnabled = true - self.followButton.setImage(self.imgUnfollow, for: .normal) - self.followButton.accessibilityLabel = Constants.LabelMessage.unfollowAnomaly - } - - case .solved: - self.anomalyCloseButton.isHidden = false - self.anomalyEditButton.isHidden = true - self.followButton.isHidden = true - self.solvedButton.isHidden = true - self.anomalyInProgressLabel.isHidden = true - self.anomalySolvedLabel.isHidden = false - self.anomalySolvedLabel.setTitle(Constants.LabelMessage.anomalieSolved, for: .normal) - self.anomalySolvedLabel.layer.cornerRadius = 15 - self.anomalySolvedLabel.backgroundColor = UIColor.greenDmr() - - self.greetingsLabel.isHidden = false - self.congratsButton.isHidden = false - self.congratsButton.isEnabled = selectedAnomaly?.source == .dmr - - default: - self.anomalyCloseButton.isHidden = false - self.anomalyEditButton.isHidden = false - self.followButton.isHidden = false - self.greetingsLabel.isHidden = false - self.congratsButton.isHidden = false - self.anomalySolvedLabel.isHidden = false - self.anomalyInProgressLabel.isHidden = true - - - } - } - - func replaceAnomalyValues() { - - if let anomalie = selectedAnomaly { - let postalCode = MapsUtils.getPostalCode(address: anomalie.address) - - self.mainTitleLabel.text = anomalie.alias - self.anomalyDescriptionLabel.text = anomalie.descriptive - self.anomalyStreetLabel.text = MapsUtils.getStreetAddress(address: anomalie.address) - self.anomalyStreetBisLabel.text = MapsUtils.boroughLabel(postalCode: postalCode) - self.timelineLabel.text = DateUtils.formatDateByLocal(dateString: anomalie.date) + " " + anomalie.hour + "\n" + anomalie.number - self.timelineLabel.lineBreakMode = .byClipping - self.timelineLabel.numberOfLines=0 - self.concernedLabel.text = "\(anomalie.followers) intéressé(e)s" - self.greetingsLabel.text = "\(anomalie.congratulations) félicitation(s)" - - self.previousButton.isHidden = anomalie.nbPhoto <= 1 - self.nextButton.isHidden = anomalie.nbPhoto <= 1 - - if anomalie.firstImageUrl.isEmpty { - if(anomalie.photoDoneUrl.isEmpty) { - anomalyMainImageView.image = anomalie.imageCategorie - anomalyMainImageView.contentMode = UIView.ContentMode.scaleAspectFit - } else { - let imageURL = URL(string: anomalie.photoDoneUrl) ?? URL(string: Constants.Image.noImage) - anomalyMainImageView.sd_setImage(with: imageURL!, placeholderImage: anomalie.imageCategorie, options: .allowInvalidSSLCertificates) - } - } else { - let imageURL = URL(string: anomalie.firstImageUrl) ?? URL(string: Constants.Image.noImage) - anomalyMainImageView.sd_setImage(with: imageURL!, placeholderImage: anomalie.imageCategorie, options: .allowInvalidSSLCertificates) - } - - } - - } - - //Navigation entre photos + + // Navigation entre photos @objc func respondToSwipeGesture(gesture: UIGestureRecognizer) { - if let swipeGesture = gesture as? UISwipeGestureRecognizer { - - switch swipeGesture.direction { case UISwipeGestureRecognizer.Direction.right: - print("Swiped right") - if (self.numberPhotoSelect>1) { - numberPhotoSelect-=1 + print("Swiped right") + if self.numberPhotoSelect > 1 { + self.numberPhotoSelect -= 1 } - anomalyMainImageView.sd_setImage(with: setPhotoURL(), placeholderImage: self.selectedAnomaly?.imageCategorie, options: .allowInvalidSSLCertificates) + self.detailsTableView.reloadData() case UISwipeGestureRecognizer.Direction.left: print("Swiped left") - if (self.numberPhotoSelect<(self.selectedAnomaly?.nbPhoto)!) { - self.numberPhotoSelect+=1 + if self.numberPhotoSelect < (self.selectedAnomaly?.nbPhoto)! { + self.numberPhotoSelect += 1 } - anomalyMainImageView.sd_setImage(with: setPhotoURL(), placeholderImage: self.selectedAnomaly?.imageCategorie, options: .allowInvalidSSLCertificates) + self.detailsTableView.reloadData() default: break } } } - - - //MARK: - IBActions - @IBAction func closeWindow(_ sender: Any) { + + // MARK: - IBActions + + @objc func closeWindow(_ sender: Any) { self.dismiss(animated: true, completion: nil) } - - @IBAction func editAnomaly(_ sender: Any) { + + @objc func editAnomaly(_ sender: Any) { self.dismiss(animated: true) { self.customNavigationDelegate?.displayAddAnomaly(anomalySelected: self.selectedAnomaly!) } } - - @IBAction func seePreviousPhoto(_ sender: Any) { - - if (self.numberPhotoSelect>1) { - numberPhotoSelect-=1 + + @objc func seePreviousPhoto(_ sender: UIButton) { + if self.numberPhotoSelect > 1 { + self.numberPhotoSelect -= 1 + self.detailsTableView.reloadData() } - - anomalyMainImageView.sd_setImage(with: setPhotoURL(), placeholderImage: self.selectedAnomaly?.imageCategorie, options: .allowInvalidSSLCertificates) } - - @IBAction func seeNextPhoto(_ sender: Any) { - - if (self.numberPhotoSelect<(self.selectedAnomaly?.nbPhoto)!) { - self.numberPhotoSelect+=1 + @objc func seeNextPhoto(_ sender: UIButton) { + if self.numberPhotoSelect < (self.selectedAnomaly?.nbPhoto)! { + self.numberPhotoSelect += 1 + self.detailsTableView.reloadData() } - anomalyMainImageView.sd_setImage(with: setPhotoURL(), placeholderImage: self.selectedAnomaly?.imageCategorie, options: .allowInvalidSSLCertificates) } - - private func setPhotoURL () -> URL { + + private func setPhotoURL() -> URL { var imageURL = URL(string: Constants.Image.noImage) switch self.numberPhotoSelect { case 1: - if(self.selectedAnomaly?.firstImageUrl.isEmpty)! { + if (self.selectedAnomaly?.firstImageUrl.isEmpty)! { imageURL = URL(string: (self.selectedAnomaly?.photoDoneUrl)!) ?? URL(string: Constants.Image.noImage) + } else { - //on a juste la photo done + // on a juste la photo done imageURL = URL(string: (self.selectedAnomaly?.firstImageUrl)!) ?? URL(string: Constants.Image.noImage) } case 2: - //1 image de l'ano (close ou far) + 1 photo done - if(URL(string: (self.selectedAnomaly?.firstImageUrl)!) == URL(string: (self.selectedAnomaly?.secondImageUrl)!)) { + // 1 image de l'ano (close ou far) + 1 photo done + if URL(string: (self.selectedAnomaly?.firstImageUrl)!) == URL(string: (self.selectedAnomaly?.secondImageUrl)!) { imageURL = URL(string: (self.selectedAnomaly?.photoDoneUrl)!) ?? URL(string: Constants.Image.noImage) } else { - //Pas de photo done + // Pas de photo done imageURL = URL(string: (self.selectedAnomaly?.secondImageUrl)!) ?? URL(string: Constants.Image.noImage) } + case 3: imageURL = URL(string: (self.selectedAnomaly?.photoDoneUrl)!) ?? URL(string: Constants.Image.noImage) default: @@ -283,36 +150,32 @@ class AnomalyDetailViewController: UIViewController { return imageURL! } - - //Abonnement ou désabonnement du suivi de l'anomalie - @IBAction func doAwesomeFeature(_ sender: Any) { - if (!User.shared.isLogged){ - followButton.setImage(imgFollow, for: .normal) - //Redirection vers le Compte Parisien - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - self.present(compteParisienVC, animated: true) + + // Abonnement ou désabonnement du suivi de l'anomalie + @objc func doAwesomeFeature(followButton: UIButton) { + if !User.shared.isLogged { + followButton.setImage(self.imgFollow, for: .normal) + followButton.accessibilityLabel = Constants.LabelMessage.followAnomaly + // Redirection vers le Compte Parisien + self.connectToMonParis() } else { var hasFollow = false if let anomalie = selectedAnomaly { - hasFollow = selectedAnomaly?.isIncidentFollowedByUser ?? false - - if (!hasFollow) { - - self.followButton.isEnabled = false - + hasFollow = self.selectedAnomaly?.isIncidentFollowedByUser ?? false + followButton.isEnabled = false + if !hasFollow { DispatchQueue.global().async { RestApiManager.sharedInstance.follow(anomalie: anomalie, onCompletion: { (result: Bool) in DispatchQueue.main.async { if result { - //Mise à jour de l'UI + // Mise à jour de l'UI self.selectedAnomaly?.isIncidentFollowedByUser = true self.selectedAnomaly?.followers += 1 - let numberOfFollowers = self.selectedAnomaly?.followers - self.concernedLabel.text = "\(String(numberOfFollowers!)) concerné(e)s" - self.followButton.setImage(self.imgUnfollow, for: .normal) - - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.followMalfunction, duration: .middle) + self.detailsTableView.reloadData() + followButton.setImage(self.imgUnfollow, for: .normal) + followButton.accessibilityLabel = Constants.LabelMessage.unfollowAnomaly + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.followMalfunction, duration: .middle) snackbar.actionTextColor = UIColor.pinkDmr() snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! @@ -320,29 +183,28 @@ class AnomalyDetailViewController: UIViewController { snackbar.messageTextAlign = NSTextAlignment.center snackbar.show() + UIAccessibility.post(notification: .announcement, argument: Constants.AlertBoxMessage.followMalfunction) + } else { + self.showPopupMaintenance() } - self.followButton.isEnabled = true - + followButton.isEnabled = true } }) } } else { - self.followButton.isEnabled = false - DispatchQueue.global().async { RestApiManager.sharedInstance.unfollow(anomalie: anomalie, onCompletion: { (result: Bool) in DispatchQueue.main.async { if result { - //Mise à jour de l'UI + // Mise à jour de l'UI self.selectedAnomaly?.isIncidentFollowedByUser = false self.selectedAnomaly?.followers -= 1 - let numberOfFollowers = self.selectedAnomaly?.followers - self.concernedLabel.text = "\(String(numberOfFollowers!)) concerné(e)s" - self.followButton.setImage(self.imgFollow, for: .normal) - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.unfollowMalfunction, duration: .middle) - + self.detailsTableView.reloadData() + followButton.setImage(self.imgFollow, for: .normal) + followButton.accessibilityLabel = Constants.LabelMessage.followAnomaly + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.unfollowMalfunction, duration: .middle) snackbar.actionTextColor = UIColor.pinkDmr() snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! @@ -350,141 +212,570 @@ class AnomalyDetailViewController: UIViewController { snackbar.messageTextAlign = NSTextAlignment.center snackbar.show() + UIAccessibility.post(notification: .announcement, argument: Constants.AlertBoxMessage.unfollowMalfunction) + } else { + self.showPopupMaintenance() } - self.followButton.isEnabled = true - + followButton.isEnabled = true } }) } } } } + } + + func didChange(_ state: OIDAuthState) { + self.setAuthState(state) + } + + func connectToMonParis() { + let authorizationEndpoint = Constants.Authentification.authorizationEndpoint + let tokenEndpoint = Constants.Authentification.tokenEndpoint + let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + + self.doAuthWithAutoCodeExchange(configuration: configuration, + clientID: Constants.Authentification.clientID, + clientSecret: nil) + } + + func setAuthState(_ authState: OIDAuthState?) { + if self.authState == authState { + return + } + self.authState = authState + self.authState?.stateChangeDelegate = self + var data: Data? = nil + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + if let userDefaults = UserDefaults(suiteName: Constants.Authentification.userDefault) { + userDefaults.set(data, forKey: self.authStateKey) + userDefaults.synchronize() + } } - @IBAction func congratsTheAnomaly(_ sender: Any) { - - if(!User.shared.isLogged){ - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - self.navigationController?.present(compteParisienVC, animated: true) + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: redirectURI) else { + print("Error creating URL for : \(redirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + print("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + self.userInfo() + } else { + print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func userInfo() { + let userInfoEndpoint = Constants.Authentification.userInfoEndpoint + print("Performing userinfo request") + let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken + + self.authState?.performAction { accessToken, _, error in + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + print("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + print("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + print("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userInfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + print("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + print("Non-HTTP response") + return + } + + guard let data = data else { + print("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + print("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + print("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json as? [String: Any], let uid = json["uid"] as? String, let validatedAccount = json["validatedAccount"] as? String { + print("Success: \(json)") + + if validatedAccount != "true" { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) + + let okAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default) { _ in + // nothing + } + + alertController.addAction(okAction) + + self.present(alertController, animated: true, completion: nil) + } else { + User.shared.uid = uid + UserDefaults.standard.set(uid, forKey: Constants.Key.uid) + RestApiManager.sharedInstance.getIdentityStore(guid: User.shared.uid!) { + (_: Bool) in + } + } + } + } + } + + task.resume() + } + } + + @objc func congratsTheAnomaly(congratsButton: UIButton) { + if !User.shared.isLogged { + self.connectToMonParis() } else { - var cancelAction = false - self.congratsButton.isEnabled = false - self.congratsButton.backgroundColor = UIColor.greyDmr() + congratsButton.isEnabled = false + congratsButton.backgroundColor = UIColor.greyDmr() - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.congratulate, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) - { (snackbar) -> Void in - cancelAction = true - self.congratsButton.isEnabled = true - let numberOfCongrats = self.selectedAnomaly?.congratulations - self.greetingsLabel.text = "\(String(numberOfCongrats!)) félicitation(s)" - self.congratsButton.backgroundColor = UIColor.greenDmr() - - } + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.congratulate, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) + { _ in + cancelAction = true + congratsButton.isEnabled = true + self.detailsTableView.reloadData() + congratsButton.backgroundColor = UIColor.greenDmr() + } snackbar.actionTextColor = UIColor.pinkDmr() snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 15.0)! snackbar.messageTextAlign = NSTextAlignment.center - //Callback si l'utilisateur ne fais pas "annuler" + // Callback si l'utilisateur ne fais pas "annuler" snackbar.dismissBlock = { - - (snackbar: TTGSnackbar) -> Void in + (_: TTGSnackbar) in if !cancelAction { DispatchQueue.global().async { - RestApiManager.sharedInstance.congratulateAnomalie(anomalie: (self.selectedAnomaly)!, onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { self.selectedAnomaly?.congratulations += 1 - let numberOfCongrats = self.selectedAnomaly?.congratulations - self.greetingsLabel.text = "\(String(numberOfCongrats!)) félicitation(s)" - self.congratsButton.isHidden = true - + self.detailsTableView.reloadData() + congratsButton.isHidden = true } + self.updateAnomlieState = .success + } else { + self.updateAnomlieState = .failure } + self.detailsTableView.reloadData() }) } } - - } snackbar.show() - } - } - @IBAction func solvedAction(_ sender: Any) { + @objc func solvedAction(solvedButton: UIButton) { + let isAnomalieFromRamen = self.selectedAnomaly!.number.uppercased().starts(with: "W") - self.solvedButton.isEnabled = false - self.solvedButton.backgroundColor = UIColor.greyDmr() - var cancelAction = false + if isAnomalieFromRamen && self.selectedAnomaly!.resolvedAuthorization { + // Affichage alert selection message service fait + let alert = UIAlertController(title: "Choisir un message ci-dessous", message: "\n\n\n\n\n\n\n\n\n", preferredStyle: UIAlertController.Style.alert) + alert.isModalInPopover = true + let pickerFrame: CGRect = .init(x: 5, y: 70, width: 250, height: 140) + let picker: UIPickerView = .init(frame: pickerFrame) + picker.delegate = self + picker.dataSource = self + alert.view.addSubview(picker) - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.solvedMalfunction, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) - { (snackbar) -> Void in - cancelAction = true - self.solvedButton.isEnabled = true - self.solvedButton.backgroundColor = UIColor.pinkDmr() - + if selectedAnomaly!.messagesSFTypologie.count>0 { + idMessageServiceFait = selectedAnomaly!.messagesSFTypologie[0].id + } + + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default, handler: { (_: UIAlertAction!) in + self.doSolvedAction(solvedButton: solvedButton) + }) + alert.addAction(OKAction) + + self.present(alert, animated: true, completion: nil) + } + else { + self.doSolvedAction(solvedButton: solvedButton) } + + } + + func doSolvedAction(solvedButton: UIButton) { + solvedButton.isEnabled = false + solvedButton.backgroundColor = UIColor.greyDmr() + var cancelAction = false + + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.solvedMalfunction, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) + { _ in + cancelAction = true + solvedButton.isEnabled = true + solvedButton.backgroundColor = UIColor.pinkDmr() + } snackbar.actionTextColor = UIColor.pinkDmr() snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 15.0)! snackbar.messageTextAlign = NSTextAlignment.center - //Callback si l'utilisateur ne fais pas "annuler" + // Callback si l'utilisateur ne fais pas "annuler" snackbar.dismissBlock = { - - (snackbar: TTGSnackbar) -> Void in + (_: TTGSnackbar) in if !cancelAction { DispatchQueue.global().async { - - RestApiManager.sharedInstance.incidentResolved(anomalie: (self.selectedAnomaly)!, onCompletion: { (result: Bool) in + RestApiManager.sharedInstance.incidentResolved(anomalie: (self.selectedAnomaly)!, numeroMessage: self.idMessageServiceFait, email: User.shared.email ?? "", onCompletion: { (result: Bool) in DispatchQueue.main.async { if result { - - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.anomalieResolue, duration: .middle) + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.anomalieResolue, duration: .middle) snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 15.0)! snackbar.messageTextAlign = NSTextAlignment.center snackbar.show() - - self.anomalySolvedLabel.isHidden = false - self.anomalySolvedLabel.setTitle(Constants.LabelMessage.anomalieSolved, for: .normal) - self.anomalySolvedLabel.layer.cornerRadius = 15 - self.anomalySolvedLabel.backgroundColor = UIColor.greenDmr() - self.anomalyInProgressLabel.isHidden = true - self.congratsButton.isHidden = false - self.greetingsLabel.isHidden = false - self.solvedButton.isHidden = true - self.followButton.isHidden = true - - } - else { - self.solvedButton.backgroundColor = UIColor.pinkDmr() - self.solvedButton.isEnabled = true + self.updateAnomlieState = UpdateAnomalieStateStatus.success + self.currentAnomalyState = .solved + } else { + self.updateAnomlieState = UpdateAnomalieStateStatus.failure + self.showPopupMaintenance() } + self.detailsTableView.reloadData() } }) } } - - } snackbar.show() + } + + func showPopupMaintenance() { + let alert = UIAlertController(title: Constants.AlertBoxTitle.information, message: Constants.AlertBoxMessage.maintenance, preferredStyle: UIAlertController.Style.alert) + let okBtn = UIAlertAction(title: "Ok", style: .default, handler: { (_: UIAlertAction) in + }) + alert.addAction(okBtn) + self.present(alert, animated: true, completion: nil) + } + + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return self.selectedAnomaly!.messagesSFTypologie.count + } + + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + return selectedAnomaly!.messagesSFTypologie[row].message + } + + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + idMessageServiceFait = selectedAnomaly!.messagesSFTypologie[row].id + } + + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + var pickerLabel: UILabel? = (view as? UILabel) + if pickerLabel == nil { + pickerLabel = UILabel(); + pickerLabel?.font = UIFont(name: "Montserrat", size: 13.0)! + } + pickerLabel?.text = selectedAnomaly!.messagesSFTypologie[row].message + + return pickerLabel! + } + + private func applyDynamicTypeSystemFont(label: UILabel, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.preferredFont(forTextStyle: .title3) + } +} + +extension AnomalyDetailViewController: UITableViewDelegate, UITableViewDataSource { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + var customCell: UITableViewCell + switch indexPath.row { + case 0: customCell = self.configureHeaderCellView(tableView: tableView, anomalie: self.selectedAnomaly!) + case 1: customCell = self.configureDetailsCellView(tableView: tableView, anomalie: self.selectedAnomaly!) + case 2: customCell = self.configureButtonsCellView(tableView: tableView, anomalie: self.selectedAnomaly!) + default: + customCell = UITableViewCell() + } + return customCell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 3 + } + + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + private func configureHeaderCellView(tableView: UITableView, anomalie: Anomalie) -> UITableViewCell { + var customCell: UITableViewCell + customCell = tableView.dequeueReusableCell(withIdentifier: "header_cell")! + let anomalyMainImageView = customCell.viewWithTag(100) as! UIImageView + let anomalyCloseButton = customCell.viewWithTag(101) as! UIButton + let anomalyEditButton = customCell.viewWithTag(102) as! UIButton + let previousButton = customCell.viewWithTag(103) as! UIButton + let nextButton = customCell.viewWithTag(104) as! UIButton + let followButton = customCell.viewWithTag(105) as! UIButton + let anomalySolvedLabel = customCell.viewWithTag(106) as! UILabel + let anomalyInProgressLabel = customCell.viewWithTag(107) as! UILabel + + let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) + let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) + swipeRight.direction = UISwipeGestureRecognizer.Direction.right + swipeLeft.direction = UISwipeGestureRecognizer.Direction.left + + anomalyMainImageView.isUserInteractionEnabled = true + anomalyMainImageView.addGestureRecognizer(swipeRight) + anomalyMainImageView.addGestureRecognizer(swipeLeft) + anomalyMainImageView.contentMode = .scaleAspectFit + anomalyMainImageView.sd_setImage(with: self.setPhotoURL(), placeholderImage: anomalie.imageCategorie, options: .allowInvalidSSLCertificates) + + anomalyCloseButton.addTarget(self, action: #selector(self.closeWindow(_:)), for: .touchUpInside) + anomalyCloseButton.accessibilityLabel = Constants.TitleButton.close + anomalyEditButton.addTarget(self, action: #selector(self.editAnomaly(_:)), for: .touchUpInside) + nextButton.addTarget(self, action: #selector(self.seeNextPhoto(_:)), for: .touchUpInside) + previousButton.addTarget(self, action: #selector(self.seePreviousPhoto(_:)), for: .touchUpInside) + followButton.setImage(self.imgFollowDisabled, for: .disabled) + followButton.addTarget(self, action: #selector(self.doAwesomeFeature(followButton:)), for: .touchUpInside) + let tintedImageBack = self.imgChevronPrevious?.withRenderingMode(.alwaysTemplate) + previousButton.setImage(tintedImageBack, for: .normal) + previousButton.tintColor = .white + let tintedImageNext = self.imgChevronNext?.withRenderingMode(.alwaysTemplate) + nextButton.setImage(tintedImageNext, for: .normal) + nextButton.tintColor = .white + + previousButton.isHidden = anomalie.nbPhoto <= 1 + nextButton.isHidden = anomalie.nbPhoto <= 1 + + switch self.currentAnomalyState { + case .notsolved: + anomalyCloseButton.isHidden = false + anomalyEditButton.isHidden = true + + anomalySolvedLabel.isHidden = true + + anomalyInProgressLabel.isHidden = false + anomalyInProgressLabel.text = Constants.LabelMessage.anomalieInProgress + anomalyInProgressLabel.layer.masksToBounds = true + anomalyInProgressLabel.layer.cornerRadius = 15 + + anomalyInProgressLabel.backgroundColor = UIColor.orangeDmr() + followButton.isAccessibilityElement = true + followButton.accessibilityTraits = .button + anomalyMainImageView.contentMode = .scaleAspectFit + anomalyMainImageView.sd_setImage(with: self.setPhotoURL(), placeholderImage: anomalie.imageCategorie, options: .allowInvalidSSLCertificates) + + if !anomalie.isIncidentFollowedByUser { + followButton.isHidden = false + followButton.isEnabled = anomalie.source == .dmr + followButton.setImage(self.imgFollow, for: .normal) + followButton.accessibilityLabel = Constants.LabelMessage.followAnomaly + followButton.accessibilityHint = Constants.LabelMessage.followAnomaly + } else { + followButton.isHidden = false + followButton.isEnabled = true + followButton.setImage(self.imgUnfollow, for: .normal) + followButton.accessibilityLabel = Constants.LabelMessage.unfollowAnomaly + followButton.accessibilityHint = Constants.LabelMessage.unfollowAnomaly + } + case .solved: + anomalyCloseButton.isHidden = false + anomalyEditButton.isHidden = true + followButton.isHidden = true + anomalyInProgressLabel.isHidden = true + anomalySolvedLabel.isHidden = false + anomalySolvedLabel.numberOfLines = 0 + anomalySolvedLabel.text = Constants.LabelMessage.anomalieSolved + anomalySolvedLabel.layer.masksToBounds = true + anomalySolvedLabel.layer.cornerRadius = 15 + anomalySolvedLabel.backgroundColor = UIColor.greenDmr() + default: + anomalyCloseButton.isHidden = false + anomalyEditButton.isHidden = false + followButton.isHidden = false + anomalySolvedLabel.isHidden = false + anomalyInProgressLabel.isHidden = true + } + anomalyInProgressLabel.adjustsFontForContentSizeCategory = true + anomalyInProgressLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 13.0) + anomalyInProgressLabel.sizeToFit() + anomalySolvedLabel.adjustsFontForContentSizeCategory = true + anomalySolvedLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 13.0) + if self.updateAnomlieState == .success { + anomalySolvedLabel.isHidden = false + anomalySolvedLabel.text = Constants.LabelMessage.anomalieSolved + anomalySolvedLabel.layer.cornerRadius = 15 + anomalySolvedLabel.backgroundColor = UIColor.greenDmr() + anomalyInProgressLabel.isHidden = true + followButton.isHidden = true + } + return customCell + } + + private func configureDetailsCellView(tableView: UITableView, anomalie: Anomalie) -> UITableViewCell { + var customCell: UITableViewCell + customCell = tableView.dequeueReusableCell(withIdentifier: "details_cell")! + customCell.isAccessibilityElement = false + let anomalyStreetLabel = customCell.viewWithTag(101) as! UILabel + let anomalyStreetBisLabel = customCell.viewWithTag(102) as! UILabel + let timelineLabel = customCell.viewWithTag(103) as! UILabel + let mainTitleLabel = customCell.viewWithTag(104) as! UILabel + let anomalyDescriptionLabel = customCell.viewWithTag(105) as! UILabel + let concernedLabel = customCell.viewWithTag(106) as! UILabel + let greetingsLabel = customCell.viewWithTag(107) as! UILabel + anomalyStreetLabel.isAccessibilityElement = true + anomalyStreetLabel.accessibilityTraits = .staticText + anomalyStreetBisLabel.isAccessibilityElement = true + anomalyStreetBisLabel.accessibilityTraits = .staticText + timelineLabel.isAccessibilityElement = true + timelineLabel.accessibilityTraits = .staticText + mainTitleLabel.isAccessibilityElement = true + mainTitleLabel.accessibilityTraits = .staticText + anomalyDescriptionLabel.isAccessibilityElement = true + anomalyDescriptionLabel.accessibilityTraits = .staticText + concernedLabel.isAccessibilityElement = true + concernedLabel.accessibilityTraits = .staticText + greetingsLabel.isAccessibilityElement = true + greetingsLabel.accessibilityTraits = .staticText + let postalCode = MapsUtils.getPostalCode(address: anomalie.address) + + mainTitleLabel.text = anomalie.alias + anomalyDescriptionLabel.text = anomalie.descriptive + anomalyStreetLabel.text = MapsUtils.getStreetAddress(address: anomalie.address) + anomalyStreetBisLabel.text = MapsUtils.boroughLabel(postalCode: postalCode) + timelineLabel.text = DateUtils.formatDateByLocal(dateString: anomalie.date) + " " + anomalie.hour + "\n" + anomalie.number + timelineLabel.lineBreakMode = .byClipping + timelineLabel.numberOfLines = 0 + concernedLabel.isHidden = anomalie.source == .ramen + concernedLabel.text = "\(anomalie.followers) intéressé(e)s" + greetingsLabel.isHidden = self.currentAnomalyState == .notsolved + greetingsLabel.text = "\(anomalie.congratulations) félicitation(s)" + anomalyStreetLabel.adjustsFontForContentSizeCategory = true + anomalyStreetLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) + anomalyStreetBisLabel.adjustsFontForContentSizeCategory = true + anomalyStreetBisLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 12.0) + timelineLabel.adjustsFontForContentSizeCategory = true + timelineLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) + mainTitleLabel.adjustsFontForContentSizeCategory = true + mainTitleLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 24.0) + anomalyDescriptionLabel.adjustsFontForContentSizeCategory = true + anomalyDescriptionLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) + concernedLabel.adjustsFontForContentSizeCategory = true + concernedLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) + greetingsLabel.adjustsFontForContentSizeCategory = true + greetingsLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) + if self.updateAnomlieState == .success { + greetingsLabel.isHidden = false + } + return customCell + } + + private func configureButtonsCellView(tableView: UITableView, anomalie: Anomalie) -> UITableViewCell { + var customCell: UITableViewCell + customCell = tableView.dequeueReusableCell(withIdentifier: "buttons_cell")! + let solvedButton = customCell.viewWithTag(101) as! UIButton_Solved + solvedButton.titleLabel?.adjustsFontForContentSizeCategory = true + solvedButton.titleLabel?.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 15.0) + let congratsButton = customCell.viewWithTag(102) as! UIButton_Congrats + congratsButton.titleLabel?.adjustsFontForContentSizeCategory = true + congratsButton.titleLabel?.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 18.0) - + if anomalie.resolvedAuthorization { + solvedButton.isHidden = false + congratsButton.isHidden = true + } else { + congratsButton.isHidden = true + solvedButton.isEnabled = false + solvedButton.backgroundColor = UIColor.greyDmr() + } + solvedButton.addTarget(self, action: #selector(self.solvedAction(solvedButton:)), for: .touchUpInside) + congratsButton.isHidden = self.currentAnomalyState != .solved + congratsButton.addTarget(self, action: #selector(self.congratsTheAnomaly(congratsButton:)), for: .touchUpInside) + if self.updateAnomlieState == .success { + solvedButton.isHidden = true + congratsButton.isHidden = false + } + if self.updateAnomlieState == .failure { + solvedButton.backgroundColor = UIColor.pinkDmr() + solvedButton.isEnabled = true + } + if self.currentAnomalyState == .solved { + solvedButton.isHidden = true + } + congratsButton.isEnabled = self.selectedAnomaly?.source == .dmr + return customCell } } - - diff --git a/DansMaRue/Controllers/Anomalie/DescriptiveAnomalyViewController.swift b/DansMaRue/Controllers/Anomalie/DescriptiveAnomalyViewController.swift index 540a412..4f09259 100644 --- a/DansMaRue/Controllers/Anomalie/DescriptiveAnomalyViewController.swift +++ b/DansMaRue/Controllers/Anomalie/DescriptiveAnomalyViewController.swift @@ -9,95 +9,116 @@ import UIKit class DescriptiveAnomalyViewController: UIViewController { - // MARK: - Attributs + var defaultDescriptive: String? let maxLength = 250 + var initialBottomConstraint: CGFloat? + + // MARK: - IBOutlets - //MARK: - IBOutlets @IBOutlet var labelCounter: UILabel! @IBOutlet var textViewDescriptive: UITextView! @IBOutlet var bottomConstraint: NSLayoutConstraint! weak var delegate: AddAnomalyViewController! + @IBOutlet var titleDescriptionLabel: UILabel! + // MARK: - View lifecycle - //MARK: - View lifecycle override func viewDidLoad() { super.viewDidLoad() + titleDescriptionLabel.adjustsFontForContentSizeCategory = true + titleDescriptionLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 12.0) + titleDescriptionLabel.text = Constants.LabelMessage.saisirDetail + titleDescriptionLabel.isHidden = true + textViewDescriptive.adjustsFontForContentSizeCategory = true + textViewDescriptive.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) textViewDescriptive.delegate = self - textViewDescriptive.text = defaultDescriptive - + textViewDescriptive.isAccessibilityElement = true + textViewDescriptive.accessibilityTraits = .staticText + textViewDescriptive.textColor = UIColor.greyDmr() + // textViewDescriptive.accessibilityLabel = defaultDescriptive != nil && !defaultDescriptive!.isEmpty ? defaultDescriptive : Constants.LabelMessage.saisirDetail + textViewDescriptive.text = defaultDescriptive != nil && !defaultDescriptive!.isEmpty ? defaultDescriptive : Constants.LabelMessage.saisirDetail + let length = defaultDescriptive?.count ?? 0 - labelCounter.text = "\(length)/\(maxLength)" - - //Modification de la navigation bar - self.navigationItem.title = Constants.LabelMessage.description + labelCounter.text = "\(length)/\(maxLength)" + labelCounter.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 12.0) + labelCounter.adjustsFontForContentSizeCategory = true + labelCounter.textColor = UIColor.greyDmr() + // Modification de la navigation bar + navigationItem.title = Constants.LabelMessage.description navigationItem.leftBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(backAction)) + navigationItem.leftBarButtonItem?.accessibilityLabel = Constants.AccessibilityLabel.backButton if let image = UIImage(named: Constants.Image.iconBack) { navigationItem.leftBarButtonItem?.image = image - } navigationItem.rightBarButtonItem = UIBarButtonItem(title: "OK", style: .plain, target: self, action: #selector(saveAction)) - + NotificationCenter.default.addObserver(self, selector: #selector(DescriptiveAnomalyViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(DescriptiveAnomalyViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) - } - + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - - //Déploie automatiquement le clavier quand on arrive sur l'écran - self.textViewDescriptive.becomeFirstResponder() + + // Déploie automatiquement le clavier quand on arrive sur l'écran + textViewDescriptive.becomeFirstResponder() + // UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: textViewDescriptive) + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + initialBottomConstraint = bottomConstraint.constant } - + override func viewWillDisappear(_ animated: Bool) { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } - - //Permet d'ajuster la taille du champs texte par rapport au clavier quand il apparait - func adjustingHeight(show:Bool, notification:NSNotification) { + + // Permet d'ajuster la taille du champs texte par rapport au clavier quand il apparait + func adjustingHeight(show: Bool, notification: NSNotification) { // 1 - var userInfo = notification.userInfo! + let userInfo = notification.userInfo! // 2 - let keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue + let keyboardFrame: CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue // 3 let animationDurarion = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval // 4 - let changeInHeight = (keyboardFrame.height) - //5 - UIView.animate(withDuration: animationDurarion, animations: { () -> Void in - self.bottomConstraint.constant = changeInHeight + var changeInHeight = (keyboardFrame.height) + if !UIDevice.current.orientation.isLandscape { + changeInHeight += 70 + } + // 5 + UIView.animate(withDuration: animationDurarion, animations: { () in + self.bottomConstraint.constant = show ? changeInHeight : self.initialBottomConstraint! }) } - @objc func keyboardWillShow(notification:NSNotification) { + + @objc func keyboardWillShow(notification: NSNotification) { adjustingHeight(show: true, notification: notification) } - - @objc func keyboardWillHide(notification:NSNotification) { + + @objc func keyboardWillHide(notification: NSNotification) { adjustingHeight(show: false, notification: notification) } - - //MARK: - Other methods - @objc func backAction(){ + // MARK: - Other methods + + @objc func backAction() { _ = navigationController?.popViewController(animated: true) } - + @objc func saveAction() { delegate.changeDescriptive(descriptive: textViewDescriptive.text) _ = navigationController?.popViewController(animated: true) } - - } +// MARK: - Extension UITextViewDelegate -//MARK: - Extension UITextViewDelegate extension DescriptiveAnomalyViewController: UITextViewDelegate { - // Vérifie si longueur max atteint func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { guard let text = textViewDescriptive.text else { return true } @@ -105,24 +126,46 @@ extension DescriptiveAnomalyViewController: UITextViewDelegate { return newLength < maxLength } - //Compteur de caractères + // Compteur de caractères func textViewDidChange(_ textView: UITextView) { - var counter = textViewDescriptive.text?.count ?? 0 - - if(counter > maxLength){ + var counter = textViewDescriptive.text?.count ?? 0 + + if counter > maxLength { textViewDescriptive.text = (textViewDescriptive.text as NSString).substring(to: maxLength) counter = textViewDescriptive.text?.count ?? 0 + DispatchQueue.main.async { + UIAccessibility.post(notification: .announcement, argument: "Limite de caractères atteinte.") + } } - + textViewDescriptive.accessibilityLabel = textView.text labelCounter.text = "\(counter)/\(maxLength)" labelCounter.textColor = UIColor.greyDmr() } -} - - - - - - + func textViewDidBeginEditing(_ textView: UITextView) { + if textView.textColor == UIColor.greyDmr() { + if defaultDescriptive == nil || defaultDescriptive!.isEmpty { + textView.text = nil + } else { + if let selectedRange = textView.selectedTextRange { + let newPosition = textView.endOfDocument + DispatchQueue.main.async { + textView.selectedTextRange = textView.textRange(from: newPosition, to: newPosition) + } + } + } + textView.textColor = UIColor.black + } + UIView.animate(withDuration: 0.25, animations: { () in + self.titleDescriptionLabel.isHidden = false + }) + } + func textViewDidEndEditing(_ textView: UITextView) { + if textView.text.isEmpty { + UIView.animate(withDuration: 0.25, animations: { () in + self.titleDescriptionLabel.isHidden = true + }) + } + } +} diff --git a/DansMaRue/Controllers/Anomalie/ModifyAddressViewController.swift b/DansMaRue/Controllers/Anomalie/ModifyAddressViewController.swift index ce85bb5..5ba0690 100644 --- a/DansMaRue/Controllers/Anomalie/ModifyAddressViewController.swift +++ b/DansMaRue/Controllers/Anomalie/ModifyAddressViewController.swift @@ -6,16 +6,15 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit -import GooglePlaces import GoogleMaps +import GooglePlaces +import UIKit class ModifyAddressViewController: UIViewController { + // MARK: - Properties - - //MARK: - Properties // Google SearchBar properties - let addressNotification = Notification.Name(rawValue:Constants.NoticationKey.addressNotification) + let addressNotification = Notification.Name(rawValue: Constants.NoticationKey.addressNotification) var resultsViewController: GMSAutocompleteResultsViewController? var searchController: UISearchController? @@ -24,9 +23,10 @@ class ModifyAddressViewController: UIViewController { var equipementSearchController: EquipementSearchTableViewController? var typeEquipementSelected: TypeEquipement? - weak var delegate : AddAnomalyViewController! + weak var delegate: AddAnomalyViewController! + + // MARK: - View lifecycle - //MARK: - View lifecycle override func viewDidLoad() { super.viewDidLoad() } @@ -35,19 +35,20 @@ class ModifyAddressViewController: UIViewController { // Initialize searchBar en fonction du context if delegate.typeContribution == .indoor { if let selectedEquipement = delegate.selectedEquipement { - self.typeEquipementSelected = ReferalManager.shared.getTypeEquipement(forId: selectedEquipement.parentId) + typeEquipementSelected = ReferalManager.shared.getTypeEquipement(forId: selectedEquipement.parentId) } - self.initializeCustomSearchBar() + initializeCustomSearchBar() } else { - self.initSearchBar() + initSearchBar() } + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: searchController?.searchBar) } // MARK: - Other functions + func initSearchBar() { resultsViewController = GMSAutocompleteResultsViewController() resultsViewController?.delegate = self - searchController = UISearchController(searchResultsController: resultsViewController) searchController?.searchResultsUpdater = resultsViewController @@ -55,16 +56,19 @@ class ModifyAddressViewController: UIViewController { if let searchBar = searchController?.searchBar { searchBar.sizeToFit() searchBar.placeholder = Constants.PlaceHolder.saisirAdresse - searchBar.tintColor = UIColor.white searchBar.isTranslucent = false - - self.perform(#selector(searchBarFirstResponder), with: nil, afterDelay: 0.1) + searchBar.accessibilityLabel = Constants.PlaceHolder.saisirAdresse + searchBar.accessibilityHint = Constants.AccessibilityHint.searchBarHint + searchBar.accessibilityTraits = .searchField + perform(#selector(searchBarFirstResponder), with: nil, afterDelay: 0.1) if #available(iOS 13.0, *) { searchController?.hidesNavigationBarDuringPresentation = true - searchBar.searchTextField.backgroundColor=UIColor.white - searchBar.searchTextField.tintColor=UIColor.black + searchBar.searchTextField.backgroundColor = UIColor.white + searchBar.searchTextField.tintColor = UIColor.black + searchBar.searchTextField.adjustsFontForContentSizeCategory = true + searchBar.searchTextField.font = UIFont.preferredFont(forTextStyle: .caption2) self.navigationItem.searchController = searchController } else { // Prevent the navigation bar from being hidden when searching. @@ -73,9 +77,18 @@ class ModifyAddressViewController: UIViewController { view.addSubview(searchBar) } + navigationController?.navigationBar.isTranslucent = false + navigationController?.navigationBar.barTintColor = UIColor.pinkButtonDmr() + extendedLayoutIncludesOpaqueBars = true + // When UISearchController presents the results view, present it in + // this view controller, not one further up the chain. + definesPresentationContext = true + + // Prevent the navigation bar from being hidden when searching. + searchController?.hidesNavigationBarDuringPresentation = false // Active sur le filtre sur Paris uniquement - MapsUtils.filterToParis(resultsViewController: self.resultsViewController!) + MapsUtils.filterToParis(resultsViewController: resultsViewController!) } func initializeCustomSearchBar() { @@ -89,7 +102,7 @@ class ModifyAddressViewController: UIViewController { customSearchController = UISearchController(searchResultsController: equipementSearchController) customSearchController?.hidesNavigationBarDuringPresentation = false - customSearchController?.searchBar.placeholder = self.typeEquipementSelected?.placeholder + customSearchController?.searchBar.placeholder = typeEquipementSelected?.placeholder customSearchController?.searchBar.sizeToFit() equipementSearchController?.tableView.tableHeaderView = customSearchController?.searchBar @@ -97,22 +110,21 @@ class ModifyAddressViewController: UIViewController { view.addSubview((customSearchController?.searchBar)!) - equipementSearchController?.equipements = ReferalManager.shared.getEquipements(forTypeEquipementId: (self.typeEquipementSelected?.typeEquipementId)!)! + equipementSearchController?.equipements = ReferalManager.shared.getEquipements(forTypeEquipementId: (typeEquipementSelected?.typeEquipementId)!)! equipementSearchController?.equipements.sort(by: { $0.name.caseInsensitiveCompare($1.name) == .orderedAscending }) equipementSearchController?.tableView.reloadData() } @objc func searchBarFirstResponder() { - self.searchController?.searchBar.becomeFirstResponder() + searchController?.searchBar.becomeFirstResponder() } } - // Handle the user's selection. extension ModifyAddressViewController: GMSAutocompleteResultsViewControllerDelegate { - func resultsController(_ resultsController: GMSAutocompleteResultsViewController, - didAutocompleteWith place: GMSPlace) { + didAutocompleteWith place: GMSPlace) + { searchController?.isActive = false let geocoder = GMSGeocoder() @@ -122,7 +134,7 @@ extension ModifyAddressViewController: GMSAutocompleteResultsViewControllerDeleg return } if let addressFound = response { - let addressSelected:GMSAddress = addressFound.firstResult()! + let addressSelected: GMSAddress = addressFound.firstResult()! var numRue = "" var rue = "" @@ -139,21 +151,21 @@ extension ModifyAddressViewController: GMSAutocompleteResultsViewControllerDeleg cp = component.name } } - let adresse = numRue + " " + rue + ", " + cp + " Paris, France" + let adresse = numRue + " " + rue + ", " + cp + " PARIS, France" addressSelected.setValue(adresse.components(separatedBy: ",")[0], forKey: "thoroughfare") self.delegate.changeAddress(newAddress: addressSelected) DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - _ = self.navigationController?.popViewController(animated: true) - } + _ = self.navigationController?.popViewController(animated: true) + } } } - } func resultsController(_ resultsController: GMSAutocompleteResultsViewController, - didFailAutocompleteWithError error: Error){ + didFailAutocompleteWithError error: Error) + { // TODO: handle the error. print("Error: ", error.localizedDescription) } @@ -168,12 +180,11 @@ extension ModifyAddressViewController: GMSAutocompleteResultsViewControllerDeleg } } - extension ModifyAddressViewController: EquipementDelegate { func didSelectEquipementAt(equipement: Equipement) { customSearchController?.isActive = false - self.delegate.changeEquipement(newEquipement: equipement) - _ = self.navigationController?.popViewController(animated: true) + delegate.changeEquipement(newEquipement: equipement) + _ = navigationController?.popViewController(animated: true) } } diff --git a/DansMaRue/Controllers/Anomalie/PriorityViewController.swift b/DansMaRue/Controllers/Anomalie/PriorityViewController.swift index 37bca71..fe55fbf 100644 --- a/DansMaRue/Controllers/Anomalie/PriorityViewController.swift +++ b/DansMaRue/Controllers/Anomalie/PriorityViewController.swift @@ -9,39 +9,47 @@ import UIKit class PriorityViewController: UIViewController { + // MARK: - Properties - //MARK: - Properties weak var delegate: AddAnomalyViewController! - //MARK: - IBoutlets + // MARK: - IBoutlets + @IBOutlet var tableView: UITableView! // MARK: - View life Cycle + override func viewDidLoad() { super.viewDidLoad() - - self.navigationItem.title = Constants.LabelMessage.priority + navigationItem.titleView?.isAccessibilityElement = true + navigationItem.title = Constants.LabelMessage.priority + navigationItem.leftBarButtonItem?.accessibilityLabel = Constants.AccessibilityLabel.backButton navigationItem.leftBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(backAction)) if let image = UIImage(named: Constants.Image.iconBack) { navigationItem.leftBarButtonItem?.image = image } - + tableView.estimatedRowHeight = 45 + tableView.rowHeight = UITableView.automaticDimension } - - //MARK: Other Methods - @objc func backAction(){ - _ = navigationController?.popViewController(animated: true) + // MARK: Other Methods + + @objc func backAction() { + _ = navigationController?.popViewController(animated: true) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + } } - //MARK: Extension +// MARK: Extension + extension PriorityViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { delegate.changePriority(newPriority: Priority.allValues[indexPath.row]) _ = navigationController?.popViewController(animated: true) - } } @@ -57,13 +65,14 @@ extension PriorityViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cellIdentifier = "PriorityTableViewCell" - guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PriorityTableViewCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PriorityTableViewCell else { fatalError("The dequeued cell is not an instance of PriorityTableViewCell.") } // Fetches the appropriate meal for the data source layout. cell.labelPriority.text = Priority.allValues[indexPath.row].description - + cell.labelPriority.adjustsFontForContentSizeCategory = true + cell.labelPriority.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) return cell } diff --git a/DansMaRue/Controllers/Anomalie/ThanksAnomalyViewController.swift b/DansMaRue/Controllers/Anomalie/ThanksAnomalyViewController.swift index c754e22..da0acf3 100644 --- a/DansMaRue/Controllers/Anomalie/ThanksAnomalyViewController.swift +++ b/DansMaRue/Controllers/Anomalie/ThanksAnomalyViewController.swift @@ -6,9 +6,10 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit +import AppAuth import SwiftyJSON import TTGSnackbar +import UIKit protocol CloseDelegate: NSObjectProtocol { func displayMap() @@ -21,193 +22,275 @@ public enum ThanksAnomalyStatus: String { case saveIncident = "S" } -class ThanksAnomalyViewController: UIViewController { +class ThanksAnomalyViewController: UIViewController, OIDAuthStateChangeDelegate { + func didChange(_ state: OIDAuthState) { + setAuthState(state) + } + + // MARK: - Properties - //MARK: - Properties var currentAnomaly: Anomalie? weak var closeDelegate: CloseDelegate? - + @IBOutlet weak var greyView: UIView! + var confirmAction: UIAlertAction? - var status : ThanksAnomalyStatus = .showMail + var status: ThanksAnomalyStatus = .showMail var typeContribution: TypeContribution = .outdoor - var timer : Timer = Timer() + var timer: Timer = .init() var timerInterval = 90 // Interval de 90 secondes - - //MARK: - IBoutlets + private var authState: OIDAuthState? + typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + let redirectURI: String = Constants.Authentification.RedirectURI + let authStateKey: String = "authState" + + // MARK: - IBoutlets - @IBOutlet weak var greyView: UIView! - @IBOutlet weak var checkImage: UIImageView! - @IBOutlet weak var thanksLabel: UILabel! - @IBOutlet weak var subtitleLabel: UILabel! - @IBOutlet weak var myAccountButton: UIButton! - @IBOutlet weak var infoLabel: UILabel! - @IBOutlet weak var mailButton: UIButton! - @IBOutlet weak var infoBisLabel: UILabel! - @IBOutlet weak var stayTunedLabel: UILabel! - @IBOutlet weak var closeButton: UIButton! - @IBOutlet weak var connectLabel: UILabel! - @IBOutlet weak var closeButtonMail: UIButton! - + @IBOutlet var checkImage: UIImageView! + @IBOutlet var thanksLabel: UILabel! + @IBOutlet var subtitleLabel: UILabel! + @IBOutlet var closeButton: UIButton! - //MARK: - IBactions + // MARK: - IBactions + @IBAction func closeModal(_ sender: Any) { - if self.status == .showMail { + if status == .showMail { // Cas de la demande de mail. On sauvegarde l'anomalie et on ferme la view - self.saveAndClose() + saveAndClose() } else { // Cas du message de remerciement. On ferme la view et on revient sur la map - self.dismiss(animated: true) - self.closeDelegate?.displayMap() + dismiss(animated: true) + closeDelegate?.displayMap() } } + + // MARK: - View lifecycle - //Connection de l'utilisateur via son compte Parisien - @IBAction func connectToCp(_ sender: Any) { - - let completionHandler: (CompteParisienViewController) -> Void = { - controller in - - if User.shared.isLogged { - self.greyView.isHidden = true - self.currentAnomaly?.mailUser = User.shared.email! - - self.saveAndClose() - } + override func viewDidLoad() { + super.viewDidLoad() + if status == .showThanks { + let originalImage = UIImage(named: Constants.Image.iconExit) + let tintedImage = originalImage?.withRenderingMode(.alwaysTemplate) + closeButton.setImage(tintedImage, for: .normal) + closeButton.tintColor = UIColor.greyDmr() + } else if status == .saveIncident { + saveAndClose() } - - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) as! CompteParisienViewController - compteParisienVC.completionHandler = completionHandler + } + + // MARK: - Other function - self.present(compteParisienVC, animated: true, completion: nil) - self.navigationController?.navigationBar.isTranslucent = false + @objc func textFieldDidChange(textToCheck: UITextField) { + if !textToCheck.text!.isValidEmail() { + confirmAction?.isEnabled = false + } else { + confirmAction?.isEnabled = true + } + } + + func connectToMonParis() { + let authorizationEndpoint = Constants.Authentification.authorizationEndpoint + let tokenEndpoint = Constants.Authentification.tokenEndpoint + let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + doAuthWithAutoCodeExchange(configuration: configuration, + clientID: Constants.Authentification.clientID, + clientSecret: nil) } + + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: redirectURI) else { + print("Error creating URL for : \(redirectURI)") + return + } - //Connection de l'utilisateur via son adresse mail - @IBAction func enterMail(_ sender: Any) { - let alertController = UIAlertController(title: Constants.AlertBoxTitle.restezInforme, message: "", preferredStyle: .alert) - - self.confirmAction = UIAlertAction(title: Constants.AlertBoxTitle.valider, style: .default) { (_) in - let field = alertController.textFields![0] - if (field.text?.isValidEmail())! { - let email = field.text - UserDefaults.standard.set(email, forKey: "userEmail") - UserDefaults.standard.synchronize() - self.currentAnomaly?.mailUser = email! - self.saveAndClose() - } else { - field.layer.borderColor = UIColor.red.cgColor - - } + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + print("Error accessing AppDelegate") + return } - - let cancelAction = UIAlertAction(title: Constants.AlertBoxTitle.annuler, style: .cancel) { (_) in } - alertController.view.tintColor = UIColor.pinkDmr() - alertController.addTextField { (textField) in - textField.placeholder = Constants.PlaceHolder.email - textField.text = UserDefaults.standard.string(forKey: "userEmail") - textField.keyboardType = .emailAddress - textField.addTarget(self, action: #selector(self.textFieldDidChange), for: .editingChanged) + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + self.userInfo() + } else { + print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } } - - alertController.addAction(confirmAction!) - alertController.addAction(cancelAction) - - self.present(alertController, animated: true, completion: nil) - } - - //MARK: - View lifecycle - override func viewDidLoad() { - super.viewDidLoad() - - if status == .showMail { - //Ajustement de la police selon le device - let modelResolution = UIDevice.current.deviceResolution - if modelResolution[0] == "640" { - infoBisLabel.font = UIFont(name: infoBisLabel.font.fontName, size: 13) - infoLabel.font = UIFont(name: infoLabel.font.fontName, size: 13) + func userInfo() { + let userInfoEndpoint = Constants.Authentification.userInfoEndpoint + print("Performing userinfo request") + let currentAccessToken: String? = authState?.lastTokenResponse?.accessToken + + authState?.performAction { accessToken, _, error in + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return } - - let originalImage = UIImage(named: Constants.Image.iconExit) - let tintedImage = originalImage?.withRenderingMode(.alwaysTemplate) - closeButtonMail.setImage(tintedImage, for: .normal) - closeButtonMail.tintColor = UIColor.greyDmr() - } else if status == .showThanks { - let originalImage = UIImage(named: Constants.Image.iconExit) - let tintedImage = originalImage?.withRenderingMode(.alwaysTemplate) - closeButton.setImage(tintedImage, for: .normal) - closeButton.tintColor = UIColor.greyDmr() - } else if status == .saveIncident { - self.saveAndClose() + + guard let accessToken = accessToken else { + print("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + print("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + print("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userInfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + print("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + print("Non-HTTP response") + return + } + + guard let data = data else { + print("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + print("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + print("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json as? [String: Any], let uid = json["uid"] as? String, let validatedAccount = json["validatedAccount"] as? String { + print("Success: \(json)") + + if validatedAccount != "true" { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) + + let okAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default) { _ in + // nothing + } + + alertController.addAction(okAction) + + self.present(alertController, animated: true, completion: nil) + } else { + User.shared.uid = uid + UserDefaults.standard.set(uid, forKey: Constants.Key.uid) + RestApiManager.sharedInstance.getIdentityStore(guid: User.shared.uid!) { + (_: Bool) in + if User.shared.isLogged { + self.currentAnomaly?.mailUser = User.shared.email! + self.saveAndClose() + } + } + } + } + } + } + + task.resume() } } - - //MARK: - Other function - @objc func textFieldDidChange (textToCheck: UITextField) { - if !textToCheck.text!.isValidEmail() { - self.confirmAction?.isEnabled = false - } else { - self.confirmAction?.isEnabled = true + func setAuthState(_ authState: OIDAuthState?) { + if self.authState == authState { + return } - } + self.authState = authState + self.authState?.stateChangeDelegate = self + var data: Data? = nil - func saveAndClose() { + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + if let userDefaults = UserDefaults(suiteName: Constants.Authentification.userDefault) { + userDefaults.set(data, forKey: authStateKey) + userDefaults.synchronize() + } + } + + func saveAndClose() { // Start the timer - self.timer = Timer.scheduledTimer(timeInterval: TimeInterval(self.timerInterval), target: self, selector: #selector(ThanksAnomalyViewController.closeForTimeout), userInfo: nil, repeats: true) + timer = Timer.scheduledTimer(timeInterval: TimeInterval(timerInterval), target: self, selector: #selector(ThanksAnomalyViewController.closeForTimeout), userInfo: nil, repeats: true) DispatchQueue.main.async { SaveAnomalyActivityIndicator.shared.showOverlay(self.view, self.greyView.frame) } - - if (Reach().connectionStatus()) { - - DispatchQueue.global().async { - //Envoie de la requete si l'utilisateur se connecte avec son compte parisien ou entre son adresse mail. - RestApiManager.sharedInstance.saveIncident(anomalie: self.currentAnomaly!) { (result: Bool) in + + DispatchQueue.global().async { + // Envoie de la requete si l'utilisateur se connecte avec son compte parisien ou entre son adresse mail. + RestApiManager.sharedInstance.saveIncident(anomalie: self.currentAnomaly!) { (result: Bool) in + + if result { + print("Enregistrement des photos pour l'incident \(result)") - if result { - print("Enregistrement des photos pour l'incident \(result)") - - DispatchQueue.main.async { - SaveAnomalyActivityIndicator.shared.hideOverlayView() - self.dismiss(animated: true) - self.closeDelegate?.displayThanks() - } - } else { - print("Erreur sur l'enregistrement de l'incident") - self.closeForTimeout() + DispatchQueue.main.async { + SaveAnomalyActivityIndicator.shared.hideOverlayView() + self.dismiss(animated: true) + self.closeDelegate?.displayThanks() } + } else { + print("Erreur sur l'enregistrement de l'incident") + self.closeForTimeout() } } - } else { - let snackbar = TTGSnackbar.init(message:Constants.AlertBoxMessage.noConnexion, duration: .middle) - - snackbar.actionTextColor = UIColor.pinkDmr() - snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! - snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 15.0)! - snackbar.messageTextAlign = NSTextAlignment.center - - snackbar.show() - } - + } } @objc func closeForTimeout() { - self.timer.invalidate() + timer.invalidate() - //message alerte + // message alerte let alertController = UIAlertController(title: Constants.AlertBoxTitle.information, message: Constants.AlertBoxMessage.errorSaveLabel, preferredStyle: .alert) // Create OK button let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { - (action:UIAlertAction!) in + (_: UIAlertAction!) in SaveAnomalyActivityIndicator.shared.hideOverlayView() self.currentAnomaly?.anomalieStatus = .Brouillon @@ -218,30 +301,28 @@ class ThanksAnomalyViewController: UIViewController { alertController.addAction(OKAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) - + present(alertController, animated: true, completion: nil) } } class SaveAnomalyActivityIndicator: UIActivityIndicatorView { - var overlayView = UIView() var backView = UIView() var activityIndicator = UIActivityIndicatorView() class var shared: SaveAnomalyActivityIndicator { - struct Static { - static let instance: SaveAnomalyActivityIndicator = SaveAnomalyActivityIndicator() + enum Static { + static let instance: SaveAnomalyActivityIndicator = .init() } return Static.instance } open func showOverlay(_ view: UIView, _ frame: CGRect) { - overlayView.frame = CGRect(x: 0, y: 0, width: frame.width, height: (frame.height + 25)) - backView.frame = CGRect(x: 0, y: 0, width: view.frame.width , height: view.frame.height) + overlayView.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height + 25) + backView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height) backView.center = view.center - let white = UIColor ( red: 1/255, green: 0/255, blue:0/255, alpha: 0.0 ) + let white = UIColor(red: 1 / 255, green: 0 / 255, blue: 0 / 255, alpha: 0.0) backView.backgroundColor = white view.addSubview(backView) @@ -249,7 +330,7 @@ class SaveAnomalyActivityIndicator: UIActivityIndicatorView { overlayView.center = view.center overlayView.backgroundColor = UIColor.white overlayView.clipsToBounds = true - //overlayView.layer.cornerRadius = 10 + // overlayView.layer.cornerRadius = 10 // Create wait label let waitLabel = UILabel() @@ -258,12 +339,12 @@ class SaveAnomalyActivityIndicator: UIActivityIndicatorView { waitLabel.numberOfLines = 2 waitLabel.lineBreakMode = .byWordWrapping waitLabel.textColor = .black - waitLabel.font = UIFont.init(name: Constants.fontDmr, size: 16) + waitLabel.font = UIFont(name: Constants.fontDmr, size: 16) waitLabel.textAlignment = .center - waitLabel.tag=100 + waitLabel.tag = 100 activityIndicator.frame = CGRect(x: 0, y: 0, width: 100, height: 100) - //activityIndicator.center = overlayView.center + // activityIndicator.center = overlayView.center activityIndicator.transform = CGAffineTransform(scaleX: 3, y: 3) activityIndicator.style = .white activityIndicator.color = UIColor.pinkDmr() @@ -273,7 +354,6 @@ class SaveAnomalyActivityIndicator: UIActivityIndicatorView { overlayView.addSubview(activityIndicator) view.addSubview(overlayView) activityIndicator.startAnimating() - } open func hideOverlayView() { @@ -286,4 +366,3 @@ class SaveAnomalyActivityIndicator: UIActivityIndicatorView { } } } - diff --git a/DansMaRue/Controllers/Anomalie/TypeAnomalieViewController.swift b/DansMaRue/Controllers/Anomalie/TypeAnomalieViewController.swift index d97e098..d8848a9 100644 --- a/DansMaRue/Controllers/Anomalie/TypeAnomalieViewController.swift +++ b/DansMaRue/Controllers/Anomalie/TypeAnomalieViewController.swift @@ -6,31 +6,43 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit import SwiftyJSON +import UIKit class TypeAnomalieViewController: UIViewController { + // MARK: Properties - //MARK: Properties var types = [TypeAnomalie]() var typesSearch = [TypeAnomalie]() var typeAnomalie = TypeAnomalie() weak var delegate: AddAnomalyViewController! - var searching:Bool? = false - - //MARK: IBOutlet - @IBOutlet weak var tableView: UITableView! - @IBOutlet weak var searchBar: UISearchBar! + var searching: Bool? = false + // MARK: IBOutlet + + // @IBOutlet var searchTextField: FloatingLabelInput! + @IBOutlet var searchTextField: UITextField! + @IBOutlet var tableView: UITableView! + @IBOutlet var titleFloatingLabel: UILabel! + // @IBOutlet var searchBar: UISearchBar! override func viewDidLoad() { super.viewDidLoad() - + view.backgroundColor = UIColor.pinkDmr() tableView.tableFooterView = UIView() - - self.navigationItem.title = Constants.LabelMessage.type - //self.navigationItem.backBarButtonItem?.title = "" + titleFloatingLabel.adjustsFontForContentSizeCategory = true + titleFloatingLabel.accessibilityTraits = .staticText + titleFloatingLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 12.0) + titleFloatingLabel.text = Constants.PlaceHolder.searchType + titleFloatingLabel.isHidden = true + navigationItem.title = Constants.AccessibilityLabel.typeTitle + navigationItem.isAccessibilityElement = true + navigationItem.titleView?.isAccessibilityElement = true navigationItem.leftBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(backAction)) + navigationItem.leftBarButtonItem?.accessibilityTraits = .button + navigationItem.leftBarButtonItem?.accessibilityLabel = Constants.AccessibilityLabel.backButton + navigationItem.rightBarButtonItem?.accessibilityLabel = Constants.AccessibilityLabel.favoriteTypesButton + navigationItem.rightBarButtonItem?.accessibilityTraits = .button if let image = UIImage(named: Constants.Image.iconBack) { navigationItem.leftBarButtonItem?.image = image } @@ -41,10 +53,24 @@ class TypeAnomalieViewController: UIViewController { @IBAction func openFavorites(_ sender: Any) { let typeVC = UIStoryboard(name: Constants.StoryBoard.manageFavorites, bundle: nil).instantiateInitialViewController() as! ManageFavoritesViewController typeVC.delegate = self - self.navigationController?.pushViewController(typeVC, animated: true) + navigationController?.pushViewController(typeVC, animated: true) } - //MARK: Other Methods + override func viewDidAppear(_ animated: Bool) { + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + searchTextField.isAccessibilityElement = true + searchTextField.delegate = self + searchTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), + for: .editingChanged) + + searchTextField.placeholder = Constants.PlaceHolder.searchType + searchTextField.accessibilityLabel = Constants.PlaceHolder.searchType + searchTextField.accessibilityTraits = .searchField + searchTextField.accessibilityHint = Constants.AccessibilityHint.searchBarTypeHint + } + + // MARK: Other Methods + private func loadRootTypes() { if delegate.typeContribution == .indoor { reloadData(childrens: (ContextManager.shared.typeEquipementSelected?.categoriesAnomaliesId)!) @@ -55,6 +81,11 @@ class TypeAnomalieViewController: UIViewController { } } + private func scrollToFirstRow() { + let indexPath = IndexPath(row: 0, section: 0) + tableView.scrollToRow(at: indexPath, at: .top, animated: true) + } + func reloadData(childrens: [String]) { types.removeAll() @@ -67,23 +98,24 @@ class TypeAnomalieViewController: UIViewController { } } else { for childrenId in childrens { - if let type = ReferalManager.shared.getTypeAnomalie(withId: childrenId) { + if let type = ReferalManager.shared.getTypeAnomalie(withId: childrenId) { // Vérification des catégories destinées aux agents let isAgent = User.shared.isAgent - if ( type.isAgent && (isAgent != nil && isAgent!) ) { + if type.isAgent && (isAgent != nil && isAgent!) { types.append(type) } else if !type.isAgent { types.append(type) } - } } } tableView.reloadData() + scrollToFirstRow() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } - @objc func backAction(){ + @objc func backAction() { if typeAnomalie.parentId.isEmpty { _ = navigationController?.popViewController(animated: true) } else if typeAnomalie.isRootCategorie { @@ -93,29 +125,49 @@ class TypeAnomalieViewController: UIViewController { label.backgroundColor = .clear label.numberOfLines = 0 label.textColor = .white - label.text = Constants.LabelMessage.type + label.text = Constants.LabelMessage.typeBackButton + label.isAccessibilityElement = true + label.accessibilityLabel = Constants.LabelMessage.typeBackButton + label.accessibilityTraits = .header label.font = UIFont.boldSystemFont(ofSize: 16.0) - self.navigationItem.titleView = label + navigationItem.titleView = label } else { if delegate.typeContribution == .indoor { guard let typeEquipementId = ContextManager.shared.typeEquipementSelected?.typeEquipementId else { return } - if let type = ReferalManager.shared.getTypeAnomalie(forTypeEquipementId: typeEquipementId, catagorieId: typeAnomalie.parentId) { + if let type = ReferalManager.shared.getTypeAnomalie(forTypeEquipementId: typeEquipementId, catagorieId: typeAnomalie.parentId) { reloadData(childrens: type.childrensId) typeAnomalie = type - self.navigationItem.title = type.name + navigationItem.title = type.name } } else { - if let type = ReferalManager.shared.getTypeAnomalie(withId: typeAnomalie.parentId) { + if let type = ReferalManager.shared.getTypeAnomalie(withId: typeAnomalie.parentId) { reloadData(childrens: type.childrensId) typeAnomalie = type - self.navigationItem.title = type.name + navigationItem.title = type.name } } + } + } + + @objc func textFieldDidChange(_ textField: UITextField) { + guard let text = textField.text else { return } + if text.count >= 3 { + searching = true + + typesSearch.removeAll() + let typesSelected = ReferalManager.shared.getAnomalieThatContainsText(type: text) + if typesSelected != nil { + typesSearch = typesSelected! + } + tableView.reloadData() + } else { + searching = false + tableView.reloadData() } } - - func changeTypeAnomalie(newType:TypeAnomalie) { + + func changeTypeAnomalie(newType: TypeAnomalie) { delegate.changeTypeAnomalie(newType: newType) _ = navigationController?.popViewController(animated: true) } @@ -130,17 +182,17 @@ extension TypeAnomalieViewController: UITableViewDelegate { } if typeAnomalie.childrensId.isEmpty { - //Si type d'ano avec message + // Si type d'ano avec message if typeAnomalie.messageBO != "" { let messageTypeAnoStoryboard = UIStoryboard(name: Constants.StoryBoard.messageTypeAno, bundle: nil) let messageTypeAnoVC = messageTypeAnoStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.messageTypeAno) as! MessageTypeAnoViewController - //Passage du type d'anomalie au controller + // Passage du type d'anomalie au controller messageTypeAnoVC.typeAnomalie = typeAnomalie - self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) - self.navigationController?.pushViewController(messageTypeAnoVC, animated: true) + navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) + navigationController?.pushViewController(messageTypeAnoVC, animated: true) } else { - //Sinon selection du type d'ano + // Sinon selection du type d'ano delegate.changeTypeAnomalie(newType: typeAnomalie) _ = navigationController?.popViewController(animated: true) } @@ -151,10 +203,9 @@ extension TypeAnomalieViewController: UITableViewDelegate { label.textColor = .white label.text = typeAnomalie.name label.font = UIFont.boldSystemFont(ofSize: 16.0) - self.navigationItem.titleView = label - self.reloadData(childrens: typeAnomalie.childrensId) + navigationItem.titleView = label + reloadData(childrens: typeAnomalie.childrensId) } - } } @@ -182,8 +233,7 @@ extension TypeAnomalieViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if !searching! { return types.count - } - else { + } else { return typesSearch.count } } @@ -191,9 +241,16 @@ extension TypeAnomalieViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cellIdentifier = "TypeTableViewCell" - guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TypeTableViewCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TypeTableViewCell else { fatalError("The dequeued cell is not an instance of TypeTableViewCell.") } + cell.typeFavorite.isAccessibilityElement = true + cell.typeFavorite.accessibilityTraits = .button + + cell.typeLabel.isAccessibilityElement = true + cell.typeLabel.accessibilityTraits = .staticText + cell.typeLabel.adjustsFontForContentSizeCategory = true + cell.typeLabel.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 14.0) if !searching! { let typeAnomalie = types[indexPath.row] @@ -201,13 +258,14 @@ extension TypeAnomalieViewController: UITableViewDataSource { cell.typeLabel.text = typeAnomalie.name cell.typeLabel.lineBreakMode = .byWordWrapping cell.typeLabel.numberOfLines = 0 - cell.typeImage.image = typeAnomalie.image + cell.typeImage.isAccessibilityElement = false + cell.typeImage.accessibilityTraits = .image - //Gestion des favoris si on est sur le dernier niveau d'ano + // Gestion des favoris si on est sur le dernier niveau d'ano if typeAnomalie.childrensId.isEmpty { - //Check si le type est enregistré en favoris - var favorite : [String] = [] + // Check si le type est enregistré en favoris + var favorite: [String] = [] let defaults = UserDefaults.standard // Tap gesture @@ -218,45 +276,79 @@ extension TypeAnomalieViewController: UITableViewDataSource { favorite = favoritesArray } - //Le type est déjà dans les favoris -> suppression - if (favorite.contains(typeAnomalie.categorieId)) { - cell.typeFavorite.image = UIImage(named:Constants.Image.favoriteCheck) + // Le type est déjà dans les favoris -> suppression + if favorite.contains(typeAnomalie.categorieId) { + cell.typeFavorite.image = UIImage(named: Constants.Image.favoriteCheck) + cell.typeFavorite.accessibilityLabel = String(format: Constants.LabelMessage.removeTypeFavorite, typeAnomalie.name) recognizer.addFavorite = false } else { - //Ajout au favoris - cell.typeFavorite.image = UIImage(named:Constants.Image.favoriteUncheck) + // Ajout au favoris + cell.typeFavorite.image = UIImage(named: Constants.Image.favoriteUncheck) + cell.typeFavorite.accessibilityLabel = String(format: Constants.LabelMessage.addTypeFavorite, typeAnomalie.name) recognizer.addFavorite = true } // Add tap gesture recognizer to favorite image cell.typeFavorite.addGestureRecognizer(recognizer) } else { cell.typeFavorite.image = nil + cell.typeFavorite.isAccessibilityElement = false } } else { let typeAnomalie = typesSearch[indexPath.row] cell.typeLabel.text = typeAnomalie.name + cell.typeLabel.accessibilityLabel = typeAnomalie.name cell.typeLabel.lineBreakMode = .byWordWrapping cell.typeLabel.numberOfLines = 0 cell.typeFavorite.image = nil cell.typeImage.image = nil + cell.typeFavorite.isAccessibilityElement = false + + // Gestion des favoris si on est sur le dernier niveau d'ano + if typeAnomalie.childrensId.isEmpty { + // Check si le type est enregistré en favoris + var favorite: [String] = [] + let defaults = UserDefaults.standard + + // Tap gesture + let recognizer = MyTapGesture(target: self, action: #selector(TypeAnomalieViewController.addOrRemoveFavorite(recognizer:))) + recognizer.categorieId = typeAnomalie.categorieId + + if let favoritesArray = defaults.stringArray(forKey: "favoritesArray") { + favorite = favoritesArray + } + + // Le type est déjà dans les favoris -> suppression + if favorite.contains(typeAnomalie.categorieId) { + cell.typeFavorite.image = UIImage(named: Constants.Image.favoriteCheck) + cell.typeFavorite.accessibilityLabel = Constants.LabelMessage.removeTypeFavorite + recognizer.addFavorite = false + } else { + // Ajout au favoris + cell.typeFavorite.image = UIImage(named: Constants.Image.favoriteUncheck) + cell.typeFavorite.accessibilityLabel = Constants.LabelMessage.addTypeFavorite + recognizer.addFavorite = true + } + // Add tap gesture recognizer to favorite image + cell.typeFavorite.addGestureRecognizer(recognizer) + } } return cell } - //Add/remove favorite + // Add/remove favorite @objc func addOrRemoveFavorite(recognizer: MyTapGesture) { - var favorite : [String] = [] + var favorite: [String] = [] let defaults = UserDefaults.standard if let favoritesArray = defaults.stringArray(forKey: "favoritesArray") { favorite = favoritesArray } - if(recognizer.addFavorite) { - //Ajout du favoris + if recognizer.addFavorite { + // Ajout du favoris favorite.append(recognizer.categorieId) } else { - //Suppression du favoris + // Suppression du favoris if let index = favorite.index(of: recognizer.categorieId) { favorite.remove(at: index) } @@ -265,33 +357,24 @@ extension TypeAnomalieViewController: UITableViewDataSource { tableView.reloadData() } - //Class tapeGesture perso pour envoyre l'id en param + // Class tapeGesture perso pour envoyre l'id en param class MyTapGesture: UITapGestureRecognizer { var categorieId = String() var addFavorite = Bool() } - } -extension TypeAnomalieViewController : UISearchBarDelegate{ - func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { - - if searchText.count >= 3 - { - searching = true - - typesSearch.removeAll() - - let typesSelected = ReferalManager.shared.getAnomalieThatContainsText(type: searchText) - if typesSelected != nil { - typesSearch = typesSelected! - } - tableView.reloadData() - } else{ - searching = false - tableView.reloadData() - } - + +extension TypeAnomalieViewController: UITextFieldDelegate { + func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + UIView.animate(withDuration: 0.25, animations: { () in + self.titleFloatingLabel.isHidden = false + }) + return true } - + func textFieldDidEndEditing(_ textField: UITextField) { + UIView.animate(withDuration: 0.25, animations: { () in + self.titleFloatingLabel.isHidden = true + }) + } } diff --git a/DansMaRue/Controllers/CustomUIAlertController.swift b/DansMaRue/Controllers/CustomUIAlertController.swift new file mode 100644 index 0000000..08eb86b --- /dev/null +++ b/DansMaRue/Controllers/CustomUIAlertController.swift @@ -0,0 +1,114 @@ +// +// CustomUIAlertController.swift +// DansMaRue +// +// Created by alaeddine.oueslati on 22/09/2023. +// Copyright © 2023 VilleDeParis. All rights reserved. +// + +import UIKit + +class CustomUIAlertController: UIViewController { + @IBOutlet var titleLabel: UILabel! + @IBOutlet var messageLabel: UILabel! + @IBOutlet var inputTextField: FloatingLabelInput! + @IBOutlet var searchButton: UIButton! + @IBOutlet var cancelButton: UIButton! + @IBOutlet var alertView: UIView! + + private var titleText: String? + private var messageText: String? + private var placeHolderText: String? + + var delegate: CustomAlertViewDelegate? + let alertViewGrayColor = UIColor(red: 224.0/255.0, green: 224.0/255.0, blue: 224.0/255.0, alpha: 1) + + override func viewDidLoad() { + super.viewDidLoad() + // inputTextField.becomeFirstResponder() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + updateAlertView() + setupView() + animateView() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + view.setNeedsLayout() + // view.layoutIfNeeded() + /* cancelButton.addBorder(side: .Top, color: alertViewGrayColor, width: 1) + cancelButton.addBorder(side: .Right, color: alertViewGrayColor, width: 1) + searchButton.addBorder(side: .Top, color: alertViewGrayColor, width: 1) */ + } + + @IBAction func onTapSearchButton(_ sender: Any) { + inputTextField.resignFirstResponder() + dismiss(animated: true, completion: nil) + delegate?.okButtonTapped(textFieldValue: inputTextField.text!) + } + + @IBAction func onTapCancelButton(_ sender: Any) { + inputTextField.resignFirstResponder() + dismiss(animated: true, completion: nil) + delegate?.cancelButtonTapped() + dismissAlertView() + } + + func configureText(title: String, message: String, placeHolder: String) { + titleText = title + messageText = message + placeHolderText = placeHolder + } + + private func updateAlertView() { + titleLabel.text = titleText + messageLabel.text = messageText + inputTextField.placeholder = placeHolderText + } + + private func setupView() { + alertView.layer.cornerRadius = 15 + view.backgroundColor = UIColor.black.withAlphaComponent(0.4) + + titleLabel.isAccessibilityElement = true + messageLabel.isAccessibilityElement = true + inputTextField.isAccessibilityElement = true + + titleLabel.accessibilityTraits = .header + messageLabel.accessibilityTraits = .staticText + inputTextField.accessibilityTraits = .searchField + + titleLabel.numberOfLines = 0 + titleLabel.adjustsFontForContentSizeCategory = true + titleLabel.font = UIFont.preferredFont(forTextStyle: .title3) + messageLabel.numberOfLines = 0 + messageLabel.adjustsFontForContentSizeCategory = true + messageLabel.font = UIFont.preferredFont(forTextStyle: .subheadline) + } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } + + private func animateView() { + alertView.alpha = 0 + alertView.frame.origin.y = alertView.frame.origin.y + 50 + UIView.animate(withDuration: 0.4, animations: { () in + self.alertView.alpha = 1.0 + self.alertView.frame.origin.y = self.alertView.frame.origin.y - 50 + }) + } + + private func dismissAlertView() { + view.removeFromSuperview() + } +} + +protocol CustomAlertViewDelegate: class { + func okButtonTapped(textFieldValue: String) + func cancelButtonTapped() +} diff --git a/DansMaRue/Controllers/MainViewController.swift b/DansMaRue/Controllers/MainViewController.swift index 50d2ec0..590421f 100644 --- a/DansMaRue/Controllers/MainViewController.swift +++ b/DansMaRue/Controllers/MainViewController.swift @@ -6,21 +6,21 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit import SwiftyJSON -//import AdtagConnection +import UIKit +// import AdtagConnection class MainViewController: UITabBarController { - - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - //Vérification accessibilité BO - RestApiManager.sharedInstance.isDMROnline { (isDMROnline) in + // Vérification accessibilité BO + RestApiManager.sharedInstance.isDMROnline { isDMROnline in if !isDMROnline { let alert = UIAlertController(title: Constants.AlertBoxTitle.information, message: Constants.AlertBoxMessage.maintenance, preferredStyle: UIAlertController.Style.alert) - let okBtn = UIAlertAction(title:"Ok" , style: .default, handler: {(_ action: UIAlertAction) -> Void in + let okBtn = UIAlertAction(title: "Ok", style: .default, handler: { (_: UIAlertAction) in }) alert.addAction(okBtn) self.present(alert, animated: true, completion: nil) @@ -28,33 +28,34 @@ class MainViewController: UITabBarController { } configureTabBarItems() - //Customisation de la bar de naviguation + // Customisation de la bar de naviguation UINavigationBar.appearance().tintColor = .white UINavigationBar.appearance().barTintColor = UIColor.pinkDmr() - UINavigationBar.appearance().titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white]) + UINavigationBar.appearance().titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white]) UITabBar.appearance().tintColor = UIColor.pinkButtonDmr() + tabBarController?.tabBar.selectionIndicatorImage = UIImage().createSelectionIndicator(color: UIColor.pinkDmr(), size: CGSize(width: tabBar.frame.width / CGFloat(tabBar.items!.count), height: tabBar.frame.height), lineHeight: 2.0) - //Chargement des catégories/Types d'anomalies outdoor + // Chargement des catégories/Types d'anomalies outdoor getCategories() - //Chargement des types équipements et équipements + // Chargement des types équipements et équipements getEquipements() - //Chargement des actualites + // Chargement des actualites getActualites() - //Chargement des aides + // Chargement des aides getAides() - //Ckeck si une MAJ est disponible + // Ckeck si une MAJ est disponible isLatestVersion() // Authentification automatique de l'utilisateur User.shared.automaticAuthentification() let nc = NotificationCenter.default - nc.addObserver(forName:Notification.Name(rawValue: Constants.NoticationKey.pushNotification), object:nil, queue:nil, using:displayProfil) + nc.addObserver(forName: Notification.Name(rawValue: Constants.NoticationKey.pushNotification), object: nil, queue: nil, using: displayProfil) } override func viewDidAppear(_ animated: Bool) { @@ -63,24 +64,23 @@ class MainViewController: UITabBarController { let hasAlreadyBeenConnected = UserDefaults.standard.bool(forKey: "hasAlreadyBeenConnected") if !hasAlreadyBeenConnected { - - //Redirect to walkthrough view + // Redirect to walkthrough view let welcomeStoryboard = UIStoryboard(name: Constants.StoryBoard.welcome, bundle: nil) let welcomeViewController = welcomeStoryboard.instantiateViewController(withIdentifier: "WelcomeViewController") as! WelcomeViewController welcomeViewController.modalPresentationStyle = .fullScreen - self.navigationController?.addChild(welcomeViewController) - self.present(welcomeViewController, animated: true, completion: nil) - + navigationController?.addChild(welcomeViewController) + present(welcomeViewController, animated: true, completion: nil) } - //showOptinPopUp() + // showOptinPopUp() } // MARK: - Other Methods + func configureTabBarItems() { var newMenuArray: [UIViewController] = [] - let menuArray = self.customizableViewControllers + let menuArray = customizableViewControllers let menuMap = menuArray![0] let menuProfile = menuArray![1] @@ -93,25 +93,24 @@ class MainViewController: UITabBarController { menuProfile.tabBarItem.image = UIImage(named: "profil_menu_selected") menuProfile.tabBarItem.title = "Mon espace" - self.setViewControllers(newMenuArray, animated: true) + setViewControllers(newMenuArray, animated: true) } - - func displayProfil(notification:Notification) { + func displayProfil(notification: Notification) { // Réception d'une notification push pour afficher le profil de l'utilisateur if notification.object as? Anomalie == nil { - self.selectedIndex = 1 + selectedIndex = 1 } } func isLatestVersion() { - VersionsUtils().isLatestVersion(onCompletion: { isUpdateDispo, err in - if(isUpdateDispo) { - //Une MAJ est disponible + VersionsUtils().isLatestVersion(onCompletion: { isUpdateDispo, _ in + if isUpdateDispo { + // Une MAJ est disponible print("update disponible") - //On vérifie si la MAJ est obligatoire - VersionsUtils().isMAJObligatoire(onCompletion: { isMAJObligatoire, err in + // On vérifie si la MAJ est obligatoire + VersionsUtils().isMAJObligatoire(onCompletion: { isMAJObligatoire, _ in if isMAJObligatoire { print("update obligatoire") self.popupUpdateObligatoireDialogue() @@ -124,13 +123,14 @@ class MainViewController: UITabBarController { }) } - private func popupUpdateDialogue(){ + private func popupUpdateDialogue() { let alertMessage = Constants.AlertBoxMessage.majDisponible let alert = UIAlertController(title: "Nouvelle version disponible", message: alertMessage, preferredStyle: UIAlertController.Style.alert) - let okBtn = UIAlertAction(title: "Oui", style: .default, handler: {(_ action: UIAlertAction) -> Void in + let okBtn = UIAlertAction(title: "Oui", style: .default, handler: { (_: UIAlertAction) in if let url = URL(string: "itms-apps://itunes.apple.com/us/app/apple-store/id662045577?mt=8"), - UIApplication.shared.canOpenURL(url){ + UIApplication.shared.canOpenURL(url) + { if #available(iOS 10.0, *) { UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil) } else { @@ -138,20 +138,21 @@ class MainViewController: UITabBarController { } } }) - let noBtn = UIAlertAction(title:"Non" , style: .default, handler: {(_ action: UIAlertAction) -> Void in + let noBtn = UIAlertAction(title: "Non", style: .default, handler: { (_: UIAlertAction) in }) alert.addAction(okBtn) alert.addAction(noBtn) - self.present(alert, animated: true, completion: nil) + present(alert, animated: true, completion: nil) } - private func popupUpdateObligatoireDialogue(){ + private func popupUpdateObligatoireDialogue() { let alertMessage = Constants.AlertBoxMessage.majObligatoire let alert = UIAlertController(title: "Nouvelle version disponible", message: alertMessage, preferredStyle: UIAlertController.Style.alert) - let okBtn = UIAlertAction(title: "Mettre à jour", style: .default, handler: {(_ action: UIAlertAction) -> Void in + let okBtn = UIAlertAction(title: "Mettre à jour", style: .default, handler: { (_: UIAlertAction) in if let url = URL(string: "itms-apps://itunes.apple.com/us/app/apple-store/id662045577?mt=8"), - UIApplication.shared.canOpenURL(url){ + UIApplication.shared.canOpenURL(url) + { if #available(iOS 10.0, *) { UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil) } else { @@ -160,37 +161,33 @@ class MainViewController: UITabBarController { } }) alert.addAction(okBtn) - self.present(alert, animated: true, completion: nil) + present(alert, animated: true, completion: nil) } func getCategories() { DispatchQueue.global().async { - RestApiManager.sharedInstance.getCategories{(result: Bool) in + RestApiManager.sharedInstance.getCategories { (result: Bool) in if result { ReferalManager.shared.loadTypeAnomalie() } else { - //Récupération des anciennes valeurs dans le fichier JSON + // Récupération des anciennes valeurs dans le fichier JSON ReferalManager.shared.loadTypeAnomalie() let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreurChargementTypes, preferredStyle: .alert) // Create try again button - let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in - //self.getCategories() + let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in + // self.getCategories() } alertController.addAction(okButton) - //self.present(alertController, animated: true, completion:nil) - - + // self.present(alertController, animated: true, completion:nil) } - - } } } func getActualites() { DispatchQueue.global().async { - RestApiManager.sharedInstance.getActualites{(result: Bool) in + RestApiManager.sharedInstance.getActualites { (_: Bool) in ReferalManager.shared.loadActualite() } } @@ -198,7 +195,7 @@ class MainViewController: UITabBarController { func getAides() { DispatchQueue.global().async { - RestApiManager.sharedInstance.getAides{(result: Bool) in + RestApiManager.sharedInstance.getAides { (_: Bool) in ReferalManager.shared.loadAides() } } @@ -206,16 +203,16 @@ class MainViewController: UITabBarController { func getEquipements() { DispatchQueue.global().async { - RestApiManager.sharedInstance.getEquipements{(result: Bool) in + RestApiManager.sharedInstance.getEquipements { (result: Bool) in if !result { let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreurChargementEquipement, preferredStyle: .alert) // Create try again button - let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in + let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in self.getEquipements() } alertController.addAction(okButton) - //self.present(alertController, animated: true, completion:nil) + // self.present(alertController, animated: true, completion:nil) } else { ReferalManager.shared.loadTypeEquipementAndEquipements() @@ -228,65 +225,72 @@ class MainViewController: UITabBarController { func getCategoriesEquipements() { DispatchQueue.global().async { - RestApiManager.sharedInstance.getCategoriesEquipements{(result: Bool) in + RestApiManager.sharedInstance.getCategoriesEquipements { (result: Bool) in if result { ReferalManager.shared.loadTypeAnomalieByEquipement() } else { let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreurChargementTypes, preferredStyle: .alert) // Create try again button - let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in + let okButton = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in self.getCategoriesEquipements() } alertController.addAction(okButton) - //self.present(alertController, animated: true, completion:nil) - + // self.present(alertController, animated: true, completion:nil) } - } } } - /*func showOptinPopUp() { - let adtagInitializer = AdtagInitializer.shared - //To know if an optin has been updated - if adtagInitializer.optinsNeverAsked() { - // No update, ask the optin ? - let alertController = UIAlertController(title: Constants.AlertBoxTitle.parametres, message: Constants.AlertBoxMessage.optinAutorisation, preferredStyle: .alert) - // Create Non button - let NonAction = UIAlertAction(title: Constants.AlertBoxTitle.non, style: .default) { (action:UIAlertAction!) in - self.manageOptin(permission: false) - } - alertController.addAction(NonAction) - // Create Oui button - let OuiAction = UIAlertAction(title: Constants.AlertBoxTitle.oui, style: .default) { (action:UIAlertAction!) in - self.manageOptin(permission: true) - } - alertController.addAction(OuiAction) - self.present(alertController, animated: true, completion:nil) - } - } + /* func showOptinPopUp() { + let adtagInitializer = AdtagInitializer.shared + //To know if an optin has been updated + if adtagInitializer.optinsNeverAsked() { + // No update, ask the optin ? + let alertController = UIAlertController(title: Constants.AlertBoxTitle.parametres, message: Constants.AlertBoxMessage.optinAutorisation, preferredStyle: .alert) + // Create Non button + let NonAction = UIAlertAction(title: Constants.AlertBoxTitle.non, style: .default) { (action:UIAlertAction!) in + self.manageOptin(permission: false) + } + alertController.addAction(NonAction) + // Create Oui button + let OuiAction = UIAlertAction(title: Constants.AlertBoxTitle.oui, style: .default) { (action:UIAlertAction!) in + self.manageOptin(permission: true) + } + alertController.addAction(OuiAction) + self.present(alertController, animated: true, completion:nil) + } + } - func manageOptin(permission : Bool) { - let adtagInitializer = AdtagInitializer.shared - //get the optin status - adtagInitializer.isOptinAuthorized(.USER_DATA) - //Update the optin status even if it's false - adtagInitializer.updateOptin(.USER_DATA, permission: permission) - // Notify the SDK that you have finished with the opti-ns update - call it each time the opt-ins are udpated - adtagInitializer.allOptinsAreUpdated() - }*/ + func manageOptin(permission : Bool) { + let adtagInitializer = AdtagInitializer.shared + //get the optin status + adtagInitializer.isOptinAuthorized(.USER_DATA) + //Update the optin status even if it's false + adtagInitializer.updateOptin(.USER_DATA, permission: permission) + // Notify the SDK that you have finished with the opti-ns update - call it each time the opt-ins are udpated + adtagInitializer.allOptinsAreUpdated() + } */ } - - // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { - guard let input = input else { return nil } - return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)}) +private func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { + guard let input = input else { return nil } + return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value) }) } // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] { - return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)}) +private func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] { + return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value) }) +} + +extension UIImage { + func createSelectionIndicator(color: UIColor, size: CGSize, lineHeight: CGFloat) -> UIImage { + UIGraphicsBeginImageContextWithOptions(size, false, 0) + color.setFill() + UIRectFill(CGRect(origin: CGPoint(x: 0, y: size.height - lineHeight), size: CGSize(width: size.width, height: lineHeight))) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image! + } } diff --git a/DansMaRue/Controllers/Map/BottomSheetViewController.swift b/DansMaRue/Controllers/Map/BottomSheetViewController.swift index 75169b7..b0b8bd4 100644 --- a/DansMaRue/Controllers/Map/BottomSheetViewController.swift +++ b/DansMaRue/Controllers/Map/BottomSheetViewController.swift @@ -6,32 +6,36 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit +import AppAuth import GoogleMaps import GooglePlaces import SDWebImage import TTGSnackbar +import UIKit protocol UberDelegate: NSObjectProtocol { func shouldDisplayUberPin(yesWeCan: Bool) } -class BottomSheetViewController: UIViewController, UITextFieldDelegate { +class BottomSheetViewController: UIViewController, UITextFieldDelegate, OIDAuthStateChangeDelegate { + // MARK: - IBOutlets - //MARK: - IBOutlets - @IBOutlet weak var bottomSheetTableView: UITableView! + @IBOutlet var bottomSheetTableView: UITableView! - //MARK: - Properties + // MARK: - Properties + + var showAnomalyWidth: CGFloat = 0 let fullView: CGFloat = 0 var partialView: CGFloat = 600 + var bottomSheetInitialY: CGFloat = 0 weak var uberDelegate: UberDelegate? var uberDisplayed = false - var mapsUtils : MapsUtils? + var mapsUtils: MapsUtils? var selectedAddress: GMSAddress? - var selectedCoordinates : CLLocationCoordinate2D? + var selectedCoordinates: CLLocationCoordinate2D? - var otherCellDisplay:Bool = false - var buttomSheetFullView:Bool = false + var otherCellDisplay: Bool = false + var buttomSheetFullView: Bool = false var selectAnomalie: Anomalie? var selectEquipement: Equipement? { didSet { @@ -45,60 +49,72 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } weak var delegate: MapViewController! - let anomalieNotification = Notification.Name(rawValue:Constants.NoticationKey.anomaliesChanged) + let anomalieNotification = Notification.Name(rawValue: Constants.NoticationKey.anomaliesChanged) - var hidemanageFavoriteBtns = false - var addAnomalyBtn: UIButton? var searchAnomalyBtn: UIButton? var showAnomalyBtn: UIButton? var followAnomalyBtn: UIButton? var unfollowAnomalyBtn: UIButton? var congratulateAnomalyBtn: UIButton? - var addFavoriteBtn: UIButton? - var removeFavoriteBtn: UIButton? + var swipeImageView: UIImageView? let imgAddAnomaly = UIImage(named: Constants.Image.createAnomalie) let imgAddAnomalySelected = UIImage(named: Constants.Image.createAnomalieSelected) let imgSearchAnomalie = UIImage(named: Constants.Image.searchAnomalie) let imgShowAnomalie = UIImage(named: Constants.Image.showAnomalies) - let imgFollowAnomaly = UIImage(named: Constants.Image.follow) - let imgFollowAnomalySelected = UIImage(named: Constants.Image.followSelected) - let imgFollowAnomalyDisabled = UIImage(named: Constants.Image.followDisabled) - let imgUnfollowAnomaly = UIImage(named: Constants.Image.unfollow) - let imgUnfollowAnomalySelected = UIImage(named: Constants.Image.unfollowSelected) - let imgCongratulateAnomaly = UIImage(named: Constants.Image.congratulate) - let imgCongratulateAnomalySelected = UIImage(named: Constants.Image.congratulateSelected) - let imgCongratulateAnomalyDisabled = UIImage(named: Constants.Image.congratulateDisabled) + let imgFollowAnomaly = UIImage(named: Constants.Image.follow) + let imgFollowAnomalySelected = UIImage(named: Constants.Image.followSelected) + let imgFollowAnomalyDisabled = UIImage(named: Constants.Image.followDisabled) + let imgUnfollowAnomaly = UIImage(named: Constants.Image.unfollow) + let imgUnfollowAnomalySelected = UIImage(named: Constants.Image.unfollowSelected) + let imgCongratulateAnomaly = UIImage(named: Constants.Image.congratulate) + let imgCongratulateAnomalySelected = UIImage(named: Constants.Image.congratulateSelected) + let imgCongratulateAnomalyDisabled = UIImage(named: Constants.Image.congratulateDisabled) let imgAddAddressFavorite = UIImage(named: Constants.Image.favoritePlus) let imgRemoveddressFavorite = UIImage(named: Constants.Image.favoriteCheck) var otherMalfunctionsArray = [Anomalie]() var currentStatus: BottomSheetStatus = .none var initFrameHeight: CGFloat = 0.0 + var buttonHeight: CGFloat = 40 + var isFirstFullView = true var isScrollEnable = true - + private var authState: OIDAuthState? + typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + let redirectURI: String = Constants.Authentification.RedirectURI + let authStateKey: String = "authState" - //MARK: - View lifecycle + var showAnomalyInitialX: CGFloat = 0 + var showAnomalyInitialY: CGFloat = 0 + var showAnomalyInitialHeight: CGFloat = 0 + var showAnomalyInitialWidth: CGFloat = 0 + var isAlreadyInFavorites = false + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - - let gesture = UIPanGestureRecognizer.init(target: self, action: #selector(BottomSheetViewController.panGesture)) + bottomSheetTableView.delegate = self + bottomSheetTableView.rowHeight = UITableView.automaticDimension + bottomSheetTableView.estimatedRowHeight = 60 + let gesture = UIPanGestureRecognizer(target: self, action: #selector(BottomSheetViewController.panGesture)) gesture.delegate = self view.addGestureRecognizer(gesture) let nc = NotificationCenter.default - nc.addObserver(forName:NSNotification.Name(rawValue: Constants.NoticationKey.addressNotification), object:nil, queue:nil, using:changeAddress) - nc.addObserver(forName:NSNotification.Name(rawValue: Constants.NoticationKey.anomaliesChanged), object:nil, queue:nil, using:reloadAnomalies) - - nc.addObserver(forName:Notification.Name(rawValue: Constants.NoticationKey.pushNotification), object:nil, queue:nil, using:notificationToDisplayAnomalie) + nc.addObserver(forName: NSNotification.Name(rawValue: Constants.NoticationKey.addressNotification), object: nil, queue: nil, using: changeAddress) + nc.addObserver(forName: NSNotification.Name(rawValue: Constants.NoticationKey.anomaliesChanged), object: nil, queue: nil, using: reloadAnomalies) + nc.addObserver(forName: Notification.Name(rawValue: Constants.NoticationKey.pushNotification), object: nil, queue: nil, using: notificationToDisplayAnomalie) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - + bottomSheetTableView.layoutIfNeeded() + bottomSheetTableView.reloadData() + if buttomSheetFullView { animateBottomSheet(withDuration: 0, status: .full) } else if otherCellDisplay { @@ -106,161 +122,248 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } else { animateBottomSheet(withDuration: 0, status: .none) } - if addAnomalyBtn == nil { - addAnomalyBtn = self.initButton() + addAnomalyBtn = initButton() addAnomalyBtn?.setImage(imgAddAnomaly, for: .normal) - addAnomalyBtn?.addTarget(self, action: #selector(self.tapAddAnomaly(sender:)), for: .touchUpInside) + addAnomalyBtn?.addTarget(self, action: #selector(tapAddAnomaly(sender:)), for: .touchUpInside) addAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.addAnomaly - - self.view.addSubview(addAnomalyBtn!) + addAnomalyBtn?.accessibilityTraits = .button + view.addSubview(addAnomalyBtn!) } if searchAnomalyBtn == nil { - searchAnomalyBtn = self.initSearchButton() + searchAnomalyBtn = initSearchButton() searchAnomalyBtn?.setImage(imgSearchAnomalie, for: .normal) - searchAnomalyBtn?.addTarget(self, action: #selector(self.tapSearchAnomaly(sender:)), for: .touchUpInside) + searchAnomalyBtn?.addTarget(self, action: #selector(tapSearchAnomaly(sender:)), for: .touchUpInside) searchAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.searchAnomaly - - self.view.addSubview(searchAnomalyBtn!) + searchAnomalyBtn?.accessibilityTraits = .button + view.addSubview(searchAnomalyBtn!) } - - if showAnomalyBtn == nil { - showAnomalyBtn = self.initShowButton() - showAnomalyBtn?.setImage(imgShowAnomalie, for: .normal) - showAnomalyBtn?.tag = 55 - //showAnomalyBtn?.addTarget(self, action: #selector(self.tapSearchAnomaly(sender:)), for: .touchUpInside) - showAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.showAnomaly - - self.view.addSubview(showAnomalyBtn!) - } - - if addFavoriteBtn == nil { - addFavoriteBtn = initButtonManageFavorite(isAddFavorite: true) - addFavoriteBtn?.setImage(imgAddAddressFavorite, for: .normal) - addFavoriteBtn?.accessibilityLabel = Constants.LabelMessage.addAdresseFavorite - - let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(self.addOrRemoveFavorite(recognizer:))) - tapGestureRecognizer.addFavorite = true - addFavoriteBtn?.addGestureRecognizer(tapGestureRecognizer) - - self.view.addSubview(addFavoriteBtn!) - } - - if removeFavoriteBtn == nil { - removeFavoriteBtn = initButtonManageFavorite(isAddFavorite: true) - removeFavoriteBtn?.setImage(imgRemoveddressFavorite, for: .normal) - removeFavoriteBtn?.accessibilityLabel = Constants.LabelMessage.removeAdresseFavorite - - let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(self.addOrRemoveFavorite(recognizer:))) - tapGestureRecognizer.addFavorite = false - removeFavoriteBtn?.addGestureRecognizer(tapGestureRecognizer) - - self.view.addSubview(removeFavoriteBtn!) + if swipeImageView == nil { + swipeImageView = initIconButton() + let singleTap = UITapGestureRecognizer(target: self, action: #selector(displayBottomSheet(sender:))) + swipeImageView?.addGestureRecognizer(singleTap) + view.addSubview(swipeImageView!) } - + if followAnomalyBtn == nil { - followAnomalyBtn = self.initButton() + followAnomalyBtn = initButton() followAnomalyBtn?.setImage(imgFollowAnomaly, for: .normal) followAnomalyBtn?.setImage(imgFollowAnomalyDisabled, for: .disabled) - followAnomalyBtn?.addTarget(self, action: #selector(self.tapFollowAnomaly(sender:)), for: .touchUpInside) + followAnomalyBtn?.addTarget(self, action: #selector(tapFollowAnomaly(sender:)), for: .touchUpInside) followAnomalyBtn?.isHidden = true followAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.followAnomaly - - self.view.addSubview(followAnomalyBtn!) + view.addSubview(followAnomalyBtn!) } if unfollowAnomalyBtn == nil { - unfollowAnomalyBtn = self.initButton() + unfollowAnomalyBtn = initButton() unfollowAnomalyBtn?.setImage(imgUnfollowAnomaly, for: .normal) - unfollowAnomalyBtn?.addTarget(self, action: #selector(self.tapUnfollowAnomaly(sender:)), for: .touchUpInside) + unfollowAnomalyBtn?.addTarget(self, action: #selector(tapUnfollowAnomaly(sender:)), for: .touchUpInside) unfollowAnomalyBtn?.isHidden = true unfollowAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.unfollowAnomaly - - self.view.addSubview(unfollowAnomalyBtn!) + view.addSubview(unfollowAnomalyBtn!) } if congratulateAnomalyBtn == nil { - congratulateAnomalyBtn = self.initButton() + congratulateAnomalyBtn = initButton() congratulateAnomalyBtn?.setImage(imgCongratulateAnomaly, for: .normal) congratulateAnomalyBtn?.setImage(imgCongratulateAnomalyDisabled, for: .disabled) - congratulateAnomalyBtn?.addTarget(self, action: #selector(self.tapCongratulateAnomaly(sender:)), for: .touchUpInside) + congratulateAnomalyBtn?.addTarget(self, action: #selector(tapCongratulateAnomaly(sender:)), for: .touchUpInside) congratulateAnomalyBtn?.isHidden = true congratulateAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.congratulateAnomaly - - self.view.addSubview(congratulateAnomalyBtn!) + view.addSubview(congratulateAnomalyBtn!) + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if showAnomalyBtn == nil { + showAnomalyBtn = initShowButton() + showAnomalyBtn?.tag = 55 + showAnomalyBtn?.addTarget(self, action: #selector(displayBottomSheet(sender:)), for: .touchUpInside) + showAnomalyBtn?.accessibilityLabel = Constants.LabelMessage.showAnomaly + showAnomalyBtn?.setTitle(Constants.LabelMessage.showAnomaly, for: .normal) + showAnomalyBtn?.tintColor = .white + showAnomalyBtn?.backgroundColor = UIColor.pinkButtonDmr() + showAnomalyBtn?.accessibilityTraits = .button + applyDynamicType(label: (showAnomalyBtn?.titleLabel!)!, fontName: "Montserrat-Regular", size: 12) + showAnomalyBtn?.sizeToFit() + updateShowButtonFrame() + view.addSubview(showAnomalyBtn!) } + + if swipeImageView == nil { + swipeImageView = initIconButton() + let singleTap = UITapGestureRecognizer(target: self, action: #selector(displayBottomSheet(sender:))) + swipeImageView?.addGestureRecognizer(singleTap) + view.addSubview(swipeImageView!) + } + } + + override func viewDidLayoutSubviews() { + handleOrientationChanged() } - + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + handleOrientationChanged() + } + func initButton() -> UIButton { - let button = UIButton(frame: CGRect(x:self.view.frame.width - 70, y:-30, width:65, height:65)) + let frame = UIDevice.current.orientation.isLandscape ? CGRect(x: view.frame.width - 130, y: -30, width: 65, height: 65) : + CGRect(x: view.frame.width - 70, y: -30, width: 65, height: 65) + let button = UIButton(frame: frame) button.backgroundColor = .black button.layer.borderWidth = 0 button.tintColor = .white button.backgroundColor = .clear - button.imageEdgeInsets = UIEdgeInsets.init(top: -10, left: -10, bottom: -10, right: -10) + button.imageEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) return button } func initSearchButton() -> UIButton { - let button = UIButton(frame: CGRect(x:self.view.frame.width - 70, y:-170, width:65, height:65)) + let frame = UIDevice.current.orientation.isLandscape ? CGRect(x: view.frame.width - 195, y: -85, width: 65, height: 65) : + CGRect(x: view.frame.width - 70, y: -170, width: 65, height: 65) + + let button = UIButton(frame: frame) button.isUserInteractionEnabled = true return button } func initShowButton() -> UIButton { - let frameWidth = self.view.frame.width - let btnWidth = self.view.frame.width/2 - let button = UIButton(frame: CGRect(x:(frameWidth/2-btnWidth/2 ), y:-40, width:btnWidth, height:60)) + let btnWidth = view.frame.width / 2 + let button = UIButton(frame: CGRect(x: view.frame.width / 2 - btnWidth / 2, y: -40, width: btnWidth, height: 40)) + button.isUserInteractionEnabled = true - + button.layer.cornerRadius = buttonHeight / 2 + button.titleLabel?.numberOfLines = 0 + button.titleLabel?.textAlignment = .center + return button } - //MARK: - Bottom sheet methods + func initIconButton() -> UIImageView { + let imageName = "arrow_swipe" + let image = UIImage(named: imageName) + let imageView = UIImageView(image: image!) + imageView.frame = CGRect(x: showAnomalyInitialX + showAnomalyInitialWidth / 2 - 10, y: -buttonHeight - 5, width: 20, height: 20) + imageView.isUserInteractionEnabled = true + return imageView + } + + func isLargerTextEnabled() -> Bool { + let contentSize = UIApplication.shared.preferredContentSizeCategory + let accessibilitySizeEnabled = contentSize.isAccessibilityCategory + return accessibilitySizeEnabled + } + + func updateShowButtonFrame() { + let initialButtonWidth: CGFloat = view.frame.width / 2 + let otherButtonWidths: CGFloat = 82 + let frameWidth = view.frame.width + if showAnomalyBtn!.frame.width > initialButtonWidth { + let isOverflow = showAnomalyBtn!.frame.width >= frameWidth - (otherButtonWidths + 16) + var btnWidth: CGFloat = 0 + if isLargerTextEnabled() { + btnWidth = isOverflow ? min(showAnomalyBtn!.frame.width, frameWidth - otherButtonWidths) : (frameWidth - 2 * (otherButtonWidths + 16)) + } else { + btnWidth = initialButtonWidth + } + + buttonHeight = showAnomalyBtn!.frame.height < showAnomalyBtn!.titleLabel!.frame.height ? 80 : 40 + + showAnomalyInitialX = CGFloat(isLargerTextEnabled() ? 16 : view.frame.width / 2 - btnWidth / 2) + showAnomalyInitialY = CGFloat(-buttonHeight + 20) + showAnomalyInitialWidth = CGFloat(btnWidth) + showAnomalyInitialHeight = CGFloat(buttonHeight) + + showAnomalyBtn?.frame = CGRect( + origin: CGPoint( + x: showAnomalyInitialX, + y: showAnomalyInitialY), + size: CGSize(width: showAnomalyInitialWidth, height: showAnomalyInitialHeight)) + } else { + buttonHeight = showAnomalyBtn!.frame.height < showAnomalyBtn!.titleLabel!.frame.height ? 80 : 40 + showAnomalyInitialX = CGFloat(view.frame.width / 2 - initialButtonWidth / 2) + showAnomalyInitialY = CGFloat(-20) + showAnomalyInitialWidth = CGFloat(isLargerTextEnabled() ? (frameWidth - otherButtonWidths) : initialButtonWidth) + showAnomalyInitialHeight = CGFloat(buttonHeight) + showAnomalyBtn?.frame = CGRect(x: showAnomalyInitialX, y: showAnomalyInitialY, width: showAnomalyInitialWidth, + height: showAnomalyInitialHeight) + } + } + + private func handleOrientationChanged() { + let frameWidth = view.frame.width + let btnWidth = view.frame.width / 2 + if UIDevice.current.orientation.isLandscape { + if bottomSheetInitialY == 0 || bottomSheetInitialY > parent!.view.frame.height { + bottomSheetInitialY = partialView + } + if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft { + addAnomalyBtn?.frame = CGRect(x: view.frame.width - 130, y: -30, width: 65, height: 65) + searchAnomalyBtn?.frame = CGRect(x: view.frame.width - 195, y: -85, width: 65, height: 65) + updateShowButtonFrame() + swipeImageView?.frame = CGRect(x: showAnomalyInitialX + showAnomalyInitialWidth / 2 - 10, y: -buttonHeight - 5, width: 20, height: 20) + } else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight { + addAnomalyBtn?.frame = CGRect(x: view.frame.width - 130, y: -30, width: 65, height: 65) + searchAnomalyBtn?.frame = CGRect(x: view.frame.width - 195, y: -85, width: 65, height: 65) + showAnomalyBtn?.frame = CGRect(x: frameWidth / 2 - btnWidth / 2, y: -buttonHeight + 20, width: btnWidth * 4 / 3, height: buttonHeight) + updateShowButtonFrame() + swipeImageView?.frame = CGRect(x: showAnomalyInitialX + showAnomalyInitialWidth / 2 - 10, y: -buttonHeight - 5, width: 20, height: 20) + } + } else { + if bottomSheetInitialY == 0 || bottomSheetInitialY < parent!.view.frame.height / 2 { + bottomSheetInitialY = partialView + } + addAnomalyBtn?.frame = CGRect(x: view.frame.width - 70, y: -30, width: 65, height: 65) + searchAnomalyBtn?.frame = CGRect(x: view.frame.width - 70, y: -170, width: 65, height: 65) + updateShowButtonFrame() + swipeImageView?.frame = CGRect(x: showAnomalyInitialX + showAnomalyInitialWidth / 2 - 10, y: -buttonHeight - 5, width: 20, height: 20) + } + } + + // MARK: - Bottom sheet methods + @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { - let loc = recognizer.location(in: recognizer.view) let subview = view?.hitTest(loc, with: nil) let objectTapped = subview?.tag - print("panGesture \(objectTapped)") - let translation = recognizer.translation(in: self.view) - let velocity = recognizer.velocity(in: self.view) - let y = self.view.frame.minY - - let direction = recognizer.velocity(in: view).y - print("direction \(direction)") + let translation = recognizer.translation(in: view) + let velocity = recognizer.velocity(in: view) + let y = view.frame.minY + _ = recognizer.velocity(in: view).y - if( !buttomSheetFullView || (buttomSheetFullView && (objectTapped == 0 || objectTapped == nil))) { - if ( y + translation.y >= fullView) && (y + translation.y <= partialView ) { - self.view.frame = CGRect(x: 0, y: y + translation.y, width: view.frame.width, height: view.frame.height) - recognizer.setTranslation(CGPoint.zero, in: self.view) + if !buttomSheetFullView || (buttomSheetFullView && (objectTapped == 0 || objectTapped == nil)) { + if (y + translation.y >= fullView) && (y + translation.y <= bottomSheetInitialY) { + view.frame = CGRect(x: 0, y: y + translation.y, width: view.frame.width, height: view.frame.height) + recognizer.setTranslation(CGPoint.zero, in: view) } if recognizer.state == .ended { - var duration = velocity.y < 0 ? Double((y - fullView) / -velocity.y) : Double((partialView - y) / velocity.y ) + var duration = velocity.y < 0 ? Double((y - fullView) / -velocity.y) : Double((bottomSheetInitialY - y) / velocity.y) duration = duration > 1.3 ? 1 : duration UIView.animate(withDuration: duration, delay: 0.0, options: [.allowUserInteraction], animations: { - if velocity.y >= 0 { + if velocity.y >= 0 { self.animateBottomSheet(withDuration: duration, status: .none) } else { - if(objectTapped != 0) { + if objectTapped != 0 { self.animateBottomSheet(withDuration: duration, status: .full) } } - }, completion: nil) } } } - //MARK: - Update data - func changeAddress(notification:Notification) { + // MARK: - Update data + + func changeAddress(notification: Notification) { if let address = notification.object as? GMSAddress { // Mode online, on recupere une GMSAddress selectedAddress = address @@ -275,27 +378,26 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { selectEquipement = nil animateBottomSheet(withDuration: 0, status: currentStatus) - self.bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) + bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) + let frame = view.frame + view.frame = CGRect(x: 0, y: bottomSheetInitialY, width: frame.width, height: frame.height + buttonHeight) } - - func reloadAnomalies(notification:Notification) { + func reloadAnomalies(notification: Notification) { otherMalfunctionsArray = (notification.object as? [Anomalie])! - self.bottomSheetTableView.reloadData() + bottomSheetTableView.reloadData() } func showAnomalie(anomalie: Anomalie) { - self.selectAnomalie = anomalie + selectAnomalie = anomalie // Update first row - self.bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) + bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) if otherCellDisplay { - self.bottomSheetTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .none) + bottomSheetTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .none) } addAnomalyBtn?.isHidden = true congratulateAnomalyBtn?.isEnabled = true - hidemanageFavoriteBtns = true - if selectAnomalie?.anomalieStatus == .Resolu { followAnomalyBtn?.isHidden = true unfollowAnomalyBtn?.isHidden = true @@ -311,12 +413,12 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } } - func showEquipement(equipement: Equipement) { - self.selectEquipement = equipement + func showEquipement(equipement: Equipement) { + selectEquipement = equipement // Update first row - self.bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) + bottomSheetTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none) if otherCellDisplay { - self.bottomSheetTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .none) + bottomSheetTableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .none) } addAnomalyBtn?.isHidden = false @@ -326,12 +428,12 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { congratulateAnomalyBtn?.isHidden = true } - //MARK: - Other functions + // MARK: - Other functions + func getDetailsAnomalies(anomalie: Anomalie, source: AnomalieSource) { if let anoEquipement = anomalie as? AnomalieEquipement { DispatchQueue.global().async { - - RestApiManager.sharedInstance.getIncidentEquipementById(idSignalement: anoEquipement.id){ (anomalie: AnomalieEquipement) in + RestApiManager.sharedInstance.getIncidentEquipementById(idSignalement: anoEquipement.id) { (anomalie: AnomalieEquipement) in DispatchQueue.main.async { let anomalyDetailStoryboard = UIStoryboard(name: Constants.StoryBoard.detailAnomaly, bundle: nil) let anomalyDetailViewController = anomalyDetailStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.detailAnomaly) as! AnomalyDetailViewController @@ -340,15 +442,14 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { anomalyDetailViewController.currentAnomalyState = (anomalie.anomalieStatus == .Resolu ? .solved : .notsolved) self.present(anomalyDetailViewController, animated: true, completion: nil) anomalyDetailViewController.customNavigationDelegate = self - } } } } else { - //Si ano DMR, on affiche le détail + // Si ano DMR, on affiche le détail if source == AnomalieSource.dmr { DispatchQueue.global().async { - RestApiManager.sharedInstance.getIncidentById(idSignalement: anomalie.id, source: source){ (anomalie: Anomalie) in + RestApiManager.sharedInstance.getIncidentById(idSignalement: anomalie.id, source: source) { (anomalie: Anomalie) in DispatchQueue.main.async { let anomalyDetailStoryboard = UIStoryboard(name: Constants.StoryBoard.detailAnomaly, bundle: nil) let anomalyDetailViewController = anomalyDetailStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.detailAnomaly) as! AnomalyDetailViewController @@ -357,51 +458,54 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { anomalyDetailViewController.currentAnomalyState = (anomalie.anomalieStatus == .Resolu ? .solved : .notsolved) self.present(anomalyDetailViewController, animated: true, completion: nil) anomalyDetailViewController.customNavigationDelegate = self - } } } } } - } - @objc func addOrRemoveFavorite(recognizer: MyTapGesture){ - var favorite : [String] = getFavoritesAddress() - //Récupération de l'adresse et des coordonées + func didChange(_ state: OIDAuthState) { + setAuthState(state) + } + + @objc func addOrRemoveFavorite(recognizer: MyTapGesture) { + var favorite: [String] = getFavoritesAddress() + // Récupération de l'adresse et des coordonées let addressWithCoordonate = MapsUtils.fullAddress() + Constants.Key.separatorAdresseCoordonate + String(MapsUtils.userLocation()!.latitude) + "-" + String(MapsUtils.userLocation()!.longitude) - //Ajout d'une adresse aux favoris - if(recognizer.addFavorite) { - //Vérification si l'adresse est dans Paris + // Ajout d'une adresse aux favoris + if recognizer.addFavorite { + // Vérification si l'adresse est dans Paris if MapsUtils.postalCode.hasPrefix(Constants.prefix75) { favorite.append(addressWithCoordonate) + isAlreadyInFavorites = true } else { - //Si hors de Paris, affichage d'une popup d'erreur + // Si hors de Paris, affichage d'une popup d'erreur let alertController = UIAlertController(title: Constants.AlertBoxTitle.adresseHorsParis, message: Constants.AlertBoxMessage.adresseHorsParis, preferredStyle: .alert) let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default, handler: nil) alertController.addAction(OKAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) + present(alertController, animated: true, completion: nil) } } else { - //Suppression si adresse et coordonnées égaux + // Suppression si adresse et coordonnées égaux if let index = favorite.index(of: addressWithCoordonate) { favorite.remove(at: index) + isAlreadyInFavorites = false } else { - //Suppression si coordonnées ou adresse égaux + // Suppression si coordonnées ou adresse égaux for fav in favorite { - var isAlreadyInFavorites = false let favArr = fav.components(separatedBy: Constants.Key.separatorAdresseCoordonate) - //Vérification sur le nom de l'adresse + // Vérification sur le nom de l'adresse if favArr[0] == MapsUtils.fullAddress() { isAlreadyInFavorites = true } - //Vérification sur les coordonnées + // Vérification sur les coordonnées if String(MapsUtils.userLocation()!.latitude) + "-" + String(MapsUtils.userLocation()!.longitude) == favArr[1] { isAlreadyInFavorites = true } @@ -409,6 +513,7 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { if isAlreadyInFavorites { let index = favorite.index(of: fav) favorite.remove(at: index!) + isAlreadyInFavorites = false } } } @@ -418,25 +523,25 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { bottomSheetTableView.reloadData() } - //Retourne les adresses favorites de l'utilisateur + // Retourne les adresses favorites de l'utilisateur func getFavoritesAddress() -> [String] { let defaults = UserDefaults.standard - var favorite : [String] = [] + var favorite: [String] = [] - //Récupération des favoris dans les UserDefaults + // Récupération des favoris dans les UserDefaults if let favoritesArray = defaults.stringArray(forKey: "favoritesAddressArray") { favorite = favoritesArray } return favorite } - @objc func displaySelectedAnomaly(_ sender:AnyObject){ + @objc func displaySelectedAnomaly(_ sender: AnyObject) { if let myAnomalie = selectAnomalie { getDetailsAnomalies(anomalie: myAnomalie, source: myAnomalie.source) } } - @objc func hideUberPin(_ sender:AnyObject){ + @objc func hideUberPin(_ sender: AnyObject) { setUberPinHidden(true) } @@ -451,13 +556,17 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } } - func notificationToDisplayAnomalie(notification:Notification) { + func notificationToDisplayAnomalie(notification: Notification) { // Réception d'une notification push pour afficher le détail d'une anomalie if let anomalie = notification.object as? Anomalie { - self.getDetailsAnomalies(anomalie: anomalie, source: .dmr) + getDetailsAnomalies(anomalie: anomalie, source: .dmr) } } + @objc func displayBottomSheet(sender: UIButton) { + animateBottomSheet(withDuration: 1, status: .full) + } + /// Animation de l'affichage de la BottomSheet en fonction du status /// /// - Parameters: @@ -471,6 +580,7 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { otherCellDisplay = true buttomSheetFullView = false showAnomalyBtn?.isHidden = true + swipeImageView?.isHidden = true if let anomalie = selectAnomalie { // Cas d'une anomalie outdoor @@ -494,7 +604,6 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { followAnomalyBtn?.isHidden = true unfollowAnomalyBtn?.isHidden = true congratulateAnomalyBtn?.isHidden = true - hidemanageFavoriteBtns = false } addAnomalyBtn?.setImage(imgAddAnomalySelected, for: .normal) @@ -521,19 +630,19 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { followAnomalyBtn?.isHidden = true unfollowAnomalyBtn?.isHidden = true congratulateAnomalyBtn?.isHidden = true - hidemanageFavoriteBtns = true showAnomalyBtn?.isHidden = true + swipeImageView?.isHidden = true - if(isFirstFullView) { - initFrameHeight = self.view.frame.height + if isFirstFullView { + initFrameHeight = view.frame.height isFirstFullView = false } + delegate.activateTopConstraint(false) UIView.animate(withDuration: duration) { [weak self] in let frame = self?.view.frame - self?.view.frame = CGRect(x: 0, y: (self?.fullView)!, width: frame!.width, height: self!.initFrameHeight - 130) + self?.view.frame = CGRect(x: 0, y: (self?.fullView)!, width: frame!.width, height: frame!.height) } - default: // Dans le cas où on vient du plein écran. On reprend le status du uberPin. if currentStatus == .full { @@ -544,14 +653,14 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { otherCellDisplay = false buttomSheetFullView = false selectAnomalie = nil - //selectEquipement = nil + // selectEquipement = nil addAnomalyBtn?.isHidden = false followAnomalyBtn?.isHidden = true unfollowAnomalyBtn?.isHidden = true congratulateAnomalyBtn?.isHidden = true - hidemanageFavoriteBtns = false showAnomalyBtn?.isHidden = false + swipeImageView?.isHidden = false addAnomalyBtn?.setImage(imgAddAnomaly, for: .normal) followAnomalyBtn?.setImage(imgFollowAnomaly, for: .normal) @@ -562,15 +671,18 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { followAnomalyBtn?.isEnabled = anomalie.source == .dmr unfollowAnomalyBtn?.isEnabled = anomalie.source == .dmr } - + delegate.activateTopConstraint(true) UIView.animate(withDuration: duration) { [weak self] in - let frame = self?.view.frame - self?.view.frame = CGRect(x: 0, y: (self?.partialView)!, width: frame!.width, height: frame!.height + 40) + guard let self = self else { return } + self.view.frame = CGRect( + x: 0, + y: self.bottomSheetInitialY, + width: self.view.frame.width, + height: self.view.frame.height + self.buttonHeight) } } - - self.navigationController?.setNavigationBarHidden(buttomSheetFullView, animated: true) - self.bottomSheetTableView.reloadData() + bottomSheetTableView.reloadData() + navigationController?.setNavigationBarHidden(buttomSheetFullView, animated: true) } func displayAddAnomalyView() { @@ -579,32 +691,31 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { let alertController = UIAlertController(title: Constants.AlertBoxTitle.adresseInvalide, message: Constants.AlertBoxMessage.adresseInvalide, preferredStyle: .alert) // Create OK button let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { - (action:UIAlertAction!) in + (_: UIAlertAction!) in } alertController.addAction(OKAction) // Present Dialog message - self.present(alertController, animated: true, completion:nil) + present(alertController, animated: true, completion: nil) } else { let addAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.addAnomaly, bundle: nil) let addAnomalyViewController = addAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.addAnomaly) as! AddAnomalyViewController addAnomalyViewController.typeContribution = ContextManager.shared.typeContribution - self.navigationController?.navigationBar.tintColor = UIColor.white - self.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white]) - self.navigationController?.pushViewController(addAnomalyViewController, animated: true) + navigationController?.navigationBar.tintColor = UIColor.white + navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white]) + navigationController?.pushViewController(addAnomalyViewController, animated: true) } - } @objc func tapAddAnomaly(sender: UIButton) { if ContextManager.shared.typeContribution == .indoor { if ContextManager.shared.equipementSelected == nil { - let alertController = UIAlertController.init(title: Constants.AlertBoxTitle.attention, message: ContextManager.shared.typeEquipementSelected?.msgAlertNoEquipement, preferredStyle: .alert) + let alertController = UIAlertController(title: Constants.AlertBoxTitle.attention, message: ContextManager.shared.typeEquipementSelected?.msgAlertNoEquipement, preferredStyle: .alert) - let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (action:UIAlertAction!) in + let OKAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: .default) { (_: UIAlertAction!) in } alertController.addAction(OKAction) - self.present(alertController, animated: true, completion: nil) + present(alertController, animated: true, completion: nil) } else { displayAddAnomalyView() } @@ -617,72 +728,79 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { showSearchPopup(erreur: "") } - func showSearchPopup ( erreur : String) { - //Popup de recherche - + func showSearchPopup(erreur: String) { var message = "" - //Ajout du message d'erreur si il y en a un + // Ajout du message d'erreur si il y en a un if erreur != "" { - message = "\n" + erreur + "\n\n\n" + message = erreur + "\n" } + let alertController = UIAlertController(title: Constants.AlertBoxTitle.searchAnomaly, message: message + Constants.AlertBoxMessage.searchAnomaly, preferredStyle: .alert) - - //Textfield pour la saisie du numéro - alertController.addTextField { [weak self] (textField) in - textField.delegate = self - textField.placeholder = "n° : " + + // Textfield pour la saisie du numéro + alertController.addTextField { [weak self] textField in + textField.delegate = self + textField.placeholder = "n° de l'anomalie: " + textField.accessibilityHint = "numéro de l'anomalie" + textField.accessibilityTraits = .searchField + textField.attributedPlaceholder = NSAttributedString( + string: "numéro de l'anomalie", + attributes: [NSAttributedString.Key.foregroundColor: UIColor.greyDmr()]) } // Boutton rechercher - let SearchAction = UIAlertAction.init(title: Constants.LabelMessage.searchAnomaly, style: .default, handler: { (action: UIAlertAction!) in + let SearchAction = UIAlertAction(title: "Rechercher", style: .default, handler: { (_: UIAlertAction!) in let textField = alertController.textFields![0] as UITextField - //Recherche + // Recherche self.searchAnomalyByNumber(number: textField.text!) }) // Boutton annuler - let cancelBtn = UIAlertAction(title:"Annuler" , style: .default, handler: {(_ action: UIAlertAction) -> Void in + let cancelBtn = UIAlertAction(title: "Annuler", style: .default, handler: { (_: UIAlertAction) in }) - + SearchAction.isAccessibilityElement = true + SearchAction.accessibilityHint = Constants.LabelMessage.searchAnomaly + SearchAction.accessibilityLabel = Constants.LabelMessage.searchAnomaly alertController.addAction(SearchAction) alertController.addAction(cancelBtn) - self.present(alertController, animated: true, completion:nil) + present(alertController, animated: true, completion: nil) + UIAccessibility.post(notification: .announcement, argument: erreur) } /// Méthode de recherche d'anomalie par numéro /// /// - Parameters: /// - number: numéro de l'anomalie - func searchAnomalyByNumber (number: String) { - let pattern = "[BSGAbsga][2][0-9]{3}[A-La-l][0-9]+$" - let result = number.range(of: pattern, options:.regularExpression) + func searchAnomalyByNumber(number: String) { + let pattern = "[BSGAWbsgaw][2][0-9]{3}[A-La-l][0-9]+$" + let result = number.range(of: pattern, options: .regularExpression) - //Le format ne correspond pas + // Le format ne correspond pas if result == nil { - //Affichage du message d'erreur + // Affichage du message d'erreur print("Erreur lors la saisie du numéro") showSearchPopup(erreur: "Numéro incorrect") } else { - //Lancement de la recherche - RestApiManager.sharedInstance.getIncidentsByNumber(number: number) { (jsonDict) in + // Lancement de la recherche + RestApiManager.sharedInstance.getIncidentsByNumber(number: number) { jsonDict in if let answer = jsonDict["answer"]?.dictionary { if let incident = answer["incident"]?.arrayValue { - let marker = incident [0] - RestApiManager.sharedInstance.getIncidentById(idSignalement: marker["id"].stringValue, source: AnomalieSource.dmr){ (anomalie: Anomalie) in + let marker = incident[0] + RestApiManager.sharedInstance.getIncidentById(idSignalement: marker["id"].stringValue, source: AnomalieSource.dmr) { (anomalie: Anomalie) in DispatchQueue.main.async { var anomaliesBottomSheet = [Anomalie]() DispatchQueue.main.async { - // Ajout du GMSMarker sur la map - self.delegate.addMarkerAnomalie(anomalie: anomalie) + // Ajout du GMSMarker sur la map + self.delegate.addMarkerAnomalie(anomalie: anomalie) - if anomalie.anomalieStatus != .Resolu && !anomaliesBottomSheet.contains(anomalie) { - anomaliesBottomSheet.append(anomalie) - } + if anomalie.anomalieStatus != .Resolu && !anomaliesBottomSheet.contains(anomalie) { + anomaliesBottomSheet.append(anomalie) + } NotificationCenter.default.post(name: self.anomalieNotification, object: anomaliesBottomSheet) - //Zoom sur l'anomalie + // Zoom sur l'anomalie let currentLocation = CLLocationCoordinate2D(latitude: anomalie.latitude, longitude: anomalie.longitude) self.delegate.mapContainerView.clear() self.delegate.centerCameraToPosition(currentLocation: currentLocation) @@ -690,11 +808,9 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } } } - } - else if let answer = jsonDict["error_message"]?.stringValue { + } else if let answer = jsonDict["error_message"]?.stringValue { self.showSearchPopup(erreur: answer) - } - else if let answer = jsonDict["erreurBO"]?.stringValue { + } else if let answer = jsonDict["erreurBO"]?.stringValue { self.showSearchPopup(erreur: answer) } } @@ -702,21 +818,21 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, - replacementString string: String) -> Bool { - return textField.text!.count <= 12 || ( string == "") + replacementString string: String) -> Bool + { + return textField.text!.count <= 12 || (string == "") } @objc func tapFollowAnomaly(sender: UIButton) { - if (!User.shared.isLogged){ - //Redirection vers le Compte Parisien - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - self.navigationController?.present(compteParisienVC, animated: true) + if !User.shared.isLogged { + // Redirection vers le Compte Parisien + connectToMonParis() } else { if let anomalie = selectAnomalie { DispatchQueue.global().async { RestApiManager.sharedInstance.follow(anomalie: anomalie, onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { anomalie.isIncidentFollowedByUser = true self.followAnomalyBtn?.isHidden = true @@ -724,24 +840,196 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.followMalfunction, duration: .middle) snackbar.messageTextAlign = .center - snackbar.messageTextFont = UIFont.init(name: Constants.fontDmr, size: 14)! + snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 14)! snackbar.show() + UIAccessibility.post(notification: .announcement, argument: Constants.AlertBoxMessage.followMalfunction) } - + } else { + self.showPopupMaintenance() } }) } } - } } + func showPopupMaintenance() { + let alert = UIAlertController(title: Constants.AlertBoxTitle.information, message: Constants.AlertBoxMessage.maintenance, preferredStyle: UIAlertController.Style.alert) + let okBtn = UIAlertAction(title: "Ok", style: .default, handler: { (_: UIAlertAction) in + }) + alert.addAction(okBtn) + self.present(alert, animated: true, completion: nil) + } + + func connectToMonParis() { + let authorizationEndpoint = Constants.Authentification.authorizationEndpoint + let tokenEndpoint = Constants.Authentification.tokenEndpoint + let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + + doAuthWithAutoCodeExchange(configuration: configuration, + clientID: Constants.Authentification.clientID, + clientSecret: nil) + } + + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: redirectURI) else { + print("Error creating URL for : \(redirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + print("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + self.userInfo() + } else { + print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func userInfo() { + let userInfoEndpoint = Constants.Authentification.userInfoEndpoint + print("Performing userinfo request") + let currentAccessToken: String? = authState?.lastTokenResponse?.accessToken + + authState?.performAction { accessToken, _, error in + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + print("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + print("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + print("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userInfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + print("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + print("Non-HTTP response") + return + } + + guard let data = data else { + print("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + print("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + print("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + + return + } + + if let json = json as? [String: Any], let uid = json["uid"] as? String, let validatedAccount = json["validatedAccount"] as? String { + print("Success: \(json)") + + if validatedAccount != "true" { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) + + let okAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default) { _ in + // nothing + } + + alertController.addAction(okAction) + + self.present(alertController, animated: true, completion: nil) + } else { + User.shared.uid = uid + UserDefaults.standard.set(uid, forKey: Constants.Key.uid) + RestApiManager.sharedInstance.getIdentityStore(guid: User.shared.uid!) { + (_: Bool) in + if User.shared.isLogged {} + } + } + } + } + } + + task.resume() + } + } + + func setAuthState(_ authState: OIDAuthState?) { + if self.authState == authState { + return + } + self.authState = authState + self.authState?.stateChangeDelegate = self + var data: Data? = nil + + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + if let userDefaults = UserDefaults(suiteName: Constants.Authentification.userDefault) { + userDefaults.set(data, forKey: authStateKey) + userDefaults.synchronize() + } + } + @objc func tapUnfollowAnomaly(sender: UIButton) { if let anomalie = selectAnomalie { DispatchQueue.global().async { RestApiManager.sharedInstance.unfollow(anomalie: anomalie, onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { anomalie.isIncidentFollowedByUser = false self.followAnomalyBtn?.isHidden = false @@ -749,47 +1037,46 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.unfollowMalfunction, duration: .middle) snackbar.messageTextAlign = .center - snackbar.messageTextFont = UIFont.init(name: Constants.fontDmr, size: 14)! + snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 14)! snackbar.show() + UIAccessibility.post(notification: .announcement, argument: Constants.AlertBoxMessage.unfollowMalfunction) } - + } + else { + self.showPopupMaintenance() } }) } } - } @objc func tapCongratulateAnomaly(sender: UIButton) { - if(!User.shared.isLogged){ - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - self.navigationController?.present(compteParisienVC, animated: true) + if !User.shared.isLogged { + connectToMonParis() } else { if let anomalie = selectAnomalie { var cancelAction = false - self.congratulateAnomalyBtn?.isEnabled = false + congratulateAnomalyBtn?.isEnabled = false - let snackbar = TTGSnackbar.init(message: Constants.AlertBoxMessage.congratulate, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) - { (snackbar) -> Void in - self.congratulateAnomalyBtn?.isEnabled = true - cancelAction = true - } + let snackbar = TTGSnackbar(message: Constants.AlertBoxMessage.congratulate, duration: .middle, actionText: Constants.AlertBoxTitle.annuler) + { _ in + self.congratulateAnomalyBtn?.isEnabled = true + cancelAction = true + } snackbar.actionTextColor = UIColor.pinkDmr() snackbar.actionTextFont = UIFont(name: Constants.fontDmr, size: 14.0)! snackbar.messageTextFont = UIFont(name: Constants.fontDmr, size: 15.0)! snackbar.messageTextAlign = NSTextAlignment.center - //Callback si l'utilisateur ne fais pas "annuler" + // Callback si l'utilisateur ne fais pas "annuler" snackbar.dismissBlock = { - - (snackbar: TTGSnackbar) -> Void in + (_: TTGSnackbar) in if !cancelAction { DispatchQueue.global().async { - RestApiManager.sharedInstance.congratulateAnomalie(anomalie: anomalie, onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { self.congratulateAnomalyBtn?.isEnabled = false } @@ -797,14 +1084,24 @@ class BottomSheetViewController: UIViewController, UITextFieldDelegate { }) } } - - } snackbar.show() } } + } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } + + func getBottomSheetVisibleHieght() -> CGFloat { + // let customCell = bottomSheetTableView.dequeueReusableCell(withIdentifier: "localization_cell") - } + // guard let height = customCell?.bounds.height else { return 0 } + guard let height = bottomSheetTableView.cellForRow(at: IndexPath(row: 0, section: 0))?.contentView.bounds.height else { return 0 } + return height + } } /// Enumération contenant les différents status d'affichage de la BottomSheet @@ -819,55 +1116,35 @@ enum BottomSheetStatus { } extension BottomSheetViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.row { case 0: - animateBottomSheet(withDuration: 0.3, status: (otherCellDisplay ? .none : .selected)) - + break case 1: if selectAnomalie == nil && selectEquipement == nil { setUberPinHidden(uberDisplayed) } else { - self.navigationController?.setNavigationBarHidden(false, animated: true) - self.displayAddAnomalyView() + navigationController?.setNavigationBarHidden(false, animated: true) + displayAddAnomalyView() } default: let anomalie = otherMalfunctionsArray[indexPath.row - 3] - self.getDetailsAnomalies(anomalie: anomalie, source: anomalie.source) - + getDetailsAnomalies(anomalie: anomalie, source: anomalie.source) } - } - } extension BottomSheetViewController: UITableViewDataSource { - - struct RowId { + enum RowId { static let description = 0 static let uberMode = 1 static let labelAnomaly = 2 static let other = 3 } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - switch indexPath.row { - case RowId.description: - return 100 - case RowId.uberMode: - // Cette ligne est visible uniquement pour les anomalies outdoor ou lors de la sélection d'un équipement indoor - return (ContextManager.shared.typeContribution == .indoor && ContextManager.shared.equipementSelected == nil) ? 0 : 68 - case RowId.labelAnomaly: - return 48 - default: - return 90 - } - } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if otherCellDisplay { - return 3 + self.otherMalfunctionsArray.count + return 3 + otherMalfunctionsArray.count } else { return 1 } @@ -883,74 +1160,114 @@ extension BottomSheetViewController: UITableViewDataSource { switch indexPath.row { case RowId.description: - customCell = tableView.dequeueReusableCell(withIdentifier: "localization_cell") - - let geolocMainTitle = customCell?.viewWithTag(102) as! UILabel - let geolocSubtitle = customCell?.viewWithTag(103) as! UILabel - - // Hidden by default - geolocSubtitle.isHidden = true - - if ContextManager.shared.typeContribution == .outdoor { - showDetailAnomalie(forCell: customCell!) - } else if ContextManager.shared.typeContribution == .indoor { - showDetailEquipement(forCell: customCell!) - } + if buttomSheetFullView { + customCell = tableView.dequeueReusableCell(withIdentifier: "header_cell") + customCell?.isAccessibilityElement = false + let geolocImageView = customCell?.viewWithTag(101) as! UIImageView + let geolocMainTitle = customCell?.viewWithTag(102) as! UILabel + let favoriteBtn = customCell?.viewWithTag(103) as! UIButton - if self.otherCellDisplay { + favoriteBtn.setTitle("", for: .normal) + favoriteBtn.backgroundColor = .white + favoriteBtn.accessibilityTraits = .button + if !isAlreadyInFavorites { + favoriteBtn.setImage(imgAddAddressFavorite, for: .normal) + favoriteBtn.accessibilityLabel = String(format: Constants.LabelMessage.addAdresseFavorite, MapsUtils.addressLabel) + let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(addOrRemoveFavorite(recognizer:))) + tapGestureRecognizer.addFavorite = true + favoriteBtn.addGestureRecognizer(tapGestureRecognizer) + } else { + favoriteBtn.setImage(imgRemoveddressFavorite, for: .normal) + favoriteBtn.accessibilityLabel = String(format: Constants.LabelMessage.removeAdresseFavorite, MapsUtils.addressLabel) + let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(addOrRemoveFavorite(recognizer:))) + tapGestureRecognizer.addFavorite = false + favoriteBtn.addGestureRecognizer(tapGestureRecognizer) + } customCell?.backgroundColor = UIColor.pinkDmr() geolocMainTitle.textColor = .white - geolocSubtitle.textColor = UIColor.lightGreyDmr() - } else { - customCell?.backgroundColor = .white - geolocMainTitle.textColor = .black - geolocSubtitle.textColor = UIColor.lightGreyDmr() - } - - if buttomSheetFullView { - let cellFrame = customCell?.frame - let titleLabel:UILabel = UILabel() - titleLabel.frame = CGRect(x: 0, y: 10, width: self.view.frame.width, height: (cellFrame?.height)!) - titleLabel.numberOfLines = 2 - titleLabel.lineBreakMode = .byWordWrapping - titleLabel.textColor = .white - titleLabel.font = UIFont.init(name: Constants.fontDmr, size: 16) - titleLabel.tag = 999 - titleLabel.textAlignment = .center - titleLabel.text = MapsUtils.addressLabel - - if ContextManager.shared.typeContribution == .outdoor || self.selectEquipement == nil { - titleLabel.text = MapsUtils.addressLabel + "\n" + MapsUtils.boroughLabel + geolocMainTitle.isAccessibilityElement = true + geolocMainTitle.accessibilityTraits = .staticText + geolocMainTitle.lineBreakMode = .byWordWrapping + geolocMainTitle.textColor = .white + geolocMainTitle.font = UIFont(name: Constants.fontDmr, size: 16) + geolocMainTitle.textAlignment = .center + geolocMainTitle.text = MapsUtils.addressLabel + if ContextManager.shared.typeContribution == .outdoor || selectEquipement == nil { + geolocMainTitle.text = MapsUtils.addressLabel + "\n" + MapsUtils.boroughLabel } else if ContextManager.shared.typeContribution == .indoor { - titleLabel.text = selectEquipement?.name + geolocMainTitle.text = selectEquipement?.name } - - customCell?.addSubview(titleLabel) - - // Ajout du bouton de fermeture - let closeBtn = UIButton(frame: CGRect(x:15, y:(customCell?.frame.height)! / 2 - 5, width:30, height:30)) - closeBtn.backgroundColor = .clear - closeBtn.layer.borderWidth = 0 - closeBtn.setImage(UIImage(named: Constants.Image.iconExit), for: .normal) - closeBtn.tintColor = .white - closeBtn.addTarget(self, action: #selector(self.closeBtnTouchUp(_:)), for: .touchUpInside) - closeBtn.accessibilityLabel = Constants.LabelMessage.reduceBottomSheet - customCell?.addSubview(closeBtn) - + geolocImageView.backgroundColor = .clear + geolocImageView.layer.borderWidth = 0 + geolocImageView.image = UIImage(named: Constants.Image.iconExit) + geolocImageView.tintColor = .white + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closeBtnTouchUp(_:))) + geolocImageView.isUserInteractionEnabled = true + geolocImageView.addGestureRecognizer(tapGestureRecognizer) + geolocImageView.isAccessibilityElement = true + geolocImageView.accessibilityLabel = Constants.LabelMessage.reduceBottomSheet + geolocImageView.accessibilityTraits = .button + applyDynamicType(label: geolocMainTitle, fontName: "Montserrat-Regular", size: 12.0) + } else { + customCell = tableView.dequeueReusableCell(withIdentifier: "localization_cell") + customCell?.isAccessibilityElement = false + let geolocMainTitle = customCell?.viewWithTag(102) as! UILabel + let geolocSubtitle = customCell?.viewWithTag(103) as! UILabel + let favoriteBtn = customCell?.viewWithTag(105) as! UIButton + applyDynamicType(label: geolocMainTitle, fontName: "Montserrat-Regular", size: 12.0) + applyDynamicType(label: geolocSubtitle, fontName: "Montserrat-Regular", size: 12.0) + // Hidden by default geolocSubtitle.isHidden = true + favoriteBtn.setTitle("", for: .normal) + favoriteBtn.backgroundColor = .white + favoriteBtn.accessibilityTraits = .button + if ContextManager.shared.typeContribution == .outdoor { + showDetailAnomalie(forCell: customCell!) + } else if ContextManager.shared.typeContribution == .indoor { + showDetailEquipement(forCell: customCell!) + } + if !isAlreadyInFavorites { + favoriteBtn.setImage(imgAddAddressFavorite, for: .normal) + favoriteBtn.accessibilityLabel = String(format: Constants.LabelMessage.addAdresseFavorite, MapsUtils.addressLabel) + let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(addOrRemoveFavorite(recognizer:))) + tapGestureRecognizer.addFavorite = true + favoriteBtn.addGestureRecognizer(tapGestureRecognizer) + } else { + favoriteBtn.setImage(imgRemoveddressFavorite, for: .normal) + favoriteBtn.accessibilityLabel = String(format: Constants.LabelMessage.removeAdresseFavorite, MapsUtils.addressLabel) + let tapGestureRecognizer = MyTapGesture(target: self, action: #selector(addOrRemoveFavorite(recognizer:))) + tapGestureRecognizer.addFavorite = false + favoriteBtn.addGestureRecognizer(tapGestureRecognizer) + } + if otherCellDisplay { + customCell?.backgroundColor = UIColor.pinkDmr() + geolocMainTitle.textColor = .white + geolocSubtitle.textColor = .white + } else { + customCell?.backgroundColor = .white + geolocMainTitle.textColor = .black + geolocSubtitle.textColor = UIColor.greyDmr() + } + geolocMainTitle.isAccessibilityElement = true + geolocMainTitle.accessibilityTraits = .staticText + geolocSubtitle.isAccessibilityElement = true + geolocSubtitle.accessibilityTraits = .staticText } case RowId.uberMode: customCell = tableView.dequeueReusableCell(withIdentifier: "preciser_position_cell") let precisionLabel = customCell?.viewWithTag(201) as! UILabel let precisionImage = customCell?.viewWithTag(202) as! UIImageView - + applyDynamicType(label: precisionLabel, fontName: "Montserrat-Regular", size: 12.0) + if selectAnomalie != nil || selectEquipement != nil { precisionLabel.text = Constants.LabelMessage.addAnomaly.uppercased() precisionImage.image = UIImage(named: Constants.Image.addAnomalie) } else { precisionLabel.text = Constants.LabelMessage.preciserPosition.uppercased() + precisionLabel.accessibilityLabel = Constants.LabelMessage.preciserPosition.uppercased() + precisionLabel.accessibilityTraits = .button precisionImage.image = UIImage(named: Constants.Image.pinRose) } @@ -960,32 +1277,40 @@ extension BottomSheetViewController: UITableViewDataSource { customCell = tableView.dequeueReusableCell(withIdentifier: "otherMalfunctionTitleCell") let otherAnomalieLabel = customCell?.viewWithTag(300) as! UILabel - + applyDynamicType(label: otherAnomalieLabel, fontName: "Montserrat-Light", size: 15.0) if ContextManager.shared.typeContribution == .indoor { otherAnomalieLabel.text = Constants.LabelMessage.otherAnomalieEquipementLabel } else { otherAnomalieLabel.text = Constants.LabelMessage.otherAnomalieLabel } + otherAnomalieLabel.accessibilityTraits = .header + otherAnomalieLabel.isAccessibilityElement = true default: customCell = tableView.dequeueReusableCell(withIdentifier: "otherMalfunctionCell") + customCell?.accessibilityTraits = .button let otherMalfunction = otherMalfunctionsArray[indexPath.row - 3] let otherMalfunctionMainTitle = customCell?.viewWithTag(402) as! UILabel otherMalfunctionMainTitle.text = otherMalfunction.alias + otherMalfunctionMainTitle.accessibilityLabel = otherMalfunction.alias + otherMalfunctionMainTitle.accessibilityTraits = .staticText otherMalfunctionMainTitle.lineBreakMode = NSLineBreakMode.byTruncatingTail otherMalfunctionMainTitle.numberOfLines = 0 - + applyDynamicType(label: otherMalfunctionMainTitle, fontName: "Montserrat-Regular", size: 14.0) let otherMalfunctionAddress = customCell?.viewWithTag(403) as! UILabel otherMalfunctionAddress.lineBreakMode = .byClipping - otherMalfunctionAddress.numberOfLines=0 + otherMalfunctionAddress.numberOfLines = 0 otherMalfunctionAddress.text = otherMalfunction.address + "\n" + otherMalfunction.number - + otherMalfunctionAddress.accessibilityLabel = otherMalfunction.address + "\n" + otherMalfunction.number + otherMalfunctionAddress.accessibilityTraits = .staticText + applyDynamicType(label: otherMalfunctionAddress, fontName: "Montserrat-Regular", size: 12.0) let otherMalfunctionImageView = customCell?.viewWithTag(401) as! UIImageView - let imageURL = (otherMalfunction.source == .ramen) ? URL(string: Constants.Image.ramen) : (URL(string: otherMalfunction.firstImageUrl) ?? URL(string: Constants.Image.noImage)) + otherMalfunctionImageView.accessibilityTraits = .image + let imageURL = (otherMalfunction.source == .ramen) ? URL(string: Constants.Image.ramen) : (URL(string: otherMalfunction.firstImageUrl) ?? URL(string: Constants.Image.noImage)) otherMalfunctionImageView.sd_setImage(with: imageURL!, placeholderImage: otherMalfunction.imageCategorie, options: .allowInvalidSSLCertificates) - if(self.currentStatus == .full) { + if currentStatus == .full { bottomSheetTableView.isScrollEnabled = true } else { bottomSheetTableView.isScrollEnabled = false @@ -998,17 +1323,13 @@ extension BottomSheetViewController: UITableViewDataSource { } @objc private func closeBtnTouchUp(_ sender: UIButton) { - self.animateBottomSheet(withDuration: 0.5, status: .none) + animateBottomSheet(withDuration: 0.5, status: .none) } private func showDetailAnomalie(forCell customCell: UITableViewCell) { - let geolocImageView = customCell.viewWithTag(101) as! UIImageView let geolocMainTitle = customCell.viewWithTag(102) as! UILabel - geolocMainTitle.isHidden = buttomSheetFullView - geolocImageView.isHidden = buttomSheetFullView - if !buttomSheetFullView, let myAnomalie = selectAnomalie { geolocMainTitle.text = myAnomalie.alias @@ -1017,12 +1338,12 @@ extension BottomSheetViewController: UITableViewDataSource { geolocSubtitle.text = myAnomalie.address geolocSubtitle.isHidden = false - if !self.buttomSheetFullView { + if !buttomSheetFullView { let imageURL = URL(string: myAnomalie.firstImageUrl) ?? URL(string: Constants.Image.noImage) geolocImageView.sd_setImage(with: imageURL!, placeholderImage: myAnomalie.imageCategorie, options: .allowInvalidSSLCertificates) - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.displaySelectedAnomaly(_:))) + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(displaySelectedAnomaly(_:))) geolocImageView.isUserInteractionEnabled = true geolocImageView.addGestureRecognizer(tapGestureRecognizer) @@ -1034,11 +1355,9 @@ extension BottomSheetViewController: UITableViewDataSource { } private func showDetailEquipement(forCell customCell: UITableViewCell) { - let geolocImageView = customCell.viewWithTag(101) as! UIImageView let geolocMainTitle = customCell.viewWithTag(102) as! UILabel - geolocMainTitle.isHidden = buttomSheetFullView geolocImageView.isHidden = buttomSheetFullView @@ -1047,11 +1366,11 @@ extension BottomSheetViewController: UITableViewDataSource { geolocImageView.isHidden = true // Affichage du nom de la piscine - let nameLbl:UILabel = addLabel(withText: myEquipement.name, andTag: 104) + let nameLbl: UILabel = addLabel(withText: myEquipement.name, andTag: 104) nameLbl.numberOfLines = 1 nameLbl.lineBreakMode = .byTruncatingTail nameLbl.frame = CGRect(x: 15, y: 8, width: geolocMainTitle.frame.width + geolocImageView.frame.width, height: geolocMainTitle.frame.height) - if self.otherCellDisplay { + if otherCellDisplay { nameLbl.textColor = .white } else { nameLbl.textColor = .black @@ -1060,7 +1379,7 @@ extension BottomSheetViewController: UITableViewDataSource { customCell.addSubview(nameLbl) // Affichage de l'adresse - let addressLbl:UILabel = addLabel(withText: myEquipement.adresse, andTag: 103) + let addressLbl: UILabel = addLabel(withText: myEquipement.adresse, andTag: 103) addressLbl.frame = CGRect(x: nameLbl.frame.origin.x, y: nameLbl.frame.origin.y + 12, width: geolocMainTitle.frame.width + geolocImageView.frame.width, height: 30) addressLbl.numberOfLines = 2 addressLbl.lineBreakMode = .byWordWrapping @@ -1069,10 +1388,10 @@ extension BottomSheetViewController: UITableViewDataSource { // Affichage du nombre d'anomalie let nbAnoText = (myEquipement.anomalies.count > 1) ? "\(myEquipement.anomalies.count) \(Constants.LabelMessage.anomalieCountLabel)" : "\(myEquipement.anomalies.count) \(Constants.LabelMessage.anomalieCountOneLabel)" - let nbAnoLbl:UILabel = addLabel(withText: nbAnoText, andTag: 888) + let nbAnoLbl: UILabel = addLabel(withText: nbAnoText, andTag: 888) nbAnoLbl.frame = CGRect(x: addressLbl.frame.origin.x, y: addressLbl.frame.origin.y + 17, width: addressLbl.frame.width, height: addressLbl.frame.height) - nbAnoLbl.font = UIFont.init(name: Constants.fontDmr, size: 10) - if self.otherCellDisplay { + nbAnoLbl.font = UIFont(name: Constants.fontDmr, size: 10) + if otherCellDisplay { nbAnoLbl.textColor = .white } else { nbAnoLbl.textColor = UIColor.pinkDmr() @@ -1085,7 +1404,6 @@ extension BottomSheetViewController: UITableViewDataSource { } private func showDetailAdresse(forCell customCell: UITableViewCell) { - let geolocImageView = customCell.viewWithTag(101) as! UIImageView let geolocMainTitle = customCell.viewWithTag(102) as! UILabel let geolocSubtitle = customCell.viewWithTag(103) as! UILabel @@ -1094,7 +1412,7 @@ extension BottomSheetViewController: UITableViewDataSource { geolocImageView.contentMode = .center geolocImageView.isUserInteractionEnabled = true - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideUberPin(_:))) + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(hideUberPin(_:))) geolocImageView.addGestureRecognizer(tapGestureRecognizer) if let myAddress = selectedAddress { @@ -1102,12 +1420,12 @@ extension BottomSheetViewController: UITableViewDataSource { geolocMainTitle.text = firstAddressLine MapsUtils.addressLabel = firstAddressLine } else { - //Si l'api renvoi le quartier et non l'adresse + // Si l'api renvoi le quartier et non l'adresse geolocMainTitle.text = myAddress.lines![0] MapsUtils.addressLabel = myAddress.lines![0] MapsUtils.postalCode = myAddress.lines![1].components(separatedBy: " ")[0] var arrondissement = (myAddress.subLocality?.components(separatedBy: CharacterSet.decimalDigits.inverted)[0])! - if(arrondissement=="1") { + if arrondissement == "1" { arrondissement.append(" er") } else { arrondissement.append(" ème") @@ -1127,32 +1445,22 @@ extension BottomSheetViewController: UITableViewDataSource { MapsUtils.locality = locality } - //Gestion de l'affichage du btn de gestion de favoris - let favorites : [String] = getFavoritesAddress() - var isAlreadyInFavorites = false + // Gestion de l'affichage du btn de gestion de favoris + let favorites: [String] = getFavoritesAddress() for favorite in favorites { let favArr = favorite.components(separatedBy: Constants.Key.separatorAdresseCoordonate) - //Vérification sur le nom de l'adresse + // Vérification sur le nom de l'adresse if favArr[0] == MapsUtils.fullAddress() { isAlreadyInFavorites = true } - //Vérification sur les coordonnées + // Vérification sur les coordonnées if String(MapsUtils.userLocation()!.latitude) + "-" + String(MapsUtils.userLocation()!.longitude) == favArr[1] { isAlreadyInFavorites = true } } - if hidemanageFavoriteBtns { - addFavoriteBtn?.isHidden = true - removeFavoriteBtn?.isHidden = true - } else { - addFavoriteBtn?.isHidden = isAlreadyInFavorites - removeFavoriteBtn?.isHidden = !isAlreadyInFavorites - } - - } else { geolocMainTitle.text = "" @@ -1167,22 +1475,12 @@ extension BottomSheetViewController: UITableViewDataSource { } } - private func initButtonManageFavorite( isAddFavorite: Bool) -> UIButton - { - let button = UIButton(frame: CGRect(x:self.view.frame.width - 55, y:+45, width:33, height:33)) - //button.backgroundColor = .clear - //button.imageEdgeInsets = UIEdgeInsets.init(top: -10, left: -10, bottom: -10, right: -10) - button.isUserInteractionEnabled = true - - return button - } - private func addLabel(withText: String, andTag newTag: Int) -> UILabel { - let myLabel:UILabel = UILabel() + let myLabel = UILabel() myLabel.numberOfLines = 1 myLabel.lineBreakMode = .byWordWrapping myLabel.textColor = UIColor.greyDmr() - myLabel.font = UIFont.init(name: Constants.fontDmr, size: 12) + myLabel.font = UIFont(name: Constants.fontDmr, size: 12) myLabel.tag = newTag myLabel.textAlignment = .left myLabel.text = withText @@ -1192,13 +1490,12 @@ extension BottomSheetViewController: UITableViewDataSource { } extension BottomSheetViewController: UIGestureRecognizerDelegate { - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { let gesture = (gestureRecognizer as! UIPanGestureRecognizer) let direction = gesture.velocity(in: view).y let y = view.frame.minY - if (y == fullView && bottomSheetTableView.contentOffset.y == 0 && direction > 0) || (y == partialView) || (y == UIScreen.main.bounds.height - 225) { + if (y == fullView && bottomSheetTableView.contentOffset.y == 0 && direction > 0) || (y == bottomSheetInitialY) || (y == UIScreen.main.bounds.height - 225) { bottomSheetTableView.isScrollEnabled = false } else { bottomSheetTableView.isScrollEnabled = true @@ -1206,31 +1503,48 @@ extension BottomSheetViewController: UIGestureRecognizerDelegate { return false } - } extension BottomSheetViewController: CustomNavigationDelegate { - func displayAddAnomaly(anomalySelected: Anomalie) { let addAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.addAnomaly, bundle: nil) let addAnomalyViewController = addAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.addAnomaly) as! AddAnomalyViewController addAnomalyViewController.currentAnomalie = anomalySelected - self.parent?.navigationController?.navigationBar.isHidden = false - self.parent?.navigationController?.navigationBar.tintColor = UIColor.white - self.parent?.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white]) - self.parent?.navigationController?.pushViewController(addAnomalyViewController, animated: true) + parent?.navigationController?.navigationBar.isHidden = false + parent?.navigationController?.navigationBar.tintColor = UIColor.white + parent?.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white]) + parent?.navigationController?.pushViewController(addAnomalyViewController, animated: true) } - } // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { - guard let input = input else { return nil } - return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)}) +private func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { + guard let input = input else { return nil } + return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value) }) } -//Class tapeGesture perso pour envoyre l'id en param +// Class tapeGesture perso pour envoyre l'id en param class MyTapGesture: UITapGestureRecognizer { var address = String() var addFavorite = Bool() } + +extension UILabel { + var maxNumberOfLines: Int { + let maxSize = CGSize(width: frame.size.width, height: CGFloat(MAXFLOAT)) + let text = (self.text ?? "") as NSString + let textHeight = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height + let lineHeight = font.lineHeight + return Int(ceil(textHeight / lineHeight)) + } +} + +extension BottomSheetViewController: CustomAlertViewDelegate { + func okButtonTapped(textFieldValue: String) { + searchAnomalyByNumber(number: textFieldValue) + } + + func cancelButtonTapped() { + print("cancelButtonTapped") + } +} diff --git a/DansMaRue/Controllers/Map/EquipementSearchTableViewController.swift b/DansMaRue/Controllers/Map/EquipementSearchTableViewController.swift index d3cb54d..2658504 100644 --- a/DansMaRue/Controllers/Map/EquipementSearchTableViewController.swift +++ b/DansMaRue/Controllers/Map/EquipementSearchTableViewController.swift @@ -13,18 +13,23 @@ protocol EquipementDelegate: NSObjectProtocol { } class EquipementSearchTableViewController: UITableViewController { - // MARK: Properties + var equipements = [Equipement]() var filteredEquipements = [Equipement]() - + weak var equipementDelegate: EquipementDelegate? - + override func viewDidLoad() { super.viewDidLoad() tableView.tableFooterView = UIView() - //self.tableView.contentInset = UIEdgeInsetsMake(-10, 0, 0, 0); + // self.tableView.contentInset = UIEdgeInsetsMake(-10, 0, 0, 0); + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } override func didReceiveMemoryWarning() { @@ -33,10 +38,11 @@ class EquipementSearchTableViewController: UITableViewController { } // MARK: - Table view data source + override func numberOfSections(in tableView: UITableView) -> Int { return 1 } - + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 58 } @@ -44,18 +50,18 @@ class EquipementSearchTableViewController: UITableViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var numberOfRows = filteredEquipements.count if filteredEquipements.isEmpty { - let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) - noDataLabel.text = Constants.LabelMessage.equipementSearchNotFound - noDataLabel.textColor = UIColor.greyDmr() + let noDataLabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) + noDataLabel.text = Constants.LabelMessage.equipementSearchNotFound + noDataLabel.textColor = UIColor.greyDmr() noDataLabel.textAlignment = .center noDataLabel.lineBreakMode = .byWordWrapping noDataLabel.numberOfLines = 0 - tableView.backgroundView = noDataLabel - tableView.separatorStyle = .none + tableView.backgroundView = noDataLabel + tableView.separatorStyle = .none numberOfRows = 0 } - + return numberOfRows } @@ -63,9 +69,9 @@ class EquipementSearchTableViewController: UITableViewController { let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) // Configure the cell... - tableView.separatorStyle = .singleLine + tableView.separatorStyle = .singleLine tableView.backgroundView = nil - + let equipement = filteredEquipements[indexPath.row] if let equipementName = cell.viewWithTag(102) as? UILabel { equipementName.text = equipement.name @@ -73,10 +79,10 @@ class EquipementSearchTableViewController: UITableViewController { if let equipementAddress = cell.viewWithTag(103) as? UILabel { equipementAddress.text = equipement.adresse } - + return cell } - + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let equipement = filteredEquipements[indexPath.row] equipementDelegate?.didSelectEquipementAt(equipement: equipement) @@ -86,17 +92,16 @@ class EquipementSearchTableViewController: UITableViewController { extension EquipementSearchTableViewController: UISearchResultsUpdating { func updateSearchResults(for searchController: UISearchController) { if let searchText = searchController.searchBar.text, !searchText.isEmpty { - self.filteredEquipements = self.equipements.filter { equipement in - return equipement.name.lowercased().contains(searchText.lowercased()) || + filteredEquipements = equipements.filter { equipement in + equipement.name.lowercased().contains(searchText.lowercased()) || equipement.name.lowercased().folding(options: .diacriticInsensitive, locale: .current).contains(searchText.lowercased()) || equipement.adresse.lowercased().contains(searchText.lowercased().folding(options: .diacriticInsensitive, locale: .current)) || equipement.adresse.lowercased().contains(searchText.lowercased()) } - + } else { - self.filteredEquipements = self.equipements + filteredEquipements = equipements } tableView.reloadData() } - } diff --git a/DansMaRue/Controllers/Map/MapViewController.swift b/DansMaRue/Controllers/Map/MapViewController.swift index 21cc829..e3d14c9 100644 --- a/DansMaRue/Controllers/Map/MapViewController.swift +++ b/DansMaRue/Controllers/Map/MapViewController.swift @@ -6,15 +6,14 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit import GoogleMaps import GooglePlaces import SwiftyJSON -import Mapbox +import UIKit class MapViewController: UIViewController { - // MARK: Properties + var locationManager = CLLocationManager() // Google SearchBar properties @@ -26,8 +25,8 @@ class MapViewController: UIViewController { var customSearchController: UISearchController? var equipementSearchController: EquipementSearchTableViewController? - let anomalieNotification = Notification.Name(rawValue:Constants.NoticationKey.anomaliesChanged) - let addressNotification = Notification.Name(rawValue:Constants.NoticationKey.addressNotification) + let anomalieNotification = Notification.Name(rawValue: Constants.NoticationKey.anomaliesChanged) + let addressNotification = Notification.Name(rawValue: Constants.NoticationKey.addressNotification) let uberPin = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) var uberPinVisible = false @@ -35,16 +34,17 @@ class MapViewController: UIViewController { var switchButton: UIButton? // MARK: IBOutlet - @IBOutlet weak var mapContainerView: GMSMapView! - @IBOutlet weak var uberActionLabel: UILabel! + @IBOutlet var mapContainerView: GMSMapView! + @IBOutlet var uberActionLabel: UILabel! + @IBOutlet var mapViewConstraintBottom: NSLayoutConstraint! + var bottomSheetConstraintTop: NSLayoutConstraint? - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - locationManager.delegate = self - loadMapContainer() // Gestion des autorisations @@ -53,11 +53,11 @@ class MapViewController: UIViewController { locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters mapContainerView.isMyLocationEnabled = true mapContainerView.settings.myLocationButton = true - locationManager.startUpdatingLocation(); + locationManager.startUpdatingLocation() } else if CLLocationManager.authorizationStatus() == .denied { // L'utilisateur refuse la location. Affichage d'une alert // Open the settings of your app - self.requestAuthorization() + requestAuthorization() positionToParis() } else { // Ask for Authorisation from the User. @@ -68,112 +68,160 @@ class MapViewController: UIViewController { placesClient = GMSPlacesClient() initializeSearchBar() initializeCustomSearchBar() - + uberActionLabel.isHidden = true // Add button to switch anomaly if equipement is available DispatchQueue.global().async { - RestApiManager.sharedInstance.getEquipements{(result: Bool) in + RestApiManager.sharedInstance.getEquipements { (result: Bool) in if result { self.addSwitchButton() } } } - - let url = URL(string: "mapbox://styles/mapbox/streets-v11") - let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), styleURL: url) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.setCenter(CLLocationCoordinate2D(latitude: 59.31, longitude: 18.06), zoomLevel: 9, animated: false) - view.addSubview(mapView) } - + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if #available(iOS 15, *) { + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white] + appearance.backgroundColor = UIColor.pinkDmr() + let proxy = UINavigationBar.appearance() + proxy.tintColor = UIColor.white + proxy.standardAppearance = appearance + proxy.scrollEdgeAppearance = appearance + self.navigationController?.navigationBar.standardAppearance = appearance + self.navigationController?.navigationBar.scrollEdgeAppearance = appearance + } else { + navigationController?.navigationBar.barTintColor = UIColor.white + } + addBottomSheetView() + } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - addBottomSheetView() - + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) // Raffraichissement de la liste des anomalies if ContextManager.shared.typeContribution == .outdoor && MapsUtils.addressLabel == "" { if let location = MapsUtils.userLocation() { - retrieve(currentLocation: location, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: location, addMarker: true) { (_: Bool) in } } } else if ContextManager.shared.typeContribution == .indoor, let equipement = ContextManager.shared.equipementSelected { - self.updateEquipementSelected(forId: equipement.equipementId) + updateEquipementSelected(forId: equipement.equipementId) } uberActionLabel.layer.masksToBounds = true uberActionLabel.layer.cornerRadius = 10 - + uberActionLabel.adjustsFontForContentSizeCategory = true + uberActionLabel.font = UIFont.scaledFont(name: "Montserrat-Bold", textSize: 14.0) } - - - //MARK: - View navigation + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + bottomSheetVC?.view.layoutIfNeeded() + if bottomSheetVC!.currentStatus == .full { + bottomSheetVC?.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height) + } else { + bottomSheetVC?.view.frame = CGRect(x: 0, y: view.frame.height - bottomSheetVC!.getBottomSheetVisibleHieght(), width: view.frame.width, height: view.frame.height) + } + bottomSheetVC?.partialView = view.frame.height - bottomSheetVC!.getBottomSheetVisibleHieght() + bottomSheetVC?.view.layoutIfNeeded() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + bottomSheetVC?.view.layoutIfNeeded() + if bottomSheetVC!.currentStatus == .full { + bottomSheetVC?.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height) + } else { + bottomSheetVC?.view.frame = CGRect(x: 0, y: view.frame.height - bottomSheetVC!.getBottomSheetVisibleHieght(), width: view.frame.width, height: view.frame.height) + } + bottomSheetVC?.partialView = view.frame.height - bottomSheetVC!.getBottomSheetVisibleHieght() + bottomSheetVC?.view.layoutIfNeeded() + } + + // MARK: - View navigation + /// Méthode permettant d'initialiser et de rajouter la bottomsheet sur la vue /// func addBottomSheetView() { let mapStoryboard = UIStoryboard(name: Constants.StoryBoard.map, bundle: nil) if bottomSheetVC == nil { - bottomSheetVC = mapStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.bottomSheet) as?BottomSheetViewController + bottomSheetVC = mapStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.bottomSheet) as? BottomSheetViewController // 2- Add bottomSheetVC as a child view and set its delegate bottomSheetVC?.uberDelegate = self bottomSheetVC?.delegate = self } - bottomSheetVC?.partialView = self.mapContainerView.frame.origin.y + self.mapContainerView.frame.height - self.addChild(bottomSheetVC!) - self.view.addSubview((bottomSheetVC?.view)!) + bottomSheetVC?.partialView = mapContainerView.frame.origin.y + mapContainerView.frame.height + addChild(bottomSheetVC!) + view.addSubview((bottomSheetVC?.view)!) bottomSheetVC?.didMove(toParent: self) // 3- Adjust bottomSheet frame and initial position. - bottomSheetVC?.view.frame = CGRect(x: 0, y: self.view.frame.maxY, width: view.frame.width, height: view.frame.height) + // bottomSheetVC?.view.frame = CGRect(x: 0, y: view.frame.maxY, width: view.frame.width, height: view.frame.height) + bottomSheetConstraintTop = bottomSheetVC?.view.topAnchor.constraint(equalTo: mapContainerView.bottomAnchor) + activateTopConstraint(true) + bottomSheetVC?.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + bottomSheetVC?.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + // bottomSheetVC?.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + bottomSheetVC?.view.heightAnchor.constraint(equalToConstant: view.frame.height).isActive = true } + func activateTopConstraint(_ state: Bool) { + bottomSheetConstraintTop?.isActive = state + } + /// Méthode permettant d'initialiser la barre de recherche d'adresse pour l'espace public /// - func initializeSearchBar() { + func initializeSearchBar() { resultsViewController = GMSAutocompleteResultsViewController() resultsViewController?.delegate = self searchController = UISearchController(searchResultsController: resultsViewController) searchController?.searchResultsUpdater = resultsViewController - // Put the search bar in the navigation bar. if let searchBar = searchController?.searchBar { - + UIAccessibility.post(notification: .layoutChanged, argument: searchBar) searchBar.placeholder = Constants.PlaceHolder.saisirAdresse - searchBar.tintColor = UIColor.white searchBar.isTranslucent = false + searchBar.accessibilityLabel = Constants.PlaceHolder.saisirAdresse + searchBar.accessibilityHint = Constants.AccessibilityHint.searchBarHint + searchBar.accessibilityTraits = .searchField + if #available(iOS 13.0, *) { - searchBar.searchTextField.backgroundColor=UIColor.white - searchBar.searchTextField.tintColor=UIColor.black + searchBar.searchTextField.backgroundColor = UIColor.white + searchBar.searchTextField.tintColor = UIColor.black + searchBar.searchTextField.adjustsFontForContentSizeCategory = true + searchBar.searchTextField.font = UIFont.preferredFont(forTextStyle: .caption2) let appearance = UINavigationBarAppearance() appearance.configureWithOpaqueBackground() appearance.backgroundColor = UIColor.pinkButtonDmr() - self.navigationController?.navigationBar.standardAppearance = appearance; + self.navigationController?.navigationBar.standardAppearance = appearance self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance } - searchBar.layer.cornerRadius = 10; - self.setNavigationTitleView(withSearchBar: searchBar) - + searchBar.layer.cornerRadius = 10 + setNavigationTitleView(withSearchBarController: searchController!) } - self.navigationController?.navigationBar.isTranslucent = false - self.navigationController?.navigationBar.barTintColor = UIColor.pinkButtonDmr() - self.extendedLayoutIncludesOpaqueBars = true - + navigationController?.navigationBar.isTranslucent = false + navigationController?.navigationBar.barTintColor = UIColor.pinkButtonDmr() + extendedLayoutIncludesOpaqueBars = true // When UISearchController presents the results view, present it in // this view controller, not one further up the chain. - self.definesPresentationContext = true + definesPresentationContext = true // Prevent the navigation bar from being hidden when searching. searchController?.hidesNavigationBarDuringPresentation = false // Active sur le filtre sur Paris uniquement - MapsUtils.filterToParis(resultsViewController: self.resultsViewController!) + MapsUtils.filterToParis(resultsViewController: resultsViewController!) } /// Méthode permettant d'initialiser la barre de recherche des équipements pour l'indoor. @@ -198,22 +246,24 @@ class MapViewController: UIViewController { /// Permet de positionner la SearchBar spécifié sur la barre de navigation /// - func setNavigationTitleView(withSearchBar searchBar: UISearchBar) { - if #available(iOS 11.0, *) { - // For iOS 11+, fix size to 32 for navigationbar - //searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true - let searchBarContainer = SearchBarContainerView(customSearchBar: searchBar) - searchBarContainer.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 32) - self.navigationItem.titleView = searchBarContainer - } else { - searchBar.sizeToFit() - self.navigationItem.titleView = searchBar + func setNavigationTitleView(withSearchBarController searchBarController: UISearchController) { + navigationItem.searchController = searchBarController + navigationItem.title = Constants.TabBarTitle.carte + navigationItem.titleView?.isUserInteractionEnabled = true + navigationItem.titleView?.becomeFirstResponder() + if #available(iOS 13.0, *) { + navigationItem.titleView?.accessibilityRespondsToUserInteraction = true } - + navigationItem.titleView?.accessibilityLabel = Constants.TabBarTitle.carte + navigationItem.titleView?.accessibilityTraits = .header + navigationItem.titleView?.isAccessibilityElement = true + let menuBtn = UIButton(type: .custom) menuBtn.frame = CGRect(x: 0.0, y: 0.0, width: 44, height: 44) - menuBtn.setImage(UIImage(named:Constants.Image.favorite), for: .normal) - menuBtn.addTarget(self, action: #selector(addTapped), for:.touchDown) + menuBtn.setImage(UIImage(named: Constants.Image.favorite), for: .normal) + menuBtn.addTarget(self, action: #selector(addTapped), for: .touchDown) + menuBtn.accessibilityTraits = .button + menuBtn.accessibilityLabel = Constants.AccessibilityLabel.favoriteAdressButton let menuBarItem = UIBarButtonItem(customView: menuBtn) let currWidth = menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 25) @@ -221,26 +271,25 @@ class MapViewController: UIViewController { let currHeight = menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 25) currHeight?.isActive = true - self.navigationItem.rightBarButtonItem = menuBarItem + navigationItem.rightBarButtonItem = menuBarItem } @objc func addTapped() { let manageAddressVC = UIStoryboard(name: Constants.StoryBoard.manageAddress, bundle: nil).instantiateInitialViewController() as! ManageAddressViewController manageAddressVC.delegate = self - self.navigationController?.pushViewController(manageAddressVC, animated: true) + navigationController?.pushViewController(manageAddressVC, animated: true) } /// Ajout d'un bouton sur la carte permettant de sélectionner le type de contribution func addSwitchButton() { - - //Si equipement est lancé, on ajoute le bouton de selection du type + // Si equipement est lancé, on ajoute le bouton de selection du type DispatchQueue.global().async { - RestApiManager.sharedInstance.getEquipements{(result: Bool) in - if result { + RestApiManager.sharedInstance.getEquipements { (result: Bool) in + if result { if UIDevice().userInterfaceIdiom == .phone { switch UIScreen.main.nativeBounds.height { case 2436: - //Iphone x - position du switchButton plus basse + // Iphone x - position du switchButton plus basse self.switchButton = UIButton(frame: CGRect(x: 15, y: 100, width: self.view.frame.size.width - 30, height: 40)) default: self.switchButton = UIButton(frame: CGRect(x: 15, y: 80, width: self.view.frame.size.width - 30, height: 40)) @@ -257,7 +306,7 @@ class MapViewController: UIViewController { self.switchButton?.backgroundColor = UIColor.pinkButtonDmr() self.switchButton?.alpha = 0.8 - self.switchButton?.addTarget(self, action: #selector(MapViewController.switchButtonAction(_: )), for: .touchUpInside) + self.switchButton?.addTarget(self, action: #selector(MapViewController.switchButtonAction(_:)), for: .touchUpInside) self.view.addSubview(self.switchButton!) } @@ -266,23 +315,23 @@ class MapViewController: UIViewController { } // MARK: - Other Methods + private func loadMapContainer() { mapContainerView.mapType = GMSMapViewType.terrain - + mapContainerView.isAccessibilityElement = true // Permet de décaler les donnees Google (Logo, icone, ...) - let mapInsets = UIEdgeInsets.init(top: 0.0, left: 0.0, bottom: 30.0, right: 0.0) + let mapInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 30.0, right: 0.0) mapContainerView.padding = mapInsets mapContainerView.delegate = self - } /// Action sur la bouton de type de contribution. /// Ouverture d'un nouvel écran permettant de sélectionner le type de contribution /// - @objc func switchButtonAction(_:Any) { + @objc func switchButtonAction(_: Any) { let typeContributionView = UIStoryboard(name: Constants.StoryBoard.typeContribution, bundle: nil).instantiateInitialViewController() as! TypeContributionViewController typeContributionView.delegate = self - self.navigationController?.pushViewController(typeContributionView, animated: true) + navigationController?.pushViewController(typeContributionView, animated: true) } /// Methode permettant de changer le type de contribution. @@ -290,30 +339,29 @@ class MapViewController: UIViewController { /// /// - Parameter withName: String - Libellé à appliquer sur le bouton func changeTypeContribution(withName buttonTitle: String) { - self.switchButton?.setTitle(buttonTitle, for: .normal) + switchButton?.setTitle(buttonTitle, for: .normal) // Changement de la searchbar en fonction du type de contribution if ContextManager.shared.typeContribution == .outdoor { // Changement du placeholder pour anomalie outdoor searchController?.searchBar.placeholder = Constants.PlaceHolder.saisirAdresse - self.setNavigationTitleView(withSearchBar: (searchController?.searchBar)!) + setNavigationTitleView(withSearchBarController: searchController!) } else { // Changement du placeholder en fonction du Type Equipement customSearchController?.searchBar.placeholder = ContextManager.shared.typeEquipementSelected?.placeholder equipementSearchController?.equipements = ReferalManager.shared.getEquipements(forTypeEquipementId: (ContextManager.shared.typeEquipementSelected?.typeEquipementId)!)! equipementSearchController?.equipements.sort(by: { $0.name.caseInsensitiveCompare($1.name) == .orderedAscending }) equipementSearchController?.tableView.reloadData() - self.setNavigationTitleView(withSearchBar: (customSearchController?.searchBar)!) + setNavigationTitleView(withSearchBarController: searchController!) // hide uber label - self.shouldDisplayUberPin(yesWeCan: false) - self.bottomSheetVC?.uberDisplayed = false + shouldDisplayUberPin(yesWeCan: false) + bottomSheetVC?.uberDisplayed = false } if let position = MapsUtils.userLocation() { - retrieve(currentLocation: position, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: position, addMarker: true) { (_: Bool) in } } - } /// Permet d'ajouter un GMSMarker et de positionner la caméra au centre de Paris. @@ -325,27 +373,25 @@ class MapViewController: UIViewController { MapsUtils.addMarker(withName: "Paris", coordinate: CLLocationCoordinate2DMake(Constants.Maps.parisLatitude, Constants.Maps.parisLongitude), inMap: mapContainerView) } - ///Demande à l'utilisateur l'accès au service de localisation + /// Demande à l'utilisateur l'accès au service de localisation /// private func requestAuthorization() { - let alertController = UIAlertController (title: Constants.AlertBoxTitle.locationDisabled, message: Constants.AlertBoxMessage.locationDisabled, preferredStyle: UIAlertController.Style.alert) + let alertController = UIAlertController(title: Constants.AlertBoxTitle.locationDisabled, message: Constants.AlertBoxMessage.locationDisabled, preferredStyle: UIAlertController.Style.alert) - let okBtn = UIAlertAction(title:"OK" , style: .default, handler: {(_ action: UIAlertAction) -> Void in + let okBtn = UIAlertAction(title: "OK", style: .default, handler: { (_: UIAlertAction) in }) alertController.addAction(okBtn) - self.present(alertController, animated: true, completion: nil) - + present(alertController, animated: true, completion: nil) } func didTapMyLocationButton(for mapView: GMSMapView) -> Bool { if let location = mapView.myLocation?.coordinate { - let cameraUpdate = GMSCameraUpdate.setTarget(location, zoom: Constants.Maps.zoomLevel_50m) mapView.animate(with: cameraUpdate) - retrieve(currentLocation: location, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: location, addMarker: true) { (_: Bool) in } } return true } @@ -356,11 +402,11 @@ class MapViewController: UIViewController { /// - location: Coordonnées de la position de l'utilisateur /// - addMarker: Flag indiquant si le marqueur de position doit etre rajouter /// - onCompletion: true : si la recherche a aboutie, false sinon - func retrieve(currentLocation location:CLLocationCoordinate2D, addMarker: Bool, address: String="", cp: String="", onCompletion: @escaping (Bool) -> Void) { - self.mapContainerView.clear() + func retrieve(currentLocation location: CLLocationCoordinate2D, addMarker: Bool, address: String = "", cp: String = "", onCompletion: @escaping (Bool) -> Void) { + mapContainerView.clear() if ContextManager.shared.typeContribution == .outdoor { - MapsUtils.addMarker(withName: MapsUtils.fullAddress(), coordinate: location, inMap: self.mapContainerView) + MapsUtils.addMarker(withName: MapsUtils.fullAddress(), coordinate: location, inMap: mapContainerView) } if Reach().connectionStatus() { @@ -383,7 +429,6 @@ class MapViewController: UIViewController { RestApiManager.sharedInstance.getIncidentsByPosition(coordinates: location) { (anomalies: [Anomalie]) in if ContextManager.shared.typeContribution == .outdoor { DispatchQueue.main.async { - for anomalie in anomalies { // Ajout du GMSMarker sur la map self.addMarkerAnomalie(anomalie: anomalie) @@ -400,32 +445,9 @@ class MapViewController: UIViewController { } } - // Cas du mode Espace public. On recherche les anomalies Ramen à proximite - DispatchQueue.global().async { - RestApiManager.sharedInstance.getDossierRamenByPosition(coordinates: location) { (anomalies: [Anomalie]) in - if ContextManager.shared.typeContribution == .outdoor { - DispatchQueue.main.async { - for anomalie in anomalies { - // Ajout du GMSMarker sur la map - self.addMarkerAnomalie(anomalie: anomalie) - - - if anomalie.anomalieStatus != .Resolu && !anomaliesBottomSheet.contains(anomalie) { - anomaliesBottomSheet.append(anomalie) - } - } - - NotificationCenter.default.post(name: self.anomalieNotification, object: anomaliesBottomSheet) - onCompletion(true) - } - } - } - } - } else if ContextManager.shared.typeContribution == .indoor { // Cas de la sélection d'un TypeEquipement, on affiche les équipements autour de nous DispatchQueue.global().async { - RestApiManager.sharedInstance.getEquipementByPosition(coordinates: location) { (equipements: [Equipement]) in if ContextManager.shared.typeContribution == .indoor { @@ -442,7 +464,6 @@ class MapViewController: UIViewController { } } } - } NotificationCenter.default.post(name: self.anomalieNotification, object: anomaliesBottomSheet) @@ -457,34 +478,32 @@ class MapViewController: UIViewController { MapsUtils.set(userLocation: location) let nc = NotificationCenter.default - if("" != address) { + if !address.isEmpty { addressFound.firstResult()?.setValue(address.components(separatedBy: ",")[0], forKey: "thoroughfare") } - if("" != cp) { + if !cp.isEmpty { addressFound.firstResult()?.setValue(cp, forKey: "postalCode") } - nc.post(name: self.addressNotification, object: addressFound.firstResult(), userInfo: ["":""]) + nc.post(name: self.addressNotification, object: addressFound.firstResult(), userInfo: ["": ""]) } } else { // Device en mode déconnecté MapsUtils.set(userLocation: location) - let nc = NotificationCenter.default - //nc.post(name: self.addressNotification, object: location) - self.mapContainerView.clear() - NotificationCenter.default.post(name: self.anomalieNotification, object: [Anomalie]()) + // nc.post(name: self.addressNotification, object: location) + mapContainerView.clear() + NotificationCenter.default.post(name: anomalieNotification, object: [Anomalie]()) onCompletion(false) } - } /// Methode permettant de récuperer le n° d'une adresse /// /// - Parameter street: l'adresse complete - func getStreetNumber(adresse : String) -> String { + func getStreetNumber(adresse: String) -> String { var number = "" var hasValue = false @@ -492,14 +511,13 @@ class MapViewController: UIViewController { for char in adresse { let str = String(char) // Checks if the char is a number - if (Int(str) != nil){ + if Int(str) != nil { // If it is it appends it to number - number+=str + number += str // Here we set the hasValue to true, beacause the street number will come in one order hasValue = true - } - else{ - if(hasValue){ + } else { + if hasValue { break } } @@ -518,11 +536,9 @@ class MapViewController: UIViewController { /// Methode permettant de recherche les informations d'un équipement avec anomalies et mise à jour de la bottomsheet /// /// - Parameter equipementId: Identifiant de l'équipement - func updateEquipementSelected(forId equipementId:String) { - + func updateEquipementSelected(forId equipementId: String) { // Cas de la sélection d'un TypeEquipement, on affiche les équipements autour de nous DispatchQueue.global().async { - RestApiManager.sharedInstance.getIncidentsByEquipement(equipementId: equipementId) { (equipement: Equipement) in DispatchQueue.main.async { @@ -539,7 +555,6 @@ class MapViewController: UIViewController { self.bottomSheetVC?.animateBottomSheet(withDuration: 0, status: .none) self.bottomSheetVC?.showEquipement(equipement: ReferalManager.shared.getEquipement(forId: equipementId)!) - } } } @@ -549,7 +564,6 @@ class MapViewController: UIViewController { /// /// - Parameter anomalie: Anomalie - Instance de l'anomalie permettant la création du marker func addMarkerAnomalie(anomalie: Anomalie) { - DispatchQueue.main.async { let position = CLLocationCoordinate2D(latitude: CLLocationDegrees(anomalie.latitude), longitude: CLLocationDegrees(anomalie.longitude)) @@ -565,8 +579,7 @@ class MapViewController: UIViewController { if anomalie.anomalieStatus == .Resolu { var iconAnomalie = UIImage(named: Constants.Image.anoDoneOther)! var iconCateg = UIImage(named: "ano_done_other") - if anomalie.source == .ramen - { + if anomalie.source == .ramen { iconCateg = UIImage(named: "ano_done_1000") } else { @@ -588,7 +601,8 @@ class MapViewController: UIViewController { } else { if let anoCateg = UIImage(named: "ano_\(categParentId)") { iconCateg = anoCateg - } } + } + } iconAnomalie = iconCateg! markerMap.icon = iconAnomalie @@ -605,7 +619,7 @@ class MapViewController: UIViewController { markerEquipement.position = CLLocationCoordinate2D(latitude: equipement.latitude, longitude: equipement.longitude) markerEquipement.appearAnimation = .pop markerEquipement.title = equipement.name - markerEquipement.map = self.mapContainerView + markerEquipement.map = mapContainerView markerEquipement.equipement = equipement let typeEquipement = ReferalManager.shared.getTypeEquipement(forId: equipement.parentId) @@ -622,12 +636,10 @@ class MapViewController: UIViewController { } } - // MARK: - GMSMapView Delegate + extension MapViewController: GMSMapViewDelegate { - func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { - if case let markerDmr as GMSMarkerAnomalie = marker { print("Select marker anomalie id \(markerDmr.anomalie?.id ?? "unkown")") self.bottomSheetVC?.showAnomalie(anomalie: markerDmr.anomalie!) @@ -641,96 +653,89 @@ extension MapViewController: GMSMapViewDelegate { } func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) { - self.bottomSheetVC?.selectAnomalie = nil + bottomSheetVC?.selectAnomalie = nil // Par defaut, utilise les coordonnees de la position de la camera var latitude = position.target.latitude var longitude = position.target.longitude // Sinon si uberPin visible, utilise la position du uberPin - if self.uberPinVisible { + if uberPinVisible { let uberPointCenter = CGPoint(x: uberPin.center.x, y: uberPin.frame.maxY) - let uberPointCoordinates = self.mapContainerView.projection.coordinate(for: uberPointCenter) + let uberPointCoordinates = mapContainerView.projection.coordinate(for: uberPointCenter) latitude = uberPointCoordinates.latitude longitude = uberPointCoordinates.longitude } - if self.uberPinVisible { + if uberPinVisible { let coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) - retrieve(currentLocation: coordinates, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: coordinates, addMarker: true) { (_: Bool) in } } } func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { // Function to remove selection in the bottomSheet when Tap into the map - self.bottomSheetVC?.selectAnomalie = nil - self.bottomSheetVC?.selectEquipement = nil - self.bottomSheetVC?.animateBottomSheet(withDuration: 0, status: (self.bottomSheetVC?.currentStatus)!) + bottomSheetVC?.selectAnomalie = nil + bottomSheetVC?.selectEquipement = nil + bottomSheetVC?.animateBottomSheet(withDuration: 0, status: (bottomSheetVC?.currentStatus)!) } func mapView(_ mapView: GMSMapView, didLongPressAt coordinate: CLLocationCoordinate2D) { - retrieve(currentLocation: coordinate, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: coordinate, addMarker: true) { (_: Bool) in } } } // MARK: - CLLocationManager Delegate + extension MapViewController: CLLocationManagerDelegate { - func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { - - if (status == CLAuthorizationStatus.authorizedWhenInUse) { + if status == CLAuthorizationStatus.authorizedWhenInUse { print("locationManager.didChangeAuthorization : authorizedWhenInUse") mapContainerView.isMyLocationEnabled = true mapContainerView.settings.myLocationButton = true - locationManager.startUpdatingLocation(); - } else if (status == CLAuthorizationStatus.denied) { + locationManager.startUpdatingLocation() + } else if status == CLAuthorizationStatus.denied { print("locationManager.didChangeAuthorization : denied") mapContainerView.isMyLocationEnabled = false mapContainerView.settings.myLocationButton = false } } - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - if MapsUtils.userLocation() == nil { locationManager.stopMonitoringSignificantLocationChanges() let coordinates = (locations.last?.coordinate)! let cameraUpdate = GMSCameraUpdate.setTarget(coordinates, zoom: Constants.Maps.zoomLevel_50m) - self.mapContainerView.animate(with: cameraUpdate) + mapContainerView.animate(with: cameraUpdate) - retrieve(currentLocation: coordinates, addMarker: true) { (result: Bool) in } + retrieve(currentLocation: coordinates, addMarker: true) { (_: Bool) in } locationManager.stopUpdatingLocation() } - } - } extension MapViewController: UberDelegate { - func shouldDisplayUberPin(yesWeCan: Bool) { if yesWeCan { uberPin.image = UIImage(named: Constants.Image.pinNoir) - uberPin.center = self.mapContainerView.center - self.view.addSubview(uberPin) - self.view.bringSubviewToFront(uberPin) - self.uberPinVisible = true + uberPin.center = mapContainerView.center + view.addSubview(uberPin) + view.bringSubviewToFront(uberPin) + uberPinVisible = true uberActionLabel.isHidden = false } else { uberPin.removeFromSuperview() - self.uberPinVisible = false + uberPinVisible = false uberActionLabel.isHidden = true } } } - // Handle the user's selection. extension MapViewController: GMSAutocompleteResultsViewControllerDelegate { - func resultsController(_ resultsController: GMSAutocompleteResultsViewController, - didAutocompleteWith place: GMSPlace) { + didAutocompleteWith place: GMSPlace) + { searchController?.isActive = false // Cache le uberPin @@ -760,13 +765,12 @@ extension MapViewController: GMSAutocompleteResultsViewControllerDelegate { cp = component.name } } - let adresse = numRue + " " + rue + ", " + cp + " Paris, France" - - retrieve(currentLocation: place.coordinate, addMarker: true, address: adresse, cp: cp) { (result: Bool) in } + let adresse = numRue + " " + rue + ", " + cp + " PARIS, France" + retrieve(currentLocation: place.coordinate, addMarker: true, address: adresse, cp: cp) { (_: Bool) in } } - func centerCameraToPosition(currentLocation location:CLLocationCoordinate2D) { + func centerCameraToPosition(currentLocation location: CLLocationCoordinate2D) { if let mapView = mapContainerView { // Positionnement de la caméra au centre du marker let camera = GMSCameraPosition.camera(withLatitude: location.latitude, @@ -777,7 +781,8 @@ extension MapViewController: GMSAutocompleteResultsViewControllerDelegate { } func resultsController(_ resultsController: GMSAutocompleteResultsViewController, - didFailAutocompleteWithError error: Error){ + didFailAutocompleteWithError error: Error) + { // TODO: handle the error. print("Error: ", error.localizedDescription) } @@ -790,15 +795,12 @@ extension MapViewController: GMSAutocompleteResultsViewControllerDelegate { func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) { UIApplication.shared.isNetworkActivityIndicatorVisible = false } - - } extension MapViewController: EquipementDelegate { func didSelectEquipementAt(equipement: Equipement) { customSearchController?.isActive = false if let mapView = mapContainerView { - // Positionnement de la caméra au centre du marker let camera = GMSCameraPosition.camera(withLatitude: equipement.latitude, longitude: equipement.longitude, @@ -807,20 +809,19 @@ extension MapViewController: EquipementDelegate { } if Reach().connectionStatus() { - self.updateEquipementSelected(forId: equipement.equipementId) + updateEquipementSelected(forId: equipement.equipementId) // on affiche le marker de l'equipement - self.addMarkerEquipement(equipement: equipement) + addMarkerEquipement(equipement: equipement) } else { // Mode hors connexion, on affiche uniquement le marker de l'equipement - self.addMarkerEquipement(equipement: equipement) + addMarkerEquipement(equipement: equipement) // Affiche des informations de l'équipement sélectionné - self.bottomSheetVC?.showEquipement(equipement: ReferalManager.shared.getEquipement(forId: equipement.equipementId)!) + bottomSheetVC?.showEquipement(equipement: ReferalManager.shared.getEquipement(forId: equipement.equipementId)!) } } } class SearchBarContainerView: UIView { - let searchBar: UISearchBar init(customSearchBar: UISearchBar) { @@ -835,6 +836,7 @@ class SearchBarContainerView: UIView { self.frame = frame } + @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/DansMaRue/Controllers/Map/TypeContributionViewController.swift b/DansMaRue/Controllers/Map/TypeContributionViewController.swift index 96eed6f..09dbe99 100644 --- a/DansMaRue/Controllers/Map/TypeContributionViewController.swift +++ b/DansMaRue/Controllers/Map/TypeContributionViewController.swift @@ -9,37 +9,41 @@ import UIKit class TypeContributionViewController: UIViewController { - var types = [TypeEquipement]() weak var delegate: MapViewController! - struct RowId { + enum RowId { static let espacePublic = 0 static let equipement = 1 static let other = 2 } - //MARK: IBOutlets - @IBOutlet weak var tableView: UITableView! + // MARK: IBOutlets + + @IBOutlet var tableView: UITableView! override func viewDidLoad() { - tableView.tableFooterView = UIView() - self.navigationItem.title = Constants.LabelMessage.typeContributionLabel + navigationItem.title = Constants.LabelMessage.typeContributionLabel navigationItem.leftBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(backAction)) if let image = UIImage(named: Constants.Image.iconExit) { navigationItem.leftBarButtonItem?.image = image } types.append(contentsOf: ReferalManager.shared.typeEquipements.values) - } - //MARK: Other Methods + // MARK: Other Methods + @objc func backAction() { _ = navigationController?.popViewController(animated: true) } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + } } extension TypeContributionViewController: UITableViewDelegate { @@ -47,18 +51,17 @@ extension TypeContributionViewController: UITableViewDelegate { switch indexPath.row { case RowId.espacePublic: ContextManager.shared.typeContribution = .outdoor - self.delegate.changeTypeContribution(withName: Constants.LabelMessage.defaultTypeContributionLabel) + delegate.changeTypeContribution(withName: Constants.LabelMessage.defaultTypeContributionLabel) default: ContextManager.shared.typeContribution = .indoor ContextManager.shared.typeEquipementSelected = types[indexPath.row - 1] - self.delegate.changeTypeContribution(withName: types[indexPath.row - 1].name) + delegate.changeTypeContribution(withName: types[indexPath.row - 1].name) } _ = navigationController?.popViewController(animated: true) } } extension TypeContributionViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return types.count + 1 } @@ -81,6 +84,4 @@ extension TypeContributionViewController: UITableViewDataSource { return defaultCell! } - } - diff --git a/DansMaRue/Controllers/Profile/CompteParisienViewController.swift b/DansMaRue/Controllers/Profile/CompteParisienViewController.swift index 3128441..288641c 100644 --- a/DansMaRue/Controllers/Profile/CompteParisienViewController.swift +++ b/DansMaRue/Controllers/Profile/CompteParisienViewController.swift @@ -38,7 +38,7 @@ class CompteParisienViewController: UIViewController, SFSafariViewControllerDele DispatchQueue.main.async { if result { - TTGSnackbar.init(message: Constants.AlertBoxMessage.authenticationOk, duration: .middle).show() + // TTGSnackbar.init(message: Constants.AlertBoxMessage.authenticationOk, duration: .middle).show() self.backToCompteParisien(self) } else { let alertController = UIAlertController (title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) @@ -66,7 +66,7 @@ class CompteParisienViewController: UIViewController, SFSafariViewControllerDele @IBAction func didTapForgetPwd(_ sender: Any) { let urlForgetPassword = Constants.Services.urlForgetPassword if let url = URL(string: urlForgetPassword) { - let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true) + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) vc.delegate = self present(vc, animated: true) @@ -76,7 +76,7 @@ class CompteParisienViewController: UIViewController, SFSafariViewControllerDele @IBAction func didTapRegister(_ sender: Any) { let urlRegiserCompteParisien = Constants.Services.urlRegiserCompteParisien if let url = URL(string: urlRegiserCompteParisien) { - let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true) + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) vc.delegate = self present(vc, animated: true) diff --git a/DansMaRue/Controllers/Profile/ProfileAboutViewController.swift b/DansMaRue/Controllers/Profile/ProfileAboutViewController.swift index f97e78c..f7435de 100644 --- a/DansMaRue/Controllers/Profile/ProfileAboutViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileAboutViewController.swift @@ -9,21 +9,32 @@ import UIKit class ProfileAboutViewController: UIViewController { + // MARK: - IBOutlets - //MARK: - IBOutlets + @IBOutlet var aboutDescriptionTextView: UITextView! + @IBOutlet var subTitle: UILabel! + + // MARK: - View lifecycle - @IBOutlet weak var aboutDescriptionTextView: UITextView! - @IBOutlet weak var subTitle: UILabel! - - //MARK: - View lifecycle override func viewDidLoad() { super.viewDidLoad() - - self.title = Constants.TabBarTitle.monEspace - subTitle.text = Constants.LabelMessage.about + title = Constants.TabBarTitle.monEspace + navigationItem.titleView?.isAccessibilityElement = true + subTitle.text = Constants.LabelMessage.about + subTitle.isAccessibilityElement = true + subTitle.textColor = UIColor.greyDmr() + subTitle.accessibilityLabel = Constants.LabelMessage.about + subTitle.accessibilityTraits = .header + subTitle.adjustsFontForContentSizeCategory = true + subTitle.font = UIFont.preferredFont(forTextStyle: .title2) + aboutDescriptionTextView.adjustsFontForContentSizeCategory = true aboutDescriptionTextView.text = "Version : \(Bundle.main.version) (\(Bundle.main.build))" - aboutDescriptionTextView.font = UIFont(name: (aboutDescriptionTextView.font?.fontName)!, size: 15) + aboutDescriptionTextView.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 15.0) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + } } diff --git a/DansMaRue/Controllers/Profile/ProfileActualitesViewController.swift b/DansMaRue/Controllers/Profile/ProfileActualitesViewController.swift index 2e0d128..388a4a1 100644 --- a/DansMaRue/Controllers/Profile/ProfileActualitesViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileActualitesViewController.swift @@ -9,41 +9,61 @@ import UIKit class ProfileActualitesViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { - - @IBOutlet weak var subTitle: UILabel! - @IBOutlet weak var actualitesTableView: UITableView! + @IBOutlet var subTitle: UILabel! + @IBOutlet var actualitesTableView: UITableView! var actualites = [Actualite]() var rowSelected = -1 var sectionsShow = Set() - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - actualitesTableView.register(SectionCustomHeader.self, forHeaderFooterViewReuseIdentifier: "sectionHeader") + actualitesTableView.delegate = self + actualitesTableView.dataSource = self + actualitesTableView.separatorStyle = UITableViewCell.SeparatorStyle.none + actualitesTableView.estimatedRowHeight = 116 + actualitesTableView.estimatedSectionHeaderHeight = 116 + actualitesTableView.rowHeight = UITableView.automaticDimension + actualitesTableView.sectionHeaderHeight = UITableView.automaticDimension - self.actualitesTableView.delegate = self - self.actualitesTableView.dataSource = self - self.actualitesTableView.separatorStyle = UITableViewCell.SeparatorStyle.none - - self.title = Constants.TabBarTitle.monEspace + title = Constants.TabBarTitle.monEspace subTitle.text = Constants.LabelMessage.actualites + subTitle.isAccessibilityElement = true + subTitle.textColor = UIColor.greyDmr() + subTitle.accessibilityLabel = Constants.LabelMessage.actualites + subTitle.accessibilityTraits = .header + subTitle.adjustsFontForContentSizeCategory = true + subTitle.font = UIFont.preferredFont(forTextStyle: .title3) - if let actualites = ReferalManager.shared.getActualites() { + if let actualites = ReferalManager.shared.getActualites() { self.actualites.append(contentsOf: actualites) } } + + override func viewDidAppear(_ animated: Bool) { + actualitesTableView.layoutIfNeeded() + actualitesTableView.reloadData() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let actualite = actualites[indexPath.section] let actualitesCell = tableView.dequeueReusableCell(withIdentifier: "actualites_cell") + actualitesCell?.isAccessibilityElement = true + actualitesCell?.accessibilityTraits = .staticText + actualitesCell?.accessibilityLabel = actualite.texte.htmlToString let actualiteTexte = actualitesCell?.viewWithTag(102) as! UILabel + actualiteTexte.isAccessibilityElement = true + actualiteTexte.accessibilityTraits = .staticText let font = UIFont.systemFont(ofSize: 14) let attributes = [NSAttributedString.Key.font: font] - let attributedQuote = NSAttributedString(string: actualite.texte.htmlToString, attributes: attributes) + _ = NSAttributedString(string: actualite.texte.htmlToString, attributes: attributes) actualiteTexte.attributedText = actualite.texte.htmlToAttributedString - + actualiteTexte.adjustsFontForContentSizeCategory = true + actualiteTexte.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 18.0) return actualitesCell! } @@ -52,35 +72,47 @@ class ProfileActualitesViewController: UIViewController, UITableViewDataSource, } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if self.sectionsShow.contains(section) { + if sectionsShow.contains(section) { return 1 - } - else { + } else { return 0 } } - + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "sectionHeader") as! SectionCustomHeader + let view = tableView.dequeueReusableCell(withIdentifier: "header_cell") let actualite = actualites[section] let imageURL = URL(string: actualite.imageUrl)! - - view.image.sd_setImage(with: imageURL, placeholderImage: nil, options: .allowInvalidSSLCertificates) - - view.sectionTitle.setTitle(actualite.libelle, for: .normal) - view.sectionTitle.setTitleColor(UIColor.orange, for: .normal) - view.sectionTitle.addTarget(self,action: #selector(self.hideSection(sender:)),for: .touchUpInside) - view.sectionTitle.tag = section - view.sectionTitle.addTarget(self,action: #selector(self.hideSection(sender:)),for: .touchUpInside) - view.sectionTitle.titleLabel!.numberOfLines = 0; - view.sectionTitle.titleLabel!.lineBreakMode = NSLineBreakMode.byWordWrapping; + let imageView = view?.viewWithTag(100) as! UIImageView + let titleLabel = view?.viewWithTag(101) as! UILabel + imageView.sd_setImage(with: imageURL, placeholderImage: nil, options: .allowInvalidSSLCertificates) + imageView.isAccessibilityElement = false + view?.isAccessibilityElement = true + view?.accessibilityLabel = actualite.libelle + view?.accessibilityTraits = .button + titleLabel.text = actualite.libelle + titleLabel.accessibilityTraits = .staticText + titleLabel.tintColor = UIColor(hexString: "#C60") + titleLabel.textColor = UIColor(hexString: "#C60") + view?.tag = section + titleLabel.numberOfLines = 0 + titleLabel.adjustsFontForContentSizeCategory = true + titleLabel.font = UIFont.scaledFont(name: "Montserrat-Bold", textSize: 18.0) + titleLabel.isUserInteractionEnabled = true + let gesture = UITapGestureRecognizer(target: self, action: #selector(hideSection(_:))) + gesture.numberOfTapsRequired = 1 + view?.addGestureRecognizer(gesture) return view } @objc - private func hideSection(sender: UIButton) { - let section = sender.tag + private func hideSection(_ sender: UITapGestureRecognizer) { + let section = sender.view?.tag ?? 0 func indexPathsForSection() -> [IndexPath] { var indexPaths = [IndexPath]() @@ -89,65 +121,29 @@ class ProfileActualitesViewController: UIViewController, UITableViewDataSource, return indexPaths } - if !self.sectionsShow.contains(section) { - self.sectionsShow.insert(section) - self.actualitesTableView.insertRows(at: indexPathsForSection(), with: .fade) + if !sectionsShow.contains(section) { + sectionsShow.insert(section) + actualitesTableView.insertRows(at: indexPathsForSection(), with: .fade) + actualitesTableView.reloadData() } else { - self.sectionsShow.remove(section) - self.actualitesTableView.deleteRows(at: indexPathsForSection(), with: .fade) + sectionsShow.remove(section) + actualitesTableView.deleteRows(at: indexPathsForSection(), with: .fade) + actualitesTableView.reloadData() } } } - extension String { var htmlToAttributedString: NSAttributedString? { guard let data = data(using: .utf8) else { return nil } do { - return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil) + return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) } catch { return nil } } + var htmlToString: String { return htmlToAttributedString?.string ?? "" } } - -class SectionCustomHeader: UITableViewHeaderFooterView { - let title = UILabel() - let image = UIImageView() - let sectionTitle = UIButton() - - override init(reuseIdentifier: String?) { - super.init(reuseIdentifier: reuseIdentifier) - configureContents() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configureContents() { - image.translatesAutoresizingMaskIntoConstraints = false - sectionTitle.translatesAutoresizingMaskIntoConstraints = false - - contentView.addSubview(image) - contentView.addSubview(sectionTitle) - contentView.heightAnchor.constraint(equalToConstant: 100) - - NSLayoutConstraint.activate([ - image.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), - image.widthAnchor.constraint(equalToConstant: 100), - image.heightAnchor.constraint(equalToConstant: 100), - image.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - - sectionTitle.heightAnchor.constraint(equalToConstant: 1000), - sectionTitle.leadingAnchor.constraint(equalTo: image.trailingAnchor, - constant: 8), - sectionTitle.trailingAnchor.constraint(equalTo: - contentView.layoutMarginsGuide.trailingAnchor), - sectionTitle.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) - ]) - } -} diff --git a/DansMaRue/Controllers/Profile/ProfileAidesViewController.swift b/DansMaRue/Controllers/Profile/ProfileAidesViewController.swift index 8982872..87ba19b 100644 --- a/DansMaRue/Controllers/Profile/ProfileAidesViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileAidesViewController.swift @@ -6,62 +6,79 @@ // Copyright © 2022 VilleDeParis. All rights reserved. // -import UIKit import SafariServices +import UIKit class ProfileAidesViewController: UIViewController, SFSafariViewControllerDelegate { - - @IBOutlet weak var subTitle: UILabel! - @IBOutlet weak var aidesTableView: UITableView! - + @IBOutlet var subTitle: UILabel! + @IBOutlet var aidesTableView: UITableView! + var aides = [Aide]() - - //MARK: - View lifecycle + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - - self.title = Constants.TabBarTitle.monEspace + aidesTableView.delegate = self + aidesTableView.dataSource = self + aidesTableView.estimatedRowHeight = 116 + aidesTableView.rowHeight = UITableView.automaticDimension + title = Constants.TabBarTitle.monEspace subTitle.text = Constants.LabelMessage.aide - - if let aides = ReferalManager.shared.getAides() { + subTitle.isAccessibilityElement = true + subTitle.textColor = UIColor.greyDmr() + subTitle.accessibilityLabel = Constants.LabelMessage.aide + subTitle.accessibilityTraits = .header + subTitle.adjustsFontForContentSizeCategory = true + subTitle.font = UIFont.preferredFont(forTextStyle: .title3) + if let aides = ReferalManager.shared.getAides() { self.aides.append(contentsOf: aides) } } + + override func viewDidAppear(_ animated: Bool) { + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + aidesTableView.layoutIfNeeded() + aidesTableView.reloadData() + } } extension ProfileAidesViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let aide = aides[indexPath.row] - + let aidesCell = tableView.dequeueReusableCell(withIdentifier: "aides_cell") - + aidesCell!.isAccessibilityElement = true + aidesCell!.accessibilityTraits = .link let imageURL = URL(string: aide.imageUrl) let aideImageUrl = aidesCell?.viewWithTag(101) as! UIImageView + aideImageUrl.isAccessibilityElement = false aideImageUrl.sd_setImage(with: imageURL!, placeholderImage: nil, options: .allowInvalidSSLCertificates) - + let aideLabel = aidesCell?.viewWithTag(102) as! UILabel aideLabel.text = aide.libelle - + aideLabel.accessibilityLabel = aide.libelle + aideLabel.adjustsFontForContentSizeCategory = true + aideLabel.font = UIFont.scaledFont(name: "Montserrat-Bold", textSize: 18.0) + aideLabel.textColor = UIColor(hexString: "#C60") + aidesCell?.setNeedsUpdateConstraints() + aidesCell?.updateConstraintsIfNeeded() return aidesCell! } - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return aides.count } } extension ProfileAidesViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let aide = aides[indexPath.row] if let url = URL(string: aide.hypertexteUrl) { - let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true) + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) vc.delegate = self present(vc, animated: true) } - } - } diff --git a/DansMaRue/Controllers/Profile/ProfileCGUViewController.swift b/DansMaRue/Controllers/Profile/ProfileCGUViewController.swift deleted file mode 100644 index 102f8ce..0000000 --- a/DansMaRue/Controllers/Profile/ProfileCGUViewController.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// ProfileCGUViewController.swift -// DansMaRue -// -// Created by Maxime Bureau on 28/04/2017. -// Copyright © 2017 VilleDeParis. All rights reserved. -// - -import UIKit - -class ProfileCGUViewController: UIViewController { - - //MARK: - IBOutlets - @IBOutlet weak var cguDescriptionTextView: UITextView! - @IBOutlet weak var subTitle: UILabel! - - //MARK: - View lifecycle - override func viewDidLoad() { - super.viewDidLoad() - - self.title = Constants.TabBarTitle.monEspace - subTitle.text = Constants.LabelMessage.cgu - - let cguText = Constants.LabelMessage.cguText1 + "

" + Constants.LabelMessage.cguText2 + "
" + Constants.LabelMessage.cguText3 + "

" + Constants.LabelMessage.cguText4 + "

" + Constants.LabelMessage.cguText5 + "

" + Constants.LabelMessage.cguText6 + "

" + Constants.LabelMessage.cguText7 + "

" + Constants.LabelMessage.cguText8 + "

" + Constants.LabelMessage.cguText9 + "

" + Constants.LabelMessage.cguText10 + "

" + Constants.LabelMessage.cguText11 - - let htmlData = NSString(string: cguText).data(using: String.Encoding.utf8.rawValue) - let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue] as [NSAttributedString.DocumentReadingOptionKey : Any] - let attributedString = try! NSAttributedString(data: htmlData!, - options: options, - documentAttributes: nil) - cguDescriptionTextView.attributedText = attributedString - cguDescriptionTextView.font = UIFont(name: (cguDescriptionTextView.font?.fontName)!, size: 15) - - } - -} diff --git a/DansMaRue/Controllers/Profile/ProfileDetailViewController.swift b/DansMaRue/Controllers/Profile/ProfileDetailViewController.swift index 7d825d6..4cb56d0 100644 --- a/DansMaRue/Controllers/Profile/ProfileDetailViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileDetailViewController.swift @@ -6,50 +6,106 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit import SafariServices +import UIKit class ProfileDetailViewController: UIViewController { - - // Mark: - IBOutlet - @IBOutlet weak var firstnameLabel: UILabel! - @IBOutlet weak var lastnameLabel: UILabel! - @IBOutlet weak var emailLabel: UILabel! - @IBOutlet weak var displayProfil: UIButton! - @IBOutlet weak var deleteAccount: UIButton! - @IBOutlet weak var subTitle: UILabel! - + // MARK: - IBOutlet + + @IBOutlet var firstnameLabel: UILabel! + @IBOutlet var lastnameLabel: UILabel! + @IBOutlet var emailLabel: UILabel! + @IBOutlet var displayProfil: UIButton! + @IBOutlet var deleteAccount: UIButton! + @IBOutlet var subTitle: UILabel! + @IBOutlet var headerLabel: UILabel! + + @IBOutlet var mailTitle: UILabel! + @IBOutlet var firstNameTitle: UILabel! + @IBOutlet var lastNameTitle: UILabel! + + @IBOutlet var scrollView: UIScrollView! + // MARK: - View lifecycle + override func viewDidLoad() { - self.title = Constants.TabBarTitle.monEspace - + title = Constants.TabBarTitle.monEspace + navigationItem.titleView?.isAccessibilityElement = true + setTitleColor() firstnameLabel.text = User.shared.firstName lastnameLabel.text = User.shared.lastName emailLabel.text = User.shared.email subTitle.text = Constants.LabelMessage.monProfil - + + applyDynamicType(label: firstnameLabel, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: firstNameTitle, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: lastnameLabel, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: lastNameTitle, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: emailLabel, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: mailTitle, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicTypeSystemFont(label: displayProfil.titleLabel!, size: 15.0) + applyDynamicTypeSystemFont(label: deleteAccount.titleLabel!, size: 15.0) + applyDynamicTypeSystemFont(label: headerLabel, size: 17.0) displayProfil.setTitle(Constants.LabelMessage.voirProfile, for: .normal) displayProfil.backgroundColor = .clear displayProfil.tintColor = UIColor.pinkDmr() - + deleteAccount.setTitle(Constants.LabelMessage.suppressionCompteMonParis, for: .normal) deleteAccount.backgroundColor = .clear - deleteAccount.tintColor = UIColor.midLightGreyDmr() + deleteAccount.tintColor = UIColor.greyDmr() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + displayProfil.sizeToFit() + deleteAccount.sizeToFit() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 120, right: 0) + } + + override func viewDidAppear(_ animated: Bool) { + User.shared.automaticAuthentification() + if !User.shared.isLogged { + _ = navigationController?.popViewController(animated: true) + } + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } - + // MARK: - IBActions + @IBAction func displayProfil(_ sender: UIButton) { if let url = URL(string: Constants.Services.urlDisplayProfile) { - let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true) + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) present(vc, animated: true) } } - + @IBAction func deleteAccount(_ sender: UIButton) { if let url = URL(string: Constants.Services.urlDeleteAccount) { - let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true) + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) present(vc, animated: true) } + } + + private func setTitleColor() { + firstNameTitle.textColor = UIColor.greyDmr() + lastNameTitle.textColor = UIColor.greyDmr() + mailTitle.textColor = UIColor.greyDmr() + headerLabel.textColor = UIColor.greyDmr() + } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } + private func applyDynamicTypeSystemFont(label: UILabel, size: Float) { + label.numberOfLines = 0 + label.adjustsFontSizeToFitWidth = true + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.preferredFont(forTextStyle: .title3) } } diff --git a/DansMaRue/Controllers/Profile/ProfileSettingsViewController.swift b/DansMaRue/Controllers/Profile/ProfileSettingsViewController.swift index 5495a7c..5d7e60a 100644 --- a/DansMaRue/Controllers/Profile/ProfileSettingsViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileSettingsViewController.swift @@ -6,157 +6,371 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // +import AppAuth +import SafariServices import UIKit -class ProfileSettingsViewController: UIViewController { +class ProfileSettingsViewController: UIViewController, OIDAuthStateChangeDelegate, SFSafariViewControllerDelegate { + func didChange(_ state: OIDAuthState) { + self.setAuthState(state) + } + + // MARK: - Properties + + var settingsArray = ["Mon profil", "Mes anomalies", "Actualités", "Aide et conseils d'utilisation", "Préférences", "Conditions générales d'utilisation", "Politique de confidentialité et de vie privée - Ville de Paris", "À Propos"] + private var authState: OIDAuthState? + typealias PostRegistrationCallback = (_ configuration: OIDServiceConfiguration?, _ registrationResponse: OIDRegistrationResponse?) -> Void + let redirectURI: String = Constants.Authentification.RedirectURI + let authStateKey: String = "authState" - //MARK: - Properties + // MARK: - IBOutlets - var settingsArray = ["Mon profil", "Mes anomalies", "Actualités", "Aide et conseils d'utilisation", "Préférences","Conditions générales d'utilisation", "À Propos"] + @IBOutlet var settingsTableView: UITableView! + @IBOutlet var logoutBtn: UIButton! + + // MARK: - IBActions - - //MARK: - IBOutlets - @IBOutlet weak var settingsTableView: UITableView! - @IBOutlet weak var logoutBtn: UIButton! - - //MARK: - IBActions @IBAction func logoutCompteParisien(_ sender: Any) { if User.shared.isLogged { // Deconnexion de l'utilisateur - User.shared.disconnect() + if let token = self.authState?.lastTokenResponse?.accessToken { + RestApiManager.sharedInstance.logoutMonParis(token: token) { (_: Bool) in + User.shared.disconnect() + self.displayLoginLogoutBtn() + } + } else { + User.shared.disconnect() + self.displayLoginLogoutBtn() + } + self.setAuthState(nil) } - displayLoginLogoutBtn() } - + @IBAction func loginToCompteParisien(_ sender: Any) { - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - compteParisienVC.modalPresentationStyle = .fullScreen - self.navigationController?.present(compteParisienVC, animated: true) - displayLoginLogoutBtn() + self.connectToMonParis() } - - //MARK: - View lifecycle + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - + self.navigationController?.navigationBar.tintColor = UIColor.white self.navigationController?.navigationBar.barTintColor = UIColor.pinkDmr() - self.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white]) - + self.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white]) if #available(iOS 13.0, *) { - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.backgroundColor = UIColor.pinkButtonDmr() - appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white])! - self.navigationController?.navigationBar.standardAppearance = appearance; - self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance - } - - settingsTableView.tableFooterView = UIView() - displayLoginLogoutBtn() - } - + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = UIColor.pinkButtonDmr() + appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white])! + self.navigationController?.navigationBar.standardAppearance = appearance + self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance + } + self.applyDynamicType(label: self.logoutBtn.titleLabel!, fontName: "Montserrat-Regular", size: 16.0) + self.settingsTableView.tableFooterView = UIView() + self.displayLoginLogoutBtn() + } + override func viewDidAppear(_ animated: Bool) { - displayLoginLogoutBtn() + self.displayLoginLogoutBtn() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } - //MARK: - Private function - + // MARK: - Private function + func authorizationSettings() { - let alertController = UIAlertController (title: Constants.AlertBoxTitle.modificationPreferences, message: Constants.AlertBoxMessage.modificationPreferences, preferredStyle: UIAlertController.Style.alert) - - let settingsAction = UIAlertAction(title: Constants.AlertBoxTitle.reglages, style: UIAlertAction.Style.default) { (_) -> Void in + let alertController = UIAlertController(title: Constants.AlertBoxTitle.modificationPreferences, message: Constants.AlertBoxMessage.modificationPreferences, preferredStyle: UIAlertController.Style.alert) + + let settingsAction = UIAlertAction(title: Constants.AlertBoxTitle.reglages, style: UIAlertAction.Style.default) { _ in let settingsUrl = NSURL(string: UIApplication.openSettingsURLString) if let url = settingsUrl { UIApplication.shared.openURL(url as URL) } } - + let cancelAction = UIAlertAction(title: Constants.AlertBoxTitle.annuler, style: UIAlertAction.Style.default, handler: nil) alertController.addAction(settingsAction) alertController.addAction(cancelAction) - - + self.present(alertController, animated: true, completion: nil) - } - + func displayLoginLogoutBtn() { if User.shared.isLogged { - logoutBtn.backgroundColor = UIColor.clear - logoutBtn.setTitleColor(UIColor.pinkDmr(), for: .normal) - logoutBtn.setTitle(Constants.TitleButton.deconnecter.uppercased(), for: .normal) - logoutBtn.isHidden = false - settingsArray[0] = "Mon profil" + self.logoutBtn.backgroundColor = UIColor.clear + self.logoutBtn.setTitleColor(UIColor.pinkDmr(), for: .normal) + self.logoutBtn.setTitle(Constants.TitleButton.deconnecter.uppercased(), for: .normal) + self.logoutBtn.isAccessibilityElement = true + self.logoutBtn.accessibilityTraits = .button + self.logoutBtn.isHidden = false + self.settingsArray[0] = "Mon profil" self.settingsTableView.reloadData() } else { - logoutBtn.isHidden = true - settingsArray[0] = "Se connecter" + self.logoutBtn.isHidden = true + self.settingsArray[0] = "Se connecter" self.settingsTableView.reloadData() } } -} + func connectToMonParis() { + let authorizationEndpoint = Constants.Authentification.authorizationEndpoint + let tokenEndpoint = Constants.Authentification.tokenEndpoint + let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, + tokenEndpoint: tokenEndpoint) + + self.doAuthWithAutoCodeExchange(configuration: configuration, + clientID: Constants.Authentification.clientID, + clientSecret: nil) + } + + func setAuthState(_ authState: OIDAuthState?) { + if self.authState == authState { + return + } + self.authState = authState + self.authState?.stateChangeDelegate = self + var data: Data? = nil + + if let authState = self.authState { + data = NSKeyedArchiver.archivedData(withRootObject: authState) + } + + if let userDefaults = UserDefaults(suiteName: Constants.Authentification.userDefault) { + userDefaults.set(data, forKey: self.authStateKey) + userDefaults.synchronize() + } + } + + func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { + guard let redirectURI = URL(string: redirectURI) else { + print("Error creating URL for : \(redirectURI)") + return + } + + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { + print("Error accessing AppDelegate") + return + } + + // builds authentication request + let request = OIDAuthorizationRequest(configuration: configuration, + clientId: clientID, + clientSecret: clientSecret, + scopes: [OIDScopeOpenID, OIDScopeProfile], + redirectURL: redirectURI, + responseType: OIDResponseTypeCode, + additionalParameters: nil) + + // performs authentication request + print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") + + appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in + + if let authState = authState { + self.setAuthState(authState) + print("Got authorization tokens. Access token: \(authState.lastTokenResponse?.accessToken ?? "DEFAULT_TOKEN")") + self.userInfo() + } else { + print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") + self.setAuthState(nil) + } + } + } + + func userInfo() { + let userInfoEndpoint = Constants.Authentification.userInfoEndpoint + print("Performing userinfo request") + let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken + + self.authState?.performAction { accessToken, _, error in + if error != nil { + print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let accessToken = accessToken else { + print("Error getting accessToken") + return + } + + if currentAccessToken != accessToken { + print("Access token was refreshed automatically (\(currentAccessToken ?? "CURRENT_ACCESS_TOKEN") to \(accessToken))") + } else { + print("Access token was fresh and not updated \(accessToken)") + } + + var urlRequest = URLRequest(url: userInfoEndpoint) + urlRequest.allHTTPHeaderFields = ["Authorization": "Bearer \(accessToken)"] + + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.main.async { + guard error == nil else { + print("HTTP request failed \(error?.localizedDescription ?? "ERROR")") + return + } + + guard let response = response as? HTTPURLResponse else { + print("Non-HTTP response") + return + } + + guard let data = data else { + print("HTTP response data is empty") + return + } + + var json: [AnyHashable: Any]? + + do { + json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } catch { + print("JSON Serialization Error") + } + + if response.statusCode != 200 { + // server replied with an error + let responseText: String? = String(data: data, encoding: String.Encoding.utf8) + + if response.statusCode == 401 { + // "401 Unauthorized" generally indicates there is an issue with the authorization + // grant. Puts OIDAuthState into an error state. + let oauthError = OIDErrorUtilities.resourceServerAuthorizationError(withCode: 0, + errorResponse: json, + underlyingError: error) + self.authState?.update(withAuthorizationError: oauthError) + print("Authorization Error (\(oauthError)). Response: \(responseText ?? "RESPONSE_TEXT")") + } else { + print("HTTP: \(response.statusCode), Response: \(responseText ?? "RESPONSE_TEXT")") + } + return + } + + if let json = json as? [String: Any], let uid = json["uid"] as? String, let validatedAccount = json["validatedAccount"] as? String { + print("Success: \(json)") + + if validatedAccount != "true" { + let alertController = UIAlertController(title: Constants.AlertBoxTitle.erreur, message: Constants.AlertBoxMessage.erreur, preferredStyle: UIAlertController.Style.alert) + + let okAction = UIAlertAction(title: Constants.AlertBoxTitle.ok, style: UIAlertAction.Style.default) { _ in + // nothing + } + + alertController.addAction(okAction) + + self.present(alertController, animated: true, completion: nil) + } else { + User.shared.uid = uid + UserDefaults.standard.set(uid, forKey: Constants.Key.uid) + RestApiManager.sharedInstance.getIdentityStore(guid: User.shared.uid!) { + (_: Bool) in + self.displayLoginLogoutBtn() + } + } + } + } + } + + task.resume() + } + } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } +} extension ProfileSettingsViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let settingCell = tableView.dequeueReusableCell(withIdentifier: "settings_cell") - let settingTitle = settingCell?.viewWithTag(101) as! UILabel - settingTitle.text = settingsArray[indexPath.row] - - let loginBtn = settingCell?.viewWithTag(102) as! UIButton - - if(indexPath.row == 0 && !User.shared.isLogged ) { + var cell: UITableViewCell + if indexPath.row == 0 && !User.shared.isLogged { + cell = tableView.dequeueReusableCell(withIdentifier: "settings_cell")! + let title = cell.viewWithTag(101) as! UILabel + title.text = self.settingsArray[indexPath.row] + self.applyDynamicType(label: title, fontName: "Montserrat-Regular", size: 14.0) + cell.isAccessibilityElement = true + cell.accessibilityTraits = .button + cell.accessibilityLabel = self.settingsArray[indexPath.row] + + let loginBtn = cell.viewWithTag(102) as! UIButton loginBtn.setTitle(Constants.TitleButton.monCompte.uppercased(), for: .normal) loginBtn.isHidden = false loginBtn.backgroundColor = UIColor.pinkButtonDmr() loginBtn.tintColor = UIColor.white loginBtn.layer.cornerRadius = 10 + self.applyDynamicType(label: loginBtn.titleLabel!, fontName: "Montserrat-Regular", size: 14.0) } else { - loginBtn.isHidden = true + cell = tableView.dequeueReusableCell(withIdentifier: "profil_cell")! + let title = cell.viewWithTag(101) as! UILabel + title.text = self.settingsArray[indexPath.row] + title.numberOfLines = 0 + self.applyDynamicType(label: title, fontName: "Montserrat-Regular", size: 14.0) + cell.isAccessibilityElement = true + cell.accessibilityTraits = .button + } + + switch indexPath.row { + case 4: + cell.accessibilityLabel = "Liens vers les préférences" + case 5: + cell.accessibilityLabel = "Liens vers les conditions générales d'utilisation" + case 6: + cell.accessibilityLabel = "Liens vers la politique de confidentialité et de vie privée - Ville de Paris" + default: + cell.accessibilityLabel = self.settingsArray[indexPath.row] } - - return settingCell! + + return cell } - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return settingsArray.count + return self.settingsArray.count + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == 0 { + return UITableView.automaticDimension + } else { + return 40 + } + } + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == 0 { + return UITableView.automaticDimension + } else { + return 40 + } } } extension ProfileSettingsViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let profileStoryboard = UIStoryboard(name: Constants.StoryBoard.profile, bundle: nil) switch indexPath.row { case Constants.ProfilTableView.profil: - if (User.shared.isLogged){ + if User.shared.isLogged { print("redirection vers page de profil") let profileDetailVC = profileStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.profileDetail) as! ProfileDetailViewController - + self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) self.navigationController?.pushViewController(profileDetailVC, animated: true) } else { - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - compteParisienVC.modalPresentationStyle = .fullScreen - self.navigationController?.present(compteParisienVC, animated: true) - displayLoginLogoutBtn() + self.connectToMonParis() } case Constants.ProfilTableView.anomalies: - if (User.shared.isLogged){ + if User.shared.isLogged { print("redirection vers page d'anomalie") let profileVC = profileStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.profile) as! ProfileViewController - + self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) self.navigationController?.pushViewController(profileVC, animated: true) } else { - let compteParisienVC = UIStoryboard(name: Constants.StoryBoard.compteParisien, bundle: nil).instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.compteParisien) - self.navigationController?.present(compteParisienVC, animated: true) + self.connectToMonParis() } - + case Constants.ProfilTableView.actualites: let profileAtualiteVC = profileStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.profileActualites) as! ProfileActualitesViewController self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) @@ -166,11 +380,19 @@ extension ProfileSettingsViewController: UITableViewDelegate { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) self.navigationController?.pushViewController(profileAideVC, animated: true) case Constants.ProfilTableView.preferences: - self.authorizationSettings() + self.authorizationSettings() case Constants.ProfilTableView.cgu: - let profileCGUVC = profileStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.profileCgu) as! ProfileCGUViewController - self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) - self.navigationController?.pushViewController(profileCGUVC, animated: true) + if let url = URL(string: Constants.Services.urlCGU) { + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) + vc.delegate = self + present(vc, animated: true) + } + case Constants.ProfilTableView.confidentialite: + if let url = URL(string: Constants.Services.urlConfidentialité) { + let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false) + vc.delegate = self + present(vc, animated: true) + } case Constants.ProfilTableView.aPropos: let profileAboutVC = profileStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.profileAbout) as! ProfileAboutViewController self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) @@ -179,11 +401,10 @@ extension ProfileSettingsViewController: UITableViewDelegate { print("nothing") } } - } // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { +private func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { guard let input = input else { return nil } - return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)}) + return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value) }) } diff --git a/DansMaRue/Controllers/Profile/ProfileViewController.swift b/DansMaRue/Controllers/Profile/ProfileViewController.swift index 27b71a3..756cd77 100644 --- a/DansMaRue/Controllers/Profile/ProfileViewController.swift +++ b/DansMaRue/Controllers/Profile/ProfileViewController.swift @@ -6,78 +6,96 @@ // Copyright © 2017 VilleDeParis. All rights reserved. // -import UIKit import SafariServices +import UIKit import WebKit class ProfileViewController: UIViewController { + // MARK: - Properties - //MARK: - Properties var currentFilter = "Draft" var malfunctionDraftArray: [Anomalie] = [] var malfunctionSolvedArray: [Anomalie] = [] var malfunctionNotSolvedArray: [Anomalie] = [] // Array [Drafts, Not Solved, Solved] let malfunctionSections = ["Brouillons", "En cours", "Clôturées"] - var isPremierAffichageFormConnexion = true; + var isPremierAffichageFormConnexion = true var anomalieByRow = [Int: Anomalie]() - var vSpinner : UIView? - + var vSpinner: UIView? - //MARK: - IBOutlets - @IBOutlet weak var malfunctionTypeSegmentedControl: UISegmentedControl! - @IBOutlet weak var malfunctionTableView: UITableView! - @IBOutlet weak var subTitle: UILabel! + // MARK: - IBOutlets + + @IBOutlet var titleLabel: UILabel! + @IBOutlet var malfunctionTypeSegmentedControl: UISegmentedControl! + @IBOutlet var malfunctionTableView: UITableView! + @IBOutlet var subTitle: UILabel! - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - self.title = Constants.TabBarTitle.monEspace + malfunctionTableView.delegate = self + malfunctionTableView.dataSource = self + malfunctionTableView.estimatedRowHeight = 126 + malfunctionTableView.rowHeight = UITableView.automaticDimension + title = Constants.TabBarTitle.monEspace subTitle.text = Constants.LabelMessage.mesAnomalies - + subTitle.isAccessibilityElement = true + subTitle.accessibilityLabel = Constants.LabelMessage.mesAnomalies + subTitle.accessibilityTraits = .header + subTitle.textColor = UIColor.greyDmr() + UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.pinkDmr()], for: .selected) + updateTitleLabel(index: 0) + malfunctionTableView.tableFooterView = UIView() - self.navigationController?.navigationBar.tintColor = UIColor.white - self.navigationController?.navigationBar.barTintColor = UIColor.pinkDmr() - self.navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white]) + navigationController?.navigationBar.tintColor = UIColor.white + navigationController?.navigationBar.barTintColor = UIColor.pinkDmr() + navigationController?.navigationBar.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white]) + titleLabel.adjustsFontForContentSizeCategory = true + titleLabel.font = UIFont.preferredFont(forTextStyle: .title2) + subTitle.adjustsFontForContentSizeCategory = true + subTitle.font = UIFont.preferredFont(forTextStyle: .title2) if #available(iOS 13.0, *) { - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.backgroundColor = UIColor.pinkButtonDmr() - appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue:UIColor.white])! - self.navigationController?.navigationBar.standardAppearance = appearance; - self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance - } + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = UIColor.pinkButtonDmr() + appearance.titleTextAttributes = convertToOptionalNSAttributedStringKeyDictionary([NSAttributedString.Key.foregroundColor.rawValue: UIColor.white])! + self.navigationController?.navigationBar.standardAppearance = appearance + self.navigationController?.navigationBar.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance + } } override func viewDidAppear(_ animated: Bool) { - self.malfunctionDraftArray = [Anomalie] (AnomalieBrouillon.shared.anomalies.values) - self.malfunctionDraftArray.sort(by: { $0.dateHour.compare($1.dateHour) == .orderedDescending }) + malfunctionTableView.layoutIfNeeded() + malfunctionTableView.reloadData() + malfunctionDraftArray = [Anomalie](AnomalieBrouillon.shared.anomalies.values) + malfunctionDraftArray.sort(by: { $0.dateHour.compare($1.dateHour) == .orderedDescending }) - // Chargement de la liste des anomalies - self.malfunctionSolvedArray.removeAll() - self.malfunctionNotSolvedArray.removeAll() + // Chargement de la liste des anomalies + malfunctionSolvedArray.removeAll() + malfunctionNotSolvedArray.removeAll() - switch currentFilter - { + switch currentFilter { case "Not Solved": fillMalfunctionNotSolvedArray() case "Solved": - fillMalfunctionSolvedArray() + fillMalfunctionSolvedArray() default: break } - self.malfunctionTableView.reloadData() + malfunctionTableView.reloadData() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) } - //MARK: - IBActions - @IBAction func changeMalfunctionFilter(_ sender: Any) { + // MARK: - IBActions - switch malfunctionTypeSegmentedControl.selectedSegmentIndex - { + @IBAction func changeMalfunctionFilter(_ sender: Any) { + updateTitleLabel(index: malfunctionTypeSegmentedControl.selectedSegmentIndex) + switch malfunctionTypeSegmentedControl.selectedSegmentIndex { case 0: currentFilter = "Draft" case 1: @@ -94,11 +112,14 @@ class ProfileViewController: UIViewController { } func fillMalfunctionSolvedArray() { - self.malfunctionSolvedArray.removeAll() - if User.shared.isLogged, let uid = User.shared.uid { - DispatchQueue.global().async { - //Affichage du spinner de chargement - self.showSpinner(onView: self.view) + malfunctionSolvedArray.removeAll() + if User.shared.isLogged, let uid = User.shared.uid { + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + // Affichage du spinner de chargement + DispatchQueue.main.async { + self.showSpinner(onView: self.view) + } // Récupération des anomalies outdoor RestApiManager.sharedInstance.getIncidentsByUser(guid: uid, isIncidentSolved: true) { (anomalies: [Anomalie]) in @@ -122,7 +143,7 @@ class ProfileViewController: UIViewController { self.malfunctionSolvedArray.sort(by: { $0.dateHour.compare($1.dateHour) == .orderedDescending }) DispatchQueue.main.async { - //Fin du chargement + // Fin du chargement self.removeSpinner() self.malfunctionTableView.reloadData() @@ -137,13 +158,16 @@ class ProfileViewController: UIViewController { } func fillMalfunctionNotSolvedArray() { - - self.malfunctionNotSolvedArray.removeAll() + malfunctionNotSolvedArray.removeAll() if User.shared.isLogged, let uid = User.shared.uid { - DispatchQueue.global().async { - //Affichage du spinner de chargement - self.showSpinner(onView: self.view) - + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + + // Affichage du spinner de chargement + DispatchQueue.main.async { + self.showSpinner(onView: self.view) + } + // Récupération des anomalies outdoor RestApiManager.sharedInstance.getIncidentsByUser(guid: uid, isIncidentSolved: false) { (anomalies: [Anomalie]) in @@ -166,7 +190,7 @@ class ProfileViewController: UIViewController { self.malfunctionSolvedArray.sort(by: { $0.dateHour.compare($1.dateHour) == .orderedDescending }) DispatchQueue.main.async { - //Fin du chargement + // Fin du chargement self.removeSpinner() self.malfunctionTableView.reloadData() @@ -180,10 +204,10 @@ class ProfileViewController: UIViewController { } } - func showSpinner(onView : UIView) { - let spinnerView = UIView.init(frame: onView.bounds) - spinnerView.backgroundColor = UIColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5) - let ai = UIActivityIndicatorView.init(style: .whiteLarge) + func showSpinner(onView: UIView) { + let spinnerView = UIView(frame: onView.bounds) + spinnerView.backgroundColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5) + let ai = UIActivityIndicatorView(style: .whiteLarge) ai.startAnimating() ai.center = spinnerView.center @@ -202,17 +226,32 @@ class ProfileViewController: UIViewController { } } + private func updateTitleLabel(index: Int) { + titleLabel.text = malfunctionSections[index] + titleLabel.isAccessibilityElement = true + titleLabel.accessibilityLabel = malfunctionSections[index] + titleLabel.textColor = UIColor.greyDmr() + titleLabel.accessibilityTraits = .header + } + + private func update() { + if #available(iOS 13.0, *) { + malfunctionTypeSegmentedControl.selectedSegmentTintColor = UIColor.pinkDmr() + } else { + // Fallback on earlier versions + } + } + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } } extension ProfileViewController: UITableViewDelegate { - - func getDetailsAnomalies(anomalie: Anomalie, source: AnomalieSource) { - if let anoEquipement = anomalie as? AnomalieEquipement { - DispatchQueue.global().async { - RestApiManager.sharedInstance.getIncidentEquipementById(idSignalement: anoEquipement.id) { (anomalie: AnomalieEquipement) in DispatchQueue.main.async { let anomalyDetailStoryboard = UIStoryboard(name: Constants.StoryBoard.detailAnomaly, bundle: nil) @@ -222,16 +261,14 @@ extension ProfileViewController: UITableViewDelegate { anomalyDetailViewController.currentAnomalyState = (anomalie.anomalieStatus == .Resolu ? .solved : .notsolved) self.present(anomalyDetailViewController, animated: true, completion: nil) anomalyDetailViewController.customNavigationDelegate = self - } } } } else { - //Si ano DMR, on affiche le détail + // Si ano DMR, on affiche le détail if source == AnomalieSource.dmr { DispatchQueue.global().async { - - RestApiManager.sharedInstance.getIncidentById(idSignalement: anomalie.id, source: source){ (anomalie: Anomalie) in + RestApiManager.sharedInstance.getIncidentById(idSignalement: anomalie.id, source: source) { (anomalie: Anomalie) in DispatchQueue.main.async { let anomalyDetailStoryboard = UIStoryboard(name: Constants.StoryBoard.detailAnomaly, bundle: nil) let anomalyDetailViewController = anomalyDetailStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.detailAnomaly) as! AnomalyDetailViewController @@ -240,21 +277,17 @@ extension ProfileViewController: UITableViewDelegate { anomalyDetailViewController.currentAnomalyState = (anomalie.anomalieStatus == .Resolu ? .solved : .notsolved) self.present(anomalyDetailViewController, animated: true, completion: nil) anomalyDetailViewController.customNavigationDelegate = self - } } - } + } } } - } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - var filterSelected: AnomalieStatus - switch malfunctionTypeSegmentedControl.selectedSegmentIndex - { + switch malfunctionTypeSegmentedControl.selectedSegmentIndex { case 0: filterSelected = malfunctionDraftArray[indexPath.row].anomalieStatus case 1: @@ -263,11 +296,9 @@ extension ProfileViewController: UITableViewDelegate { filterSelected = malfunctionSolvedArray[indexPath.row].anomalieStatus default: filterSelected = .Brouillon - break } - switch filterSelected - { + switch filterSelected { case .Brouillon, .APublier: let addAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.addAnomaly, bundle: nil) let addAnomalyViewController = addAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.addAnomaly) as! AddAnomalyViewController @@ -282,7 +313,7 @@ extension ProfileViewController: UITableViewDelegate { addAnomalyViewController.typeContribution = .outdoor addAnomalyViewController.currentAnomalie = malfunctionDraftArray[indexPath.row] } - self.navigationController?.pushViewController(addAnomalyViewController, animated: true) + navigationController?.pushViewController(addAnomalyViewController, animated: true) case .Ouvert, .ATraiter: getDetailsAnomalies(anomalie: malfunctionNotSolvedArray[indexPath.row], source: malfunctionNotSolvedArray[indexPath.row].source) case .Resolu: @@ -293,34 +324,42 @@ extension ProfileViewController: UITableViewDelegate { } } + func showPopupMaintenance() { + let alert = UIAlertController(title: Constants.AlertBoxTitle.information, message: Constants.AlertBoxMessage.maintenance, preferredStyle: UIAlertController.Style.alert) + let okBtn = UIAlertAction(title: "Ok", style: .default, handler: { (_: UIAlertAction) in + }) + alert.addAction(okBtn) + self.present(alert, animated: true, completion: nil) + } + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { - switch currentFilter - { + switch currentFilter { case "Draft": AnomalieBrouillon.shared.remove(anomalie: malfunctionDraftArray[indexPath.row]) - malfunctionDraftArray.remove(at: indexPath.row) + malfunctionDraftArray.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) case "Not Solved": - RestApiManager.sharedInstance.unfollow(anomalie: malfunctionNotSolvedArray[indexPath.row] ,onCompletion: { (result: Bool) in + RestApiManager.sharedInstance.unfollow(anomalie: malfunctionNotSolvedArray[indexPath.row], onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { self.malfunctionNotSolvedArray.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) - } } + else { + self.showPopupMaintenance() + } }) case "Solved": - RestApiManager.sharedInstance.unfollow(anomalie: malfunctionSolvedArray[indexPath.row] ,onCompletion: { (result: Bool) in + RestApiManager.sharedInstance.unfollow(anomalie: malfunctionSolvedArray[indexPath.row], onCompletion: { (result: Bool) in if result { - //Mise à jour de l'UI + // Mise à jour de l'UI DispatchQueue.main.async { self.malfunctionSolvedArray.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) - } } }) @@ -329,14 +368,11 @@ extension ProfileViewController: UITableViewDelegate { } } } - } extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDelegate { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch currentFilter - { + switch currentFilter { case "Draft": return malfunctionDraftArray.count case "Not Solved": @@ -351,8 +387,7 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe func numberOfSections(in tableView: UITableView) -> Int { var numberOfSections = 1 - switch currentFilter - { + switch currentFilter { case "Draft": if malfunctionDraftArray.count > 0 { numberOfSections = 1 @@ -360,14 +395,16 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe tableView.backgroundView = nil } else { numberOfSections = 1 - let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) - noDataLabel.text = Constants.LabelMessage.noDraft - noDataLabel.textColor = UIColor.greyDmr() + let noDataLabel: UILabel = .init(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) + noDataLabel.text = Constants.LabelMessage.noDraft + noDataLabel.textColor = UIColor.greyDmr() noDataLabel.textAlignment = .center noDataLabel.lineBreakMode = .byWordWrapping noDataLabel.numberOfLines = 2 - tableView.backgroundView = noDataLabel - tableView.separatorStyle = .none + tableView.backgroundView = noDataLabel + tableView.separatorStyle = .none + noDataLabel.adjustsFontForContentSizeCategory = true + noDataLabel.font = UIFont.preferredFont(forTextStyle: .caption1) } case "Not Solved": if malfunctionNotSolvedArray.count > 0 { @@ -376,14 +413,16 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe tableView.backgroundView = nil } else { numberOfSections = 1 - let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) - noDataLabel.text = Constants.LabelMessage.noNotSolved - noDataLabel.textColor = UIColor.greyDmr() + let noDataLabel: UILabel = .init(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) + noDataLabel.text = Constants.LabelMessage.noNotSolved + noDataLabel.textColor = UIColor.greyDmr() noDataLabel.textAlignment = .center noDataLabel.lineBreakMode = .byWordWrapping noDataLabel.numberOfLines = 2 - tableView.backgroundView = noDataLabel - tableView.separatorStyle = .none + tableView.backgroundView = noDataLabel + tableView.separatorStyle = .none + noDataLabel.adjustsFontForContentSizeCategory = true + noDataLabel.font = UIFont.preferredFont(forTextStyle: .caption1) } case "Solved": if malfunctionSolvedArray.count > 0 { @@ -392,14 +431,16 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe tableView.backgroundView = nil } else { numberOfSections = 1 - let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) - noDataLabel.text = Constants.LabelMessage.noSolved - noDataLabel.textColor = UIColor.greyDmr() + let noDataLabel: UILabel = .init(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height)) + noDataLabel.text = Constants.LabelMessage.noSolved + noDataLabel.textColor = UIColor.greyDmr() noDataLabel.textAlignment = .center noDataLabel.lineBreakMode = .byWordWrapping noDataLabel.numberOfLines = 2 - tableView.backgroundView = noDataLabel - tableView.separatorStyle = .none + noDataLabel.adjustsFontForContentSizeCategory = true + noDataLabel.font = UIFont.preferredFont(forTextStyle: .caption1) + tableView.backgroundView = noDataLabel + tableView.separatorStyle = .none } default: numberOfSections = malfunctionSections.count @@ -409,20 +450,19 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - switch currentFilter - { + switch currentFilter { case "All": return malfunctionSections[section] default: return nil } } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let malfunctionCell = tableView.dequeueReusableCell(withIdentifier: "malfunction_cell") - - switch currentFilter - { + malfunctionCell?.isAccessibilityElement = true + malfunctionCell?.accessibilityTraits = .button + switch currentFilter { case "Draft": displayAnomalie(atSection: 0, andRow: indexPath.row, malfunctionCell!) case "Not Solved": @@ -431,27 +471,30 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe displayAnomalie(atSection: 2, andRow: indexPath.row, malfunctionCell!) default: displayAnomalie(atSection: indexPath.section, andRow: indexPath.row, malfunctionCell!) - break } return malfunctionCell! - } - //MARK: - Other functions - func displayAnomalie(atSection section: Int, andRow row: Int, _ malfunctionCell: UITableViewCell) { + // MARK: - Other functions + + func displayAnomalie(atSection section: Int, andRow row: Int, _ malfunctionCell: UITableViewCell) { let malfunctionImage = malfunctionCell.viewWithTag(102) as! UIImageView let malfunctionMainTitle = malfunctionCell.viewWithTag(103) as! UILabel let malfunctionAddress = malfunctionCell.viewWithTag(104) as! UILabel let malfunctionSecondTitle = malfunctionCell.viewWithTag(105) as! UILabel let malfunctionTypeTitle = malfunctionCell.viewWithTag(106) as! UILabel let responsableQuartier = malfunctionCell.viewWithTag(107) as! AnomalieUIButton + + applyDynamicType(label: malfunctionTypeTitle, fontName: "Montserrat-Bold", size: 15.0) + applyDynamicType(label: malfunctionMainTitle, fontName: "Montserrat-Regular", size: 14.0) + applyDynamicType(label: malfunctionAddress, fontName: "Montserrat-Light", size: 12.0) + applyDynamicType(label: malfunctionSecondTitle, fontName: "Montserrat-Light", size: 12.0) var anomalie: Anomalie responsableQuartier.isHidden = true - responsableQuartier.addTarget(self,action: #selector(self.redirectToSolen(sender:)),for: .touchUpInside) + responsableQuartier.addTarget(self, action: #selector(redirectToSolen(sender:)), for: .touchUpInside) responsableQuartier.setTitle("", for: .normal) - if section == 0 { anomalie = malfunctionDraftArray[row] if let photo = anomalie.photo1 { @@ -474,7 +517,6 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe anomalieByRow[row] = anomalie } - malfunctionMainTitle.text = anomalie.alias malfunctionAddress.text = anomalie.address malfunctionSecondTitle.text = DateUtils.formatDateByLocal(dateString: anomalie.date) + " " + anomalie.hour + " " + anomalie.number @@ -487,8 +529,8 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe } else { malfunctionTypeTitle.text = Constants.LabelMessage.defaultTypeContributionLabel } - - malfunctionTypeTitle.textColor = UIColor.orangeDmr() + malfunctionTypeTitle.textColor = UIColor(hexString: "#C60") + malfunctionSecondTitle.textColor = UIColor.greyDmr() } @objc @@ -498,7 +540,7 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe let longitude = anomalie.longitude let contentController = WKUserContentController() - let scriptSource = "javascript:document.getElementById('username').value='" + User.shared.email! + "';document.getElementById('password').value='" + User.shared.password! + "';document.getElementsByName('Submit')[0].click()"; + let scriptSource = "" let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true) contentController.addUserScript(script) @@ -528,25 +570,19 @@ extension ProfileViewController: UITableViewDataSource, SFSafariViewControllerDe webView.load(URLRequest(url: url)) } } - } - - extension ProfileViewController: CustomNavigationDelegate { - func displayAddAnomaly(anomalySelected: Anomalie) { let addAnomalyStoryboard = UIStoryboard(name: Constants.StoryBoard.addAnomaly, bundle: nil) let addAnomalyViewController = addAnomalyStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.addAnomaly) as! AddAnomalyViewController addAnomalyViewController.currentAnomalie = anomalySelected - self.navigationController?.pushViewController(addAnomalyViewController, animated: true) + navigationController?.pushViewController(addAnomalyViewController, animated: true) } - } - // Helper function inserted by Swift 4.2 migrator. -fileprivate func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { - guard let input = input else { return nil } - return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value)}) +private func convertToOptionalNSAttributedStringKeyDictionary(_ input: [String: Any]?) -> [NSAttributedString.Key: Any]? { + guard let input = input else { return nil } + return Dictionary(uniqueKeysWithValues: input.map { key, value in (NSAttributedString.Key(rawValue: key), value) }) } diff --git a/DansMaRue/Controllers/Welcome/WelcomePageContentViewController.swift b/DansMaRue/Controllers/Welcome/WelcomePageContentViewController.swift index 6e9a04a..14d0a21 100644 --- a/DansMaRue/Controllers/Welcome/WelcomePageContentViewController.swift +++ b/DansMaRue/Controllers/Welcome/WelcomePageContentViewController.swift @@ -9,18 +9,20 @@ import UIKit class WelcomePageContentViewController: UIPageViewController { + // MARK: - Properties - //MARK: - Properties var pageIndex = 0 - var arrayPageTitle: NSArray = NSArray() - var arrayPagePhoto: NSArray = NSArray() - var arrayPageText: NSArray = NSArray() - - //MARK: - View lifecycle + var arrayPageTitle: NSArray = .init() + var arrayPagePhoto: NSArray = .init() + var arrayPageText: NSArray = .init() + let proxy = UIPageControl.appearance() + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - self.dataSource = self + dataSource = self arrayPageTitle = [Constants.LabelMessage.bienvenue, Constants.LabelMessage.envoyerInfo, @@ -33,46 +35,62 @@ class WelcomePageContentViewController: UIPageViewController { arrayPageText = [Constants.LabelMessage.textSlide1, Constants.LabelMessage.textSlide2, Constants.LabelMessage.textSlide3] + + proxy.pageIndicatorTintColor = .white + proxy.currentPageIndicatorTintColor = UIColor(hexString: "#B1002D") + proxy.currentPage = pageIndex - self.setViewControllers([getViewControllerAtIndex(index:0)] as [UIViewController], direction: UIPageViewController.NavigationDirection.forward, animated: false, completion: nil) - + setViewControllers([getViewControllerAtIndex(index: 0)] as [UIViewController], direction: UIPageViewController.NavigationDirection.forward, animated: false, completion: nil) + } + + override func viewDidLayoutSubviews() { + if #available(iOS 14.0, *) { + self.pageControl?.currentPageIndicatorTintColor = .white + self.pageControl?.setIndicatorImage(UIImage(named: "dot_white_small"), forPage: 0) + self.pageControl?.setIndicatorImage(UIImage(named: "dot_white_small"), forPage: 1) + self.pageControl?.setIndicatorImage(UIImage(named: "dot_white_small"), forPage: 2) + } + if #available(iOS 16.0, *) { + self.pageControl?.currentPageIndicatorTintColor = .white + self.pageControl?.setCurrentPageIndicatorImage(UIImage(named: "dot_white_large"), forPage: 0) + self.pageControl?.setCurrentPageIndicatorImage(UIImage(named: "dot_white_large"), forPage: 1) + self.pageControl?.setCurrentPageIndicatorImage(UIImage(named: "dot_white_large"), forPage: 2) + } } - } // MARK: UIPageViewControllerDataSource + extension WelcomePageContentViewController: UIPageViewControllerDataSource { - - func pageViewController (_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { let pageContent: WelcomeSliderViewController = viewController as! WelcomeSliderViewController var index = pageContent.welcomePageIndex - if ((index == 0) || (index == NSNotFound)) { + if (index == 0) || (index == NSNotFound) { return nil } - index -= 1; + index -= 1 return getViewControllerAtIndex(index: index) } - func pageViewController (_ pageViewController: UIPageViewController,viewControllerAfter viewController: UIViewController) -> UIViewController? { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { let pageContent: WelcomeSliderViewController = viewController as! WelcomeSliderViewController var index = pageContent.welcomePageIndex - if (index == NSNotFound) { - return nil; + if index == NSNotFound { + return nil } - index += 1; - if (index == arrayPageTitle.count) { - return nil; + index += 1 + if index == arrayPageTitle.count { + return nil } return getViewControllerAtIndex(index: index) } func getViewControllerAtIndex(index: NSInteger) -> WelcomeSliderViewController { - pageIndex = index let welcomeStoryboard = UIStoryboard(name: Constants.StoryBoard.welcome, bundle: nil) @@ -82,8 +100,7 @@ extension WelcomePageContentViewController: UIPageViewControllerDataSource { welcomeSliderViewController.welcomeImage = "\(arrayPagePhoto[index])" welcomeSliderViewController.welcomePageIndex = index welcomeSliderViewController.welcomeSubtitleText = "\(arrayPageText[index])" - - + return welcomeSliderViewController } @@ -96,3 +113,13 @@ extension WelcomePageContentViewController: UIPageViewControllerDataSource { } } +extension UIPageViewController { + var pageControl: UIPageControl? { + for view in view.subviews { + if view is UIPageControl { + return view as? UIPageControl + } + } + return nil + } +} diff --git a/DansMaRue/Controllers/Welcome/WelcomeSliderViewController.swift b/DansMaRue/Controllers/Welcome/WelcomeSliderViewController.swift index b036c10..5f8850a 100644 --- a/DansMaRue/Controllers/Welcome/WelcomeSliderViewController.swift +++ b/DansMaRue/Controllers/Welcome/WelcomeSliderViewController.swift @@ -9,7 +9,6 @@ import UIKit class WelcomeSliderViewController: UIViewController { - var welcomePageIndex = 0 var welcomeImage = "" var welcomeTitleText = "" @@ -20,42 +19,42 @@ class WelcomeSliderViewController: UIViewController { @IBOutlet var welcomeSubtitle: UILabel! @IBOutlet var welcomeMainImageBackground: UIView! - - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - self.welcomeMainImage.image = UIImage(named: welcomeImage) - self.welcomeTitle.text = welcomeTitleText - self.welcomeSubtitle.text = welcomeSubtitleText + welcomeMainImage.image = UIImage(named: welcomeImage) + welcomeTitle.text = welcomeTitleText + welcomeSubtitle.text = welcomeSubtitleText - self.welcomeMainImage.layer.cornerRadius = welcomeMainImage.frame.height/2 - self.welcomeMainImageBackground.layer.cornerRadius = welcomeMainImageBackground.frame.height/2 - self.welcomeMainImageBackground.layer.cornerRadius = welcomeMainImageBackground.frame.height/2 + welcomeMainImage.layer.cornerRadius = welcomeMainImage.frame.height/2 + welcomeMainImageBackground.layer.cornerRadius = welcomeMainImageBackground.frame.height/2 + welcomeMainImageBackground.layer.cornerRadius = welcomeMainImageBackground.frame.height/2 - //Ajout du l'espacement entre les lignes + // Ajout du l'espacement entre les lignes let attributedString = NSMutableAttributedString(string: welcomeSubtitleText) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 3 paragraphStyle.alignment = NSTextAlignment.center - attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length)) - - welcomeSubtitle.attributedText = attributedString; - - let modelResolution = UIDevice.current.deviceResolution - if modelResolution[0] == "640" { - welcomeTitle.font = UIFont(name: welcomeSubtitle.font.fontName, size: 17) - welcomeSubtitle.font = UIFont(name: welcomeSubtitle.font.fontName, size: 13) - } - if modelResolution[0] == "1242" { - welcomeTitle.font = UIFont(name: welcomeSubtitle.font.fontName, size: 22) - welcomeSubtitle.font = UIFont(name: welcomeSubtitle.font.fontName, size: 18) - } + attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length)) - + welcomeSubtitle.attributedText = attributedString + configureAccessibility() + } + + func configureAccessibility() { + welcomeTitle.adjustsFontForContentSizeCategory = true + welcomeTitle.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 20) + welcomeSubtitle.adjustsFontForContentSizeCategory = true + welcomeSubtitle.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 16) + welcomeTitle.isAccessibilityElement = true + welcomeTitle.accessibilityTraits = .header + welcomeTitle.accessibilityLabel = welcomeTitleText + welcomeSubtitle.isAccessibilityElement = true + welcomeSubtitle.accessibilityLabel = welcomeSubtitleText + welcomeSubtitle.accessibilityTraits = .staticText } - - } diff --git a/DansMaRue/Controllers/Welcome/WelcomeViewController.swift b/DansMaRue/Controllers/Welcome/WelcomeViewController.swift index 2cd37b8..93f718b 100644 --- a/DansMaRue/Controllers/Welcome/WelcomeViewController.swift +++ b/DansMaRue/Controllers/Welcome/WelcomeViewController.swift @@ -9,28 +9,36 @@ import UIKit class WelcomeViewController: UIViewController { - - //MARK: - IBOutlets + // MARK: - IBOutlets + @IBOutlet var sliderView: UIView! @IBOutlet var startButton: UIButton! - - //MARK: - View lifecycle + + @IBOutlet var backgroundView: UIView! + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - + let welcomePageContentViewController = WelcomePageContentViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) let sliderContainerView = welcomePageContentViewController.view sliderContainerView?.frame = CGRect(x: 0, y: 0, width: self.sliderView.frame.size.width, height: self.sliderView.frame.size.height) self.sliderView.addSubview(sliderContainerView!) self.addChild(welcomePageContentViewController) - startButton.layer.cornerRadius = 8 - + self.startButton.layer.cornerRadius = 8 + self.startButton.isAccessibilityElement = true + self.startButton.accessibilityLabel = "Commencer" + self.startButton.accessibilityTraits = .button + self.startButton.titleLabel?.adjustsFontForContentSizeCategory = true + self.startButton.titleLabel?.font = UIFont.scaledFont(name: "Montserrat-Regular", textSize: 16.0) + self.backgroundView.backgroundColor = UIColor(hexString: "#224659") } - - //MARK: - IBActions + + // MARK: - IBActions + @IBAction func beginApp(_ sender: UIButton) { UserDefaults.standard.set(true, forKey: Constants.Key.hasAlreadyBeenConnected) self.dismiss(animated: true, completion: nil) } - } diff --git a/DansMaRue/Extensions/UIButton+AnomalieDMR.swift b/DansMaRue/Extensions/UIButton+AnomalieDMR.swift index f309899..315ae97 100644 --- a/DansMaRue/Extensions/UIButton+AnomalieDMR.swift +++ b/DansMaRue/Extensions/UIButton+AnomalieDMR.swift @@ -12,12 +12,12 @@ class AnomalieUIButton: UIButton { var anomalie: Anomalie override init(frame: CGRect) { - self.anomalie = Anomalie(id: "", address: "", latitude: 0, longitude: 0, categorieId: "", descriptive: "", priorityId: "", anomalieStatus: .Brouillon, photoCloseUrl: nil, photoFarUrl: nil, photoDoneUrl: nil, number: "") + self.anomalie = Anomalie(id: "", address: "", latitude: 0, longitude: 0, categorieId: "", descriptive: "", priorityId: "", anomalieStatus: .Brouillon, photoCloseUrl: nil, photoFarUrl: nil, photoDoneUrl: nil, number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { - self.anomalie = Anomalie(id: "", address: "", latitude: 0, longitude: 0, categorieId: "", descriptive: "", priorityId: "", anomalieStatus: .Brouillon, photoCloseUrl: nil, photoFarUrl: nil, photoDoneUrl: nil, number: "") + self.anomalie = Anomalie(id: "", address: "", latitude: 0, longitude: 0, categorieId: "", descriptive: "", priorityId: "", anomalieStatus: .Brouillon, photoCloseUrl: nil, photoFarUrl: nil, photoDoneUrl: nil, number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) super.init(coder: aDecoder) } } diff --git a/DansMaRue/Extensions/UIColor+DMR.swift b/DansMaRue/Extensions/UIColor+DMR.swift index b793f2e..01d9c39 100644 --- a/DansMaRue/Extensions/UIColor+DMR.swift +++ b/DansMaRue/Extensions/UIColor+DMR.swift @@ -10,37 +10,53 @@ import Foundation import UIKit extension UIColor { + convenience init(hexString: String) { + let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int = UInt64() + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (255, 0, 0, 0) + } + self.init(red: CGFloat(r)/255, green: CGFloat(g)/255, blue: CGFloat(b)/255, alpha: CGFloat(a)/255) + } class func pinkButtonDmr() -> UIColor { - return UIColor(red: 223.0/255.0, green: 34.0/255.0, blue: 90.0/255.0, alpha: 1.0) + return UIColor(hexString: "#B1002D") } - + class func pinkDmr() -> UIColor { - return UIColor(red: 223.0/255.0, green: 34.0/255.0, blue: 90.0/255.0, alpha: 1.0) + return UIColor(hexString: "#B1002D") } - + class func lightGreyDmr() -> UIColor { return UIColor(red: 242.0/255.0, green: 242.0/255.0, blue: 242.0/255.0, alpha: 1.0) } - + class func midLightGreyDmr() -> UIColor { return UIColor(red: 200.0/255.0, green: 200.0/255.0, blue: 200.0/255.0, alpha: 1.0) } - + class func greyDmr() -> UIColor { - return UIColor(red: 125.0/255.0, green: 125.0/255.0, blue: 125.0/255.0, alpha: 1.0) + return UIColor(hexString: "#6D6F72") } - + class func blueDmr() -> UIColor { return UIColor(red: 12/255.0, green: 81/255.0, blue: 138/255.0, alpha: 0.5) } - + class func greenDmr() -> UIColor { return UIColor(red: 67/255.0, green: 181/255.0, blue: 126/255.0, alpha: 1) } - + class func orangeDmr() -> UIColor { return UIColor(red: 247/255.0, green: 127/255.0, blue: 104/255.0, alpha: 1) } - } diff --git a/DansMaRue/ManageAddress.storyboard b/DansMaRue/ManageAddress.storyboard index 4f169b3..a6229fa 100644 --- a/DansMaRue/ManageAddress.storyboard +++ b/DansMaRue/ManageAddress.storyboard @@ -1,9 +1,9 @@ - + - + @@ -17,10 +17,10 @@ - + - - - + + - + - + - + @@ -62,6 +63,7 @@ + @@ -69,7 +71,6 @@ - @@ -79,6 +80,7 @@ + diff --git a/DansMaRue/ManageAddressViewController.swift b/DansMaRue/ManageAddressViewController.swift index b8242bd..2f433da 100644 --- a/DansMaRue/ManageAddressViewController.swift +++ b/DansMaRue/ManageAddressViewController.swift @@ -6,91 +6,109 @@ // Copyright © 2019 VilleDeParis. All rights reserved. // -import UIKit import CoreLocation +import UIKit class ManageAddressViewController: UIViewController { weak var delegate: MapViewController! var isModification = false var items: [String] = [] - //MARK: IBOutlet - @IBOutlet weak var tableView: UITableView! + // MARK: IBOutlet + + @IBOutlet var tableView: UITableView! - //MARK: - View lifecycle + @IBOutlet var deleteButton: UIButton! + + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.title = Constants.LabelMessage.adressesFavorites + if #available(iOS 13.0, *) { + let appearance = UINavigationBarAppearance() + appearance.titleTextAttributes = [.foregroundColor: UIColor.white] + appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white] + appearance.backgroundColor = UIColor.pinkDmr() + + navigationItem.standardAppearance = appearance + navigationItem.scrollEdgeAppearance = appearance + } + navigationItem.title = Constants.LabelMessage.adressesFavorites + navigationItem.titleView?.tintColor = .white items = getFavoritesAddress() tableView.dataSource = self - self.tableView.isEditing = false + tableView.isEditing = false + tableView.estimatedRowHeight = 91 + tableView.rowHeight = UITableView.automaticDimension delegate.searchController?.isActive = false // Put the search bar in the navigation bar. if let searchBar = delegate.searchController?.searchBar { - searchBar.placeholder = Constants.PlaceHolder.saisirAdresse searchBar.tintColor = UIColor.white searchBar.isTranslucent = false if #available(iOS 13.0, *) { - searchBar.searchTextField.backgroundColor=UIColor.white - searchBar.searchTextField.tintColor=UIColor.black + searchBar.searchTextField.backgroundColor = UIColor.white + searchBar.searchTextField.tintColor = UIColor.black } - searchBar.layer.cornerRadius = 10; - delegate.setNavigationTitleView(withSearchBar: searchBar) - + searchBar.layer.cornerRadius = 10 + delegate.setNavigationTitleView(withSearchBarController: delegate.searchController!) } + applyDynamicType(label: deleteButton.titleLabel!, fontName: Constants.fontDmr, size: 16) } @IBAction func modifier(_ sender: Any) { isModification = !isModification - self.tableView.isEditing = isModification - let deleteAllButton = self.tableView.viewWithTag(101) as! UIButton + tableView.isEditing = isModification + tableView.reloadData() + let deleteAllButton = tableView.viewWithTag(101) as! UIButton if isModification { - self.navigationItem.rightBarButtonItem?.title = "Terminer" + navigationItem.rightBarButtonItem?.title = "Terminer" } else { - self.navigationItem.rightBarButtonItem?.title = "Modifier" + navigationItem.rightBarButtonItem?.title = "Modifier" } - deleteAllButton.isHidden = !isModification || items.count==0 + deleteAllButton.isHidden = !isModification || items.count == 0 } @IBAction func deleteAll(_ sender: Any) { items.removeAll() tableView.reloadData() - //Sauvegarde des favoris MAJ + // Sauvegarde des favoris MAJ let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesAddressArray") hideDeleteAllIfNoAddress() } - + + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } } -//Retourne les adresses favorites de l'utilisateur, stockées sous la forme libelle***lat-long +// Retourne les adresses favorites de l'utilisateur, stockées sous la forme libelle***lat-long func getFavoritesAddress() -> [String] { let defaults = UserDefaults.standard - var favorite : [String] = [] + var favorite: [String] = [] - //Récupération des favoris dans les UserDefaults + // Récupération des favoris dans les UserDefaults if let favoritesArray = defaults.stringArray(forKey: "favoritesAddressArray") { favorite = favoritesArray } return favorite } - - extension ManageAddressViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "AddressTableViewCell") let adresseLabel = cell?.viewWithTag(100) as? UILabel let addressArr = items[indexPath.row].components(separatedBy: Constants.Key.separatorAdresseCoordonate) - + applyDynamicType(label: adresseLabel!, fontName: "Montserrat-Regular", size: 15) if addressArr.count == 2 { adresseLabel?.text = addressArr[0].trimmingCharacters(in: .whitespaces) } @@ -117,7 +135,7 @@ extension ManageAddressViewController: UITableViewDataSource, UITableViewDelegat _ = navigationController?.popViewController(animated: true) let currentLocation = CLLocationCoordinate2D(latitude: Double(coordArr[0])!, longitude: Double(coordArr[1])!) delegate.centerCameraToPosition(currentLocation: currentLocation) - delegate.retrieve(currentLocation: currentLocation, addMarker: true, address: addressArr[0]) { (result: Bool) in } + delegate.retrieve(currentLocation: currentLocation, addMarker: true, address: addressArr[0]) { (_: Bool) in } } } } @@ -130,19 +148,19 @@ extension ManageAddressViewController: UITableViewDataSource, UITableViewDelegat return self.tableView.isEditing } - //Modification de l'ordre des favoris + // Modification de l'ordre des favoris func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { - //Modification de l'ordre des favoris + // Modification de l'ordre des favoris let itemToMove = items[sourceIndexPath.row] items.remove(at: sourceIndexPath.row) items.insert(itemToMove, at: destinationIndexPath.row) - //Sauvegarde du nouvel ordre + // Sauvegarde du nouvel ordre let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesAddressArray") } - //Suppression de favoris + // Suppression de favoris func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { tableView.beginUpdates() @@ -151,7 +169,7 @@ extension ManageAddressViewController: UITableViewDataSource, UITableViewDelegat tableView.reloadData() tableView.endUpdates() - //Sauvegarde des favoris MAJ + // Sauvegarde des favoris MAJ let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesAddressArray") @@ -159,10 +177,10 @@ extension ManageAddressViewController: UITableViewDataSource, UITableViewDelegat } } - //Cache le bouton "tout supprimer" si il n'y a plus de favoris + // Cache le bouton "tout supprimer" si il n'y a plus de favoris func hideDeleteAllIfNoAddress() { - if(items.count==0) { - let deleteAllButton = self.tableView.viewWithTag(101) as! UIButton + if items.count == 0 { + let deleteAllButton = tableView.viewWithTag(101) as! UIButton deleteAllButton.isHidden = true } } diff --git a/DansMaRue/ManageFavorites.storyboard b/DansMaRue/ManageFavorites.storyboard index cfba00a..e9a3428 100644 --- a/DansMaRue/ManageFavorites.storyboard +++ b/DansMaRue/ManageFavorites.storyboard @@ -1,9 +1,9 @@ - + - + @@ -17,10 +17,10 @@ - + - - + - - - - + + + + @@ -63,6 +64,7 @@ + @@ -70,7 +72,6 @@ - @@ -80,6 +81,7 @@ + diff --git a/DansMaRue/ManageFavoritesViewController.swift b/DansMaRue/ManageFavoritesViewController.swift index 6108008..5c10754 100644 --- a/DansMaRue/ManageFavoritesViewController.swift +++ b/DansMaRue/ManageFavoritesViewController.swift @@ -9,55 +9,68 @@ import UIKit class ManageFavoritesViewController: UIViewController { - //MARK: Properties + // MARK: Properties + weak var delegate: TypeAnomalieViewController! var isModification = false - //MARK: IBOutlet - @IBOutlet weak var tableView: UITableView! + // MARK: IBOutlet + + @IBOutlet var tableView: UITableView! + @IBOutlet var deleteButton: UIButton! var items: [String] = [] - //MARK: - View lifecycle + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.title = Constants.LabelMessage.typeFavoris + navigationItem.titleView?.isAccessibilityElement = true + navigationItem.title = Constants.LabelMessage.typeFavoris items = getFavorites() tableView.dataSource = self + applyDynamicType(label: deleteButton.titleLabel!, fontName: Constants.fontDmr, size: 16) } - //Tap sur le bouton de modification + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: navigationItem.titleView) + } + + // Tap sur le bouton de modification @IBAction func modifier(_ sender: Any) { isModification = !isModification - self.tableView.isEditing = isModification - let deleteAllButton = self.tableView.viewWithTag(101) as! UIButton + tableView.isEditing = isModification + tableView.reloadData() + let deleteAllButton = tableView.viewWithTag(101) as! UIButton if isModification { - self.navigationItem.rightBarButtonItem?.title = "Terminer" + navigationItem.rightBarButtonItem?.title = "Terminer" } else { - self.navigationItem.rightBarButtonItem?.title = "Modifier" + navigationItem.rightBarButtonItem?.title = "Modifier" } - deleteAllButton.isHidden = !isModification || items.count==0 + deleteAllButton.isHidden = !isModification || items.count == 0 } @IBAction func deleteAll(_ sender: Any) { items.removeAll() tableView.reloadData() - //Sauvegarde des favoris MAJ + // Sauvegarde des favoris MAJ let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesArray") delegate.tableView.reloadData() hideDeleteAllIfNoAddress() } - - -} + private func applyDynamicType(label: UILabel, fontName: String, size: Float) { + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.scaledFont(name: fontName, textSize: CGFloat(size)) + } +} extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSections(in tableView: UITableView) -> Int { return 1 } @@ -65,18 +78,18 @@ extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDeleg func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let typeAnomalie = items[indexPath.row] print(typeAnomalie) - if let typeAno = ReferalManager.shared.getTypeAnomalie(withId: typeAnomalie) { - //Si type d'ano avec message + if let typeAno = ReferalManager.shared.getTypeAnomalie(withId: typeAnomalie) { + // Si type d'ano avec message if typeAno.messageBO != "" { let messageTypeAnoStoryboard = UIStoryboard(name: Constants.StoryBoard.messageTypeAno, bundle: nil) let messageTypeAnoVC = messageTypeAnoStoryboard.instantiateViewController(withIdentifier: Constants.ViewControllerIdentifier.messageTypeAno) as! MessageTypeAnoViewController - //Passage du type d'anomalie au controller + // Passage du type d'anomalie au controller messageTypeAnoVC.typeAnomalie = typeAno - self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) - self.navigationController?.pushViewController(messageTypeAnoVC, animated: true) + navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) + navigationController?.pushViewController(messageTypeAnoVC, animated: true) } else { - //Sinon selection du type d'ano + // Sinon selection du type d'ano delegate.changeTypeAnomalie(newType: typeAno) _ = navigationController?.popViewController(animated: true) } @@ -91,12 +104,12 @@ extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDeleg let isAgent = User.shared.isAgent let cellIdentifier = "TypeFavoritesTableViewCell" - guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TypeFavoritesTableViewCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TypeFavoritesTableViewCell else { fatalError("The dequeued cell is not an instance of TypeFavoritesTableViewCell.") } - - if let typeAnomalie = ReferalManager.shared.getTypeAnomalie(withId: items[indexPath.row]){ - if ( !typeAnomalie.isAgent || (typeAnomalie.isAgent && (isAgent != nil && isAgent!) ) ) { + applyDynamicType(label: cell.typeLabel, fontName: "Montserrat-Regular", size: 15) + if let typeAnomalie = ReferalManager.shared.getTypeAnomalie(withId: items[indexPath.row]) { + if !typeAnomalie.isAgent || (typeAnomalie.isAgent && (isAgent != nil && isAgent!)) { cell.typeLabel.text = typeAnomalie.name } else { cell.typeLabel.text = typeAnomalie.name @@ -112,24 +125,23 @@ extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDeleg return true } - func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool - { - return self.tableView.isEditing + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return self.tableView.isEditing } - //Modification de l'ordre des favoris + // Modification de l'ordre des favoris func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { - //Modification de l'ordre des favoris + // Modification de l'ordre des favoris let itemToMove = items[sourceIndexPath.row] items.remove(at: sourceIndexPath.row) items.insert(itemToMove, at: destinationIndexPath.row) - //Sauvegarde du nouvel ordre + // Sauvegarde du nouvel ordre let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesArray") } - //Suppression de favoris + // Suppression de favoris func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { print(indexPath) @@ -139,7 +151,7 @@ extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDeleg tableView.reloadData() tableView.endUpdates() - //Sauvegarde des favoris MAJ + // Sauvegarde des favoris MAJ let defaults = UserDefaults.standard defaults.set(items, forKey: "favoritesArray") @@ -149,23 +161,23 @@ extension ManageFavoritesViewController: UITableViewDataSource, UITableViewDeleg } } - //Cache le bouton "tout supprimer" si il n'y a plus de favoris + // Cache le bouton "tout supprimer" si il n'y a plus de favoris func hideDeleteAllIfNoAddress() { - if(items.count==0) { - let deleteAllButton = self.tableView.viewWithTag(101) as! UIButton + if items.count == 0 { + let deleteAllButton = tableView.viewWithTag(101) as! UIButton deleteAllButton.isHidden = true } } } -//Retourne les type favoris de l'utilisateur +// Retourne les type favoris de l'utilisateur private func getFavorites() -> [String] { - var favorite : [String] = [] + var favorite: [String] = [] let defaults = UserDefaults.standard - //Récupération des favoris dans les UserDefaults + // Récupération des favoris dans les UserDefaults if let favoritesArray = defaults.stringArray(forKey: "favoritesArray") { - //On ne garde que les types toujours présents dans le BO + // On ne garde que les types toujours présents dans le BO for typeFavorite in favoritesArray { if let type = ReferalManager.shared.getTypeAnomalie(withId: typeFavorite) { favorite.append(type.categorieId) diff --git a/DansMaRue/Model/Anomalie.swift b/DansMaRue/Model/Anomalie.swift index d0cb9eb..9b2c2e0 100644 --- a/DansMaRue/Model/Anomalie.swift +++ b/DansMaRue/Model/Anomalie.swift @@ -26,7 +26,8 @@ class Anomalie : NSObject, NSCoding { var photoCloseUrl: String var photoFarUrl: String var photoDoneUrl: String - + var messagesSFGeneric: [(id: String, message: String)] + var messagesSFTypologie: [(id: String, message: String)] var firstImageUrl: String { return photoFarUrl.isEmpty ? photoCloseUrl : photoFarUrl } @@ -98,7 +99,7 @@ class Anomalie : NSObject, NSCoding { var number: String //MARK: Initialization - init(address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, photo1: UIImage?, photo2: UIImage?, anomalieStatus: AnomalieStatus, mailUser: String?, number: String?) { + init(address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, photo1: UIImage?, photo2: UIImage?, anomalieStatus: AnomalieStatus, mailUser: String?, number: String?, messagesSFGeneric: [(id: String, message: String)], messagesSFTypologie: [(id: String, message: String)]) { // Initialize stored properties. self.id = "" self.address = address @@ -129,10 +130,12 @@ class Anomalie : NSObject, NSCoding { self.alias = "" self.resolvedAuthorization = false self.number = number ?? "" + self.messagesSFGeneric = messagesSFGeneric + self.messagesSFTypologie = messagesSFTypologie } - init(id: String?, address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, anomalieStatus: AnomalieStatus, photoCloseUrl: String?, photoFarUrl: String?, photoDoneUrl: String?, number: String?) { + init(id: String?, address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, anomalieStatus: AnomalieStatus, photoCloseUrl: String?, photoFarUrl: String?, photoDoneUrl: String?, number: String?, messagesSFGeneric: [(id: String, message: String)], messagesSFTypologie: [(id: String, message: String)]) { // Initialize stored properties. self.id = id ?? "" self.address = address @@ -163,6 +166,8 @@ class Anomalie : NSObject, NSCoding { self.alias = "" self.resolvedAuthorization = false self.number = number ?? "" + self.messagesSFGeneric = messagesSFGeneric + self.messagesSFTypologie = messagesSFTypologie } @@ -212,6 +217,8 @@ class Anomalie : NSObject, NSCoding { } self.resolvedAuthorization = false self.number = "" + self.messagesSFGeneric = [(id: String, message: String)]() + self.messagesSFTypologie = [(id: String, message: String)]() } func archive() -> Data { @@ -271,12 +278,12 @@ class AnomalieEquipement: Anomalie { aCoder.encode(equipementId, forKey: "equipementId") } - override init(address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, photo1: UIImage?, photo2: UIImage?, anomalieStatus: AnomalieStatus, mailUser: String?, number: String?) { - super.init(address: address, latitude: latitude, longitude: longitude, categorieId: categorieId, descriptive: descriptive, priorityId: priorityId, photo1: photo1, photo2: photo2, anomalieStatus: anomalieStatus, mailUser: mailUser, number: number) + override init(address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, photo1: UIImage?, photo2: UIImage?, anomalieStatus: AnomalieStatus, mailUser: String?, number: String?, messagesSFGeneric: [(id: String, message: String)], messagesSFTypologie: [(id: String, message: String)]) { + super.init(address: address, latitude: latitude, longitude: longitude, categorieId: categorieId, descriptive: descriptive, priorityId: priorityId, photo1: photo1, photo2: photo2, anomalieStatus: anomalieStatus, mailUser: mailUser, number: number, messagesSFGeneric: messagesSFGeneric, messagesSFTypologie: messagesSFTypologie) } - override init(id: String?, address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, anomalieStatus: AnomalieStatus, photoCloseUrl: String?, photoFarUrl: String?, photoDoneUrl: String?, number: String?) { - super.init(id: id, address: address, latitude: latitude, longitude: longitude, categorieId: categorieId, descriptive: descriptive, priorityId: priorityId, anomalieStatus: anomalieStatus, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: number) + override init(id: String?, address: String, latitude: Double, longitude: Double, categorieId: String?, descriptive: String?, priorityId: String?, anomalieStatus: AnomalieStatus, photoCloseUrl: String?, photoFarUrl: String?, photoDoneUrl: String?, number: String?, messagesSFGeneric: [(id: String, message: String)], messagesSFTypologie: [(id: String, message: String)]) { + super.init(id: id, address: address, latitude: latitude, longitude: longitude, categorieId: categorieId, descriptive: descriptive, priorityId: priorityId, anomalieStatus: anomalieStatus, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: number, messagesSFGeneric: messagesSFGeneric, messagesSFTypologie: messagesSFTypologie) } required init(coder decoder: NSCoder) { diff --git a/DansMaRue/Model/User.swift b/DansMaRue/Model/User.swift index e4408b7..baf383c 100644 --- a/DansMaRue/Model/User.swift +++ b/DansMaRue/Model/User.swift @@ -10,44 +10,46 @@ import Foundation import TTGSnackbar class User { - // MARK: - Properties + var firstName: String? var lastName: String? var email: String? var password: String? var isAgent: Bool? + var uid: String? var tokenId: String? - var uid: String? var accessToken: String? - var isLogged:Bool = false + var isLogged: Bool = false private init() { - firstName = UserDefaults.standard.object(forKey: Constants.Key.firstName) as? String - lastName = UserDefaults.standard.object(forKey: Constants.Key.lastName) as? String - email = UserDefaults.standard.object(forKey: Constants.Key.email) as? String - password = UserDefaults.standard.object(forKey: Constants.Key.password) as? String - isAgent = UserDefaults.standard.object(forKey: Constants.Key.isAgent) as? Bool + self.firstName = UserDefaults.standard.object(forKey: Constants.Key.firstName) as? String + self.lastName = UserDefaults.standard.object(forKey: Constants.Key.lastName) as? String + self.email = UserDefaults.standard.object(forKey: Constants.Key.email) as? String + self.password = UserDefaults.standard.object(forKey: Constants.Key.password) as? String + self.isAgent = UserDefaults.standard.object(forKey: Constants.Key.isAgent) as? Bool } static let shared = User() func automaticAuthentification() { - if let email = self.email, let password = self.password { - RestApiManager.sharedInstance.authenticate(email: email, password: password) { + if let uid = UserDefaults.standard.object(forKey: Constants.Key.uid) as? String { + User.shared.uid = uid + RestApiManager.sharedInstance.getIdentityStore(guid: uid) { (result: Bool) in - if result { print("Autentification réussi ...") DispatchQueue.main.async { - TTGSnackbar.init(message: "Authentification réussie", duration: .middle).show() + TTGSnackbar(message: "Authentification réussie", duration: .middle).show() + UIAccessibility.post(notification: .announcement, argument: "Authentification réussie") } } else { print("Echec Autentification ...") DispatchQueue.main.async { - TTGSnackbar.init(message: "Echec de l'authentification", duration: .middle).show() + TTGSnackbar(message: "Echec de l'authentification", duration: .middle).show() + UIAccessibility.post(notification: .announcement, argument: "Echec de l'authentification") self.email = nil self.password = nil } @@ -63,11 +65,13 @@ class User { self.email = nil self.password = nil self.isAgent = nil + self.uid = nil UserDefaults.standard.removeObject(forKey: Constants.Key.firstName) UserDefaults.standard.removeObject(forKey: Constants.Key.lastName) UserDefaults.standard.removeObject(forKey: Constants.Key.email) UserDefaults.standard.removeObject(forKey: Constants.Key.password) UserDefaults.standard.removeObject(forKey: Constants.Key.isAgent) + UserDefaults.standard.removeObject(forKey: Constants.Key.uid) } } diff --git a/DansMaRue/Services/RestApiManager.swift b/DansMaRue/Services/RestApiManager.swift index 15b5d54..efe546a 100644 --- a/DansMaRue/Services/RestApiManager.swift +++ b/DansMaRue/Services/RestApiManager.swift @@ -65,7 +65,7 @@ class RestApiManager: NSObject { // On affiche dans la liste que les anos en cours - let anomalie = Anomalie(id: marker["id"].stringValue, address: marker["address"].stringValue, latitude: Double(marker["lat"].floatValue), longitude: Double(marker["lng"].floatValue), categorieId: categorieId, descriptive: marker["descriptive"].stringValue, priorityId: marker["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: nil, number: marker["numero"].stringValue) + let anomalie = Anomalie(id: marker["id"].stringValue, address: marker["address"].stringValue, latitude: Double(marker["lat"].floatValue), longitude: Double(marker["lng"].floatValue), categorieId: categorieId, descriptive: marker["descriptive"].stringValue, priorityId: marker["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: nil, number: marker["numero"].stringValue, messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) anomalie.isIncidentFollowedByUser = marker["isIncidentFollowedByUser"].boolValue anomalie.alias = marker["alias"].stringValue @@ -105,43 +105,6 @@ class RestApiManager: NSObject { }) } - /// Méthode permettant de rechercher toutes les anomalies Ramen à proximiter des coordonnées spécifiées. - /// - /// - Parameters: - /// - coordinates: Coordonnées de l'utilisateur - /// - onCompletion: Flux JSON retourné par le service - func getDossierRamenByPosition(coordinates: CLLocationCoordinate2D, onCompletion: @escaping ([Anomalie]) -> Void) { - let route = Constants.Services.apiBaseUrl + "signalement/getDossiersCourrantsByGeomWithLimit" - - let bodyString = "{\"latitude\":\(coordinates.latitude),\"longitude\":\(coordinates.longitude)}" - - let headerList = [ - "Content-Type": "application/json;" - ] - - makeHTTPPostRequest(isJson: false, path: route, body: bodyString, header: headerList, onCompletion: { json, err in - if let jsonArray = json.array { - var anomalies = [Anomalie]() - for item in jsonArray { - if let jsonDict = item.dictionary { - // Create anomalie - // On affiche dans la liste que les anos en cours - let anomalie = Anomalie(id: jsonDict["id"]?.stringValue, address: (jsonDict["adresse"]?.stringValue)!, latitude: Double((jsonDict["lat"]?.floatValue)!), longitude: Double((jsonDict["lng"]?.floatValue)!), categorieId: nil, descriptive: nil, priorityId: nil, anomalieStatus: AnomalieStatus.Ouvert, photoCloseUrl: nil, photoFarUrl: nil, photoDoneUrl: nil, number: "") - - anomalie.source = AnomalieSource(rawValue: "ramen") ?? .ramen - anomalie.alias = "Demande d'enlèvement des objets encombrants" - - anomalies.append(anomalie) - } - onCompletion(anomalies) - } - } - - }) - - } - - /// Méthode permettant de récupérer le détail d'une anomalie /// /// - Parameter id Signalement: Id du signalement sélectionné @@ -166,6 +129,25 @@ class RestApiManager: NSObject { for item in jsonArray { if let jsonDict = item.dictionary { if let answer = jsonDict["answer"]?.dictionary { + var messagesSFTypologie = [(id: String, message: String)]() + var messagesSFGeneric = [(id: String, message: String)]() + + if let messagesSFGenericFromWS = answer["messages_sf_generic"]?.arrayValue { + for messageSFGeneric in messagesSFGenericFromWS { + let numeroMessage = messageSFGeneric.dictionary!["numero_message"]?.stringValue + let message = messageSFGeneric.dictionary!["message"]?.stringValue + messagesSFGeneric.append((id: numeroMessage!, message: message!)) + } + } + + if let messagesSFTypologieFromWS = answer["messages_sf_typologie"]?.arrayValue { + for messageSFTypologie in messagesSFTypologieFromWS { + let numeroMessage = messageSFTypologie.dictionary!["numero_message"]?.stringValue + let message = messageSFTypologie.dictionary!["message"]?.stringValue + messagesSFTypologie.append((id: numeroMessage!, message: message!)) + } + } + if let marker = answer["incident"]?.dictionary { // Creation d'une anomalie @@ -198,7 +180,7 @@ class RestApiManager: NSObject { let state = marker["state"]?.stringValue ?? "O" let status = AnomalieStatus(rawValue: state) ?? .Ouvert // On affiche l'anomalie sélectionée dans la liste - let selectedAnomaly = Anomalie(id: marker["id"]?.stringValue, address: (marker["address"]?.stringValue)!, latitude: Double((marker["lat"]?.floatValue)!), longitude: Double((marker["lng"]?.floatValue)!), categorieId: marker["categoryId"]?.stringValue, descriptive: marker["descriptive"]?.stringValue, priorityId: marker["priorityId"]?.stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: marker["numero"]?.stringValue) + let selectedAnomaly = Anomalie(id: marker["id"]?.stringValue, address: (marker["address"]?.stringValue)!, latitude: Double((marker["lat"]?.floatValue)!), longitude: Double((marker["lng"]?.floatValue)!), categorieId: marker["categoryId"]?.stringValue, descriptive: marker["descriptive"]?.stringValue, priorityId: marker["priorityId"]?.stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: marker["numero"]?.stringValue, messagesSFGeneric: messagesSFGeneric, messagesSFTypologie: messagesSFTypologie) let source = AnomalieSource(rawValue: (marker["source"]?.stringValue)!) ?? .dmr selectedAnomaly.source = source @@ -235,7 +217,6 @@ class RestApiManager: NSObject { if let resolvedAuthorization = answer["resolved_authorization"]!.bool { selectedAnomaly.resolvedAuthorization = resolvedAuthorization } - onCompletion(selectedAnomaly) @@ -247,6 +228,15 @@ class RestApiManager: NSObject { }) } + func logoutMonParis( token : String, onCompletion: @escaping (Bool) -> Void) { + let route = Constants.Authentification.logoutEndpoint + "?id_token_hint=" + token + + self.makeHTTPGetRequest(path: route, header: ["":""], onCompletion: { + json, err in + onCompletion(true) + }) + } + /// Méthode permettant de récupérer le détail d'une anomalie equipemnet /// /// - Parameter id Signalement: Id du signalement sélectionné @@ -300,7 +290,7 @@ class RestApiManager: NSObject { let state = marker["state"]?.stringValue ?? "O" let status = AnomalieStatus(rawValue: state) ?? .Ouvert // On affiche l'anomalie sélectionée dans la liste - let selectedAnomaly = AnomalieEquipement(id: marker["id"]?.stringValue, address: (marker["address"]?.stringValue)!, latitude: Double((marker["lat"]?.floatValue)!), longitude: Double((marker["lng"]?.floatValue)!), categorieId: marker["categoryId"]?.stringValue, descriptive: marker["descriptive"]?.stringValue, priorityId: marker["priorityId"]?.stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: "") + let selectedAnomaly = AnomalieEquipement(id: marker["id"]?.stringValue, address: (marker["address"]?.stringValue)!, latitude: Double((marker["lat"]?.floatValue)!), longitude: Double((marker["lng"]?.floatValue)!), categorieId: marker["categoryId"]?.stringValue, descriptive: marker["descriptive"]?.stringValue, priorityId: marker["priorityId"]?.stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) selectedAnomaly.source = .dmr @@ -552,6 +542,8 @@ class RestApiManager: NSObject { anomalie.anomalieStatus = AnomalieStatus.APublier AnomalieBrouillon.shared.append(anomalie: anomalie) onCompletion(false) + } else { + onCompletion(false) } @@ -568,9 +560,9 @@ class RestApiManager: NSObject { let headerList = [ "Content-Type": "image/jpeg", "type": type, - "incident_id": incidentId, + "incident-id": incidentId, "udid": uuid, - "img_comment": "no_comment" + "img-comment": "no_comment" ] makeHTTPPostRequest(isJson: false, path: route, body: data!, header: headerList, onCompletion: { json, err in @@ -677,7 +669,7 @@ class RestApiManager: NSObject { /// - Parameters: /// - anomalie: Instance de l'anomalie à déclarer comme service fait /// - onCompletion: True si status = 0, false sinon - func incidentResolved(anomalie: Anomalie, onCompletion: @escaping (Bool) -> Void) { + func incidentResolved(anomalie: Anomalie, numeroMessage: String, email: String, onCompletion: @escaping (Bool) -> Void) { var route = Constants.Services.apiBaseUrl + Constants.Services.apiUrl if anomalie as? AnomalieEquipement != nil { @@ -687,7 +679,13 @@ class RestApiManager: NSObject { let guid = User.shared.uid ?? "" let udid = UIDevice.current.identifierForVendor?.uuidString ?? "-1" - let bodyNoJson = "jsonStream=[{\"request\":\"incidentResolved\", \"incidentId\":\"\(anomalie.id)\", \"guid\":\"\(guid)\", \"udid\":\"\(udid)\"}]" + var bodyNoJson = "" + + if(numeroMessage != "") { + bodyNoJson = "jsonStream=[{\"request\":\"incidentResolved\", \"incidentId\":\"\(anomalie.id)\", \"guid\":\"\(guid)\", \"udid\":\"\(udid)\", \"numeroMessage\":\"\(numeroMessage)\", \"isGeneric\":\"false\", \"email\":\"\(email)\"}]" + } else { + bodyNoJson = "jsonStream=[{\"request\":\"incidentResolved\", \"incidentId\":\"\(anomalie.id)\", \"guid\":\"\(guid)\", \"udid\":\"\(udid)\", \"email\":\"\(email)\"}]" + } let headerList = [ "Content-Type": "application/x-www-form-urlencoded", @@ -857,7 +855,7 @@ class RestApiManager: NSObject { // On affiche dans la liste que les anos en cours - let anomalie = Anomalie(id: incident["id"].stringValue, address: incident["address"].stringValue, latitude: Double(incident["lat"].floatValue), longitude: Double(incident["lng"].floatValue), categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: nil, photoDoneUrl: nil, number: number) + let anomalie = Anomalie(id: incident["id"].stringValue, address: incident["address"].stringValue, latitude: Double(incident["lat"].floatValue), longitude: Double(incident["lng"].floatValue), categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: nil, photoDoneUrl: nil, number: number, messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) anomalie.alias = incident["alias"].stringValue anomalie.followers = incident["followers"].intValue @@ -923,7 +921,7 @@ class RestApiManager: NSObject { // On affiche dans la liste que les anos en cours - let anomalie = AnomalieEquipement(id: incident["id"].stringValue, address: incident["address"].stringValue, latitude: Double(incident["lat"].floatValue), longitude: Double(incident["lng"].floatValue), categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: nil, photoDoneUrl: nil, number : "") + let anomalie = AnomalieEquipement(id: incident["id"].stringValue, address: incident["address"].stringValue, latitude: Double(incident["lat"].floatValue), longitude: Double(incident["lng"].floatValue), categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: nil, photoDoneUrl: nil, number : "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) anomalie.alias = incident["alias"].stringValue anomalie.followers = incident["followers"].intValue @@ -1090,6 +1088,8 @@ class RestApiManager: NSObject { User.shared.lastName = user["name"]?.stringValue User.shared.firstName = user["firstname"]?.stringValue User.shared.isAgent = user["isAgent"]?.boolValue + User.shared.isLogged = true + User.shared.email = user["mail"]?.stringValue } } } @@ -1406,7 +1406,7 @@ class RestApiManager: NSObject { let categorieId = incident["categoryId"].stringValue // On affiche dans la liste que les anos en cours - let anomalie = AnomalieEquipement(id: incident["id"].stringValue, address: equipement.adresse, latitude: equipement.latitude, longitude: equipement.longitude, categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: "") + let anomalie = AnomalieEquipement(id: incident["id"].stringValue, address: equipement.adresse, latitude: equipement.latitude, longitude: equipement.longitude, categorieId: categorieId, descriptive: incident["descriptive"].stringValue, priorityId: incident["priorityId"].stringValue, anomalieStatus: status, photoCloseUrl: photoCloseUrl, photoFarUrl: photoFarUrl, photoDoneUrl: photoDoneUrl, number: "", messagesSFGeneric: [(id: String, message: String)](),messagesSFTypologie:[(id: String, message: String)]()) anomalie.isIncidentFollowedByUser = incident["isIncidentFollowedByUser"].boolValue anomalie.alias = incident["alias"].stringValue @@ -1450,6 +1450,7 @@ class RestApiManager: NSObject { // MARK: Perform a HEAD Request private func makeHTTPHeadRequest(path: String, header: [String: String], onCompletion: @escaping (_ result: Int)->()){ let request = NSMutableURLRequest(url: NSURL(string: path)! as URL) + request.addValue( Constants.Services.tokenWS, forHTTPHeaderField: Constants.Services.headerKeyTokenWS) for header in header { request.addValue(header.value, forHTTPHeaderField: header.key) } @@ -1469,6 +1470,7 @@ class RestApiManager: NSObject { // MARK: Perform a GET Request private func makeHTTPGetRequest(path: String, header: [String: String], onCompletion: @escaping ServiceResponse) { let request = NSMutableURLRequest(url: NSURL(string: path)! as URL) + request.addValue( Constants.Services.tokenWS, forHTTPHeaderField: Constants.Services.headerKeyTokenWS) for header in header { if header.key != "" { request.addValue(header.value, forHTTPHeaderField: header.key) @@ -1494,6 +1496,7 @@ class RestApiManager: NSObject { // MARK: Perform a POST Request private func makeHTTPPostRequest(isJson: Bool, path: String, body: Any, header: [String: String], onCompletion: @escaping ServiceResponse) { var request = URLRequest(url: NSURL(string: path)! as URL) + request.addValue( Constants.Services.tokenWS, forHTTPHeaderField: Constants.Services.headerKeyTokenWS) for header in header { request.addValue(header.value, forHTTPHeaderField: header.key) } diff --git a/DansMaRue/Storyboards/Anomalie/AddAnomaly.storyboard b/DansMaRue/Storyboards/Anomalie/AddAnomaly.storyboard index d15288a..6723465 100644 --- a/DansMaRue/Storyboards/Anomalie/AddAnomaly.storyboard +++ b/DansMaRue/Storyboards/Anomalie/AddAnomaly.storyboard @@ -1,9 +1,9 @@ - + - + @@ -25,31 +25,36 @@ - + - + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + - + @@ -100,20 +119,20 @@ - + - - + + - - - - - - - - - + + + + + + + + + + @@ -150,7 +171,7 @@ - + @@ -163,23 +184,23 @@ - + - - - - - - - - - - - + - + + + + + + - + @@ -266,18 +287,18 @@ - - + + - - + @@ -314,20 +340,20 @@ - + + - - - - - - - - + + + + + + + + + @@ -353,14 +382,14 @@ - + - - - - - + + + @@ -396,12 +424,12 @@ - + - + @@ -425,7 +453,7 @@ - + diff --git a/DansMaRue/Storyboards/Anomalie/AnomalyDetail.storyboard b/DansMaRue/Storyboards/Anomalie/AnomalyDetail.storyboard index 9db129e..ec66256 100644 --- a/DansMaRue/Storyboards/Anomalie/AnomalyDetail.storyboard +++ b/DansMaRue/Storyboards/Anomalie/AnomalyDetail.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -24,235 +25,248 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - + @@ -264,5 +278,8 @@ + + + diff --git a/DansMaRue/Storyboards/Anomalie/DescriptiveAnomaly.storyboard b/DansMaRue/Storyboards/Anomalie/DescriptiveAnomaly.storyboard index eb30a4e..6abd544 100644 --- a/DansMaRue/Storyboards/Anomalie/DescriptiveAnomaly.storyboard +++ b/DansMaRue/Storyboards/Anomalie/DescriptiveAnomaly.storyboard @@ -1,10 +1,9 @@ - - - - + + - + + @@ -25,45 +24,53 @@ - - + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + - - + + + + - + + + - + + - + diff --git a/DansMaRue/Storyboards/Anomalie/PopupPhoto.storyboard b/DansMaRue/Storyboards/Anomalie/PopupPhoto.storyboard index 4742837..559d92b 100644 --- a/DansMaRue/Storyboards/Anomalie/PopupPhoto.storyboard +++ b/DansMaRue/Storyboards/Anomalie/PopupPhoto.storyboard @@ -1,11 +1,9 @@ - - - - + + - - + + @@ -27,7 +25,7 @@ - + - - - + - + @@ -112,4 +110,9 @@ + + + + + diff --git a/DansMaRue/Storyboards/Anomalie/Priority.storyboard b/DansMaRue/Storyboards/Anomalie/Priority.storyboard index 837c86b..e233276 100644 --- a/DansMaRue/Storyboards/Anomalie/Priority.storyboard +++ b/DansMaRue/Storyboards/Anomalie/Priority.storyboard @@ -1,10 +1,9 @@ - - - - - + + + - + + @@ -25,26 +24,31 @@ - + - + - + - + + + + + + @@ -66,7 +70,7 @@ - + diff --git a/DansMaRue/Storyboards/Anomalie/Thanks.storyboard b/DansMaRue/Storyboards/Anomalie/Thanks.storyboard index 8e77e51..8a79fe5 100644 --- a/DansMaRue/Storyboards/Anomalie/Thanks.storyboard +++ b/DansMaRue/Storyboards/Anomalie/Thanks.storyboard @@ -1,20 +1,15 @@ - - - - + + - + Montserrat-Regular - - Montserrat-SemiBold - @@ -36,103 +31,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -144,14 +45,7 @@ - - - - - - - @@ -257,6 +151,5 @@ - diff --git a/DansMaRue/Storyboards/Anomalie/TypeAnomalie.storyboard b/DansMaRue/Storyboards/Anomalie/TypeAnomalie.storyboard index a39d5ea..6167048 100644 --- a/DansMaRue/Storyboards/Anomalie/TypeAnomalie.storyboard +++ b/DansMaRue/Storyboards/Anomalie/TypeAnomalie.storyboard @@ -1,9 +1,9 @@ - + - + @@ -24,21 +24,12 @@ - - - - - - - - - - + - + @@ -47,34 +38,39 @@ - - + + - - + + - - - - - - - - - + + + + + + + + + + @@ -90,13 +86,32 @@ + + + + + + - - - - + + + + + + + + + + + + @@ -109,8 +124,9 @@ - + + diff --git a/DansMaRue/Storyboards/CustomUIAlertView.storyboard b/DansMaRue/Storyboards/CustomUIAlertView.storyboard new file mode 100644 index 0000000..45d7e3c --- /dev/null +++ b/DansMaRue/Storyboards/CustomUIAlertView.storyboard @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DansMaRue/Storyboards/LaunchScreen.storyboard b/DansMaRue/Storyboards/LaunchScreen.storyboard index 985d24f..9bfb8f0 100644 --- a/DansMaRue/Storyboards/LaunchScreen.storyboard +++ b/DansMaRue/Storyboards/LaunchScreen.storyboard @@ -1,9 +1,9 @@ - + - + @@ -22,43 +22,54 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/DansMaRue/Storyboards/Main.storyboard b/DansMaRue/Storyboards/Main.storyboard index 251eb8b..cae66a0 100644 --- a/DansMaRue/Storyboards/Main.storyboard +++ b/DansMaRue/Storyboards/Main.storyboard @@ -1,9 +1,9 @@ - + - + diff --git a/DansMaRue/Storyboards/Map.storyboard b/DansMaRue/Storyboards/Map.storyboard index ff8f45e..c6f9d20 100644 --- a/DansMaRue/Storyboards/Map.storyboard +++ b/DansMaRue/Storyboards/Map.storyboard @@ -1,9 +1,9 @@ - + - + @@ -23,19 +23,19 @@ - - + + - + - - - - + + + + @@ -56,12 +57,13 @@ + - + @@ -79,98 +81,160 @@ - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - + - - - + + + + - - - - - + + + + + + + - + - - - - - + + + + - + @@ -180,39 +244,34 @@ - + + - @@ -237,7 +296,7 @@ - + @@ -249,20 +308,20 @@ - + - + diff --git a/DansMaRue/Storyboards/Profile.storyboard b/DansMaRue/Storyboards/Profile.storyboard index 97f213c..6a2f131 100644 --- a/DansMaRue/Storyboards/Profile.storyboard +++ b/DansMaRue/Storyboards/Profile.storyboard @@ -1,9 +1,9 @@ - + - + @@ -31,80 +31,100 @@ + + + + + + + + + + + + + + - + - + - + - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -112,38 +132,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -153,11 +167,12 @@ + - + @@ -168,32 +183,24 @@ - + - + - + - - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -221,14 +256,15 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - - - - - + - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + @@ -494,28 +578,35 @@ + - + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - + + + + + + + + @@ -525,7 +616,7 @@ - + @@ -539,38 +630,38 @@ - - + + - - + + - + - + - + + - - - - - - - - + + + + + + @@ -580,8 +671,8 @@ - - - - - - - - + + + + + + + @@ -605,7 +696,7 @@ - + @@ -619,21 +710,47 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + - - - - - - - + + + + + + + + @@ -685,7 +809,7 @@ - + @@ -694,7 +818,7 @@ - + @@ -717,6 +841,9 @@ + + + diff --git a/DansMaRue/Storyboards/Welcome.storyboard b/DansMaRue/Storyboards/Welcome.storyboard index ce32568..ca848d2 100644 --- a/DansMaRue/Storyboards/Welcome.storyboard +++ b/DansMaRue/Storyboards/Welcome.storyboard @@ -1,15 +1,12 @@ - + - + - - Montserrat-Light - Montserrat-Regular @@ -33,14 +30,13 @@ - -