From ce60bd6781a78d76c4a5b87259438d6b6684f917 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Fri, 15 Dec 2023 14:17:02 +0200 Subject: [PATCH 01/13] Added a new Storage menu screen for accessing Add storage, Gift storage and Redeem code. --- Permanent.xcodeproj/project.pbxproj | 88 +++++++++++------ .../SwiftUIViews/CustomListItemView.swift | 49 ++++++++++ .../SwiftUIViews/GradientCustomBarView.swift | 42 ++++++++ .../Base/SwiftUIViews/NewBadgeView.swift | 31 ++++++ Permanent/Common/Constants/Colors.swift | 14 +++ Permanent/Common/Models/DrawerOption.swift | 6 ++ .../ViewModifiers/TextViewModifiers.swift | 16 ++++ .../RightSideMenuViewController.swift | 12 +-- .../DonateViewController.swift | 0 .../{ => ViewModels}/DonateViewModel.swift | 0 .../ViewModels/GiftStorageViewModel.swift | 0 .../Storage/ViewModels/StorageViewModel.swift | 39 ++++++++ .../Storage/Views/AddStorageView.swift | 57 +++++++++++ .../Storage/Views/DonateStorageView.swift | 21 ++++ .../Views/GiftStorageView.swift | 0 .../Storage/Views/RedeemCodeView.swift | 59 ++++++++++++ .../Modules/Storage/Views/StorageView.swift | 95 +++++++++++++++++++ .../BarneyPurple.colorset/Contents.json | 18 ---- .../Colors/BilbaoGreen.colorset/Contents.json | 18 ---- .../Colors/Black.colorset/Contents.json | 18 ---- .../Colors/Blue400.colorset/Contents.json | 20 ++++ .../Colors/Blue900.colorset/Contents.json | 20 ++++ .../Colors/BlueGray.colorset/Contents.json | 18 ---- .../Colors/BrightRed.colorset/Contents.json | 18 ---- .../Colors/Yellow.colorset/Contents.json | 20 ++++ .../Images/StorageMenu/Contents.json | 6 ++ .../storageGift.imageset/Contents.json | 15 +++ .../storageGift.imageset/storageGift.svg | 3 + .../storagePlus.imageset/Contents.json | 15 +++ .../storagePlus.imageset/storagePlus.svg | 3 + .../storageRedeem.imageset/Contents.json | 15 +++ .../storageRedeem.imageset/storageRedeem.svg | 3 + .../Resources/Storyboards/Donate.storyboard | 8 +- 33 files changed, 618 insertions(+), 129 deletions(-) create mode 100644 Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift create mode 100644 Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift create mode 100644 Permanent/Common/Base/SwiftUIViews/NewBadgeView.swift rename Permanent/Modules/Storage/{ => ViewControllers}/DonateViewController.swift (100%) rename Permanent/Modules/Storage/{ => ViewModels}/DonateViewModel.swift (100%) rename Permanent/Modules/{GiftStorage => Storage}/ViewModels/GiftStorageViewModel.swift (100%) create mode 100644 Permanent/Modules/Storage/ViewModels/StorageViewModel.swift create mode 100644 Permanent/Modules/Storage/Views/AddStorageView.swift create mode 100644 Permanent/Modules/Storage/Views/DonateStorageView.swift rename Permanent/Modules/{GiftStorage => Storage}/Views/GiftStorageView.swift (100%) create mode 100644 Permanent/Modules/Storage/Views/RedeemCodeView.swift create mode 100644 Permanent/Modules/Storage/Views/StorageView.swift create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue400.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue900.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Yellow.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/storageGift.svg create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/storagePlus.svg create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/storageRedeem.svg diff --git a/Permanent.xcodeproj/project.pbxproj b/Permanent.xcodeproj/project.pbxproj index a6eebaca..d71e8710 100644 --- a/Permanent.xcodeproj/project.pbxproj +++ b/Permanent.xcodeproj/project.pbxproj @@ -172,6 +172,9 @@ 5E4D0AF528328AC500C6439C /* AccountOnboardingHeaderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5E4D0AF328328AC500C6439C /* AccountOnboardingHeaderTableViewCell.xib */; }; 5E4E4CCF29826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E4E4CCD29826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.swift */; }; 5E4E4CD029826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5E4E4CCE29826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.xib */; }; + 5E548E842B29F9D600DD2C59 /* AddStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E548E832B29F9D600DD2C59 /* AddStorageView.swift */; }; + 5E548E862B29F9F000DD2C59 /* RedeemCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E548E852B29F9F000DD2C59 /* RedeemCodeView.swift */; }; + 5E548E8A2B2AF3EF00DD2C59 /* DonateStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E548E892B2AF3EF00DD2C59 /* DonateStorageView.swift */; }; 5E559EC829BF438200F129BF /* IntExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E559EC729BF438200F129BF /* IntExtension.swift */; }; 5E559EC929BF446200F129BF /* IntExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E559EC729BF438200F129BF /* IntExtension.swift */; }; 5E56FDBF2744247C00BC3BE0 /* ProfilePageArchiveCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E56FDBD2744247C00BC3BE0 /* ProfilePageArchiveCollectionViewCell.swift */; }; @@ -208,6 +211,8 @@ 5E62F7C927F79FF40046F6C8 /* PopularArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E62F7C827F79FF40046F6C8 /* PopularArchive.swift */; }; 5E62F7CE27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E62F7CC27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.swift */; }; 5E62F7CF27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5E62F7CD27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.xib */; }; + 5E633A722B28D7E1004D561C /* StorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E633A712B28D7E1004D561C /* StorageView.swift */; }; + 5E633A742B28D7EE004D561C /* StorageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E633A732B28D7EE004D561C /* StorageViewModel.swift */; }; 5E63944E29702F830043D952 /* ForgotPasswordResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E63944D29702F830043D952 /* ForgotPasswordResponse.swift */; }; 5E63B3E528A3EC3300067FAF /* SharedFilesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E63B3E428A3EC3300067FAF /* SharedFilesUITests.swift */; }; 5E63B3E728A43EC400067FAF /* LeftSideMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E63B3E628A43EC400067FAF /* LeftSideMenu.swift */; }; @@ -273,6 +278,9 @@ 5EA03CA527832D0300CE0320 /* BasicProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA03CA427832D0200CE0320 /* BasicProfileItem.swift */; }; 5EA04CE826AEE14A00BC00FB /* SignUpTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA04CE726AEE14A00BC00FB /* SignUpTests.swift */; }; 5EA5B3DA2A71026500C39217 /* BottomButtonsSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA5B3D92A71026500C39217 /* BottomButtonsSectionView.swift */; }; + 5EA70D8B2B2C78E200175D9C /* NewBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */; }; + 5EA70D8D2B2C791100175D9C /* CustomListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8C2B2C791100175D9C /* CustomListItemView.swift */; }; + 5EA70D8F2B2C793A00175D9C /* GradientCustomBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8E2B2C793A00175D9C /* GradientCustomBarView.swift */; }; 5EADF803262475D500D14E9C /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADF801262475D500D14E9C /* TagCollectionViewCell.swift */; }; 5EADF804262475D500D14E9C /* TagCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5EADF802262475D500D14E9C /* TagCollectionViewCell.xib */; }; 5EADF8082625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADF8062625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift */; }; @@ -989,6 +997,9 @@ 5E4D0AF328328AC500C6439C /* AccountOnboardingHeaderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountOnboardingHeaderTableViewCell.xib; sourceTree = ""; }; 5E4E4CCD29826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingsTagsHeaderCollectionView.swift; sourceTree = ""; }; 5E4E4CCE29826F4000FEF292 /* ArchiveSettingsTagsHeaderCollectionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ArchiveSettingsTagsHeaderCollectionView.xib; sourceTree = ""; }; + 5E548E832B29F9D600DD2C59 /* AddStorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddStorageView.swift; sourceTree = ""; }; + 5E548E852B29F9F000DD2C59 /* RedeemCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedeemCodeView.swift; sourceTree = ""; }; + 5E548E892B2AF3EF00DD2C59 /* DonateStorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonateStorageView.swift; sourceTree = ""; }; 5E559EC729BF438200F129BF /* IntExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntExtension.swift; sourceTree = ""; }; 5E56FDBD2744247C00BC3BE0 /* ProfilePageArchiveCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePageArchiveCollectionViewCell.swift; sourceTree = ""; }; 5E56FDBE2744247C00BC3BE0 /* ProfilePageArchiveCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfilePageArchiveCollectionViewCell.xib; sourceTree = ""; }; @@ -1021,6 +1032,8 @@ 5E62F7C827F79FF40046F6C8 /* PopularArchive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularArchive.swift; sourceTree = ""; }; 5E62F7CC27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicGalleryCellCollectionViewCell.swift; sourceTree = ""; }; 5E62F7CD27FB10610046F6C8 /* PublicGalleryCellCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PublicGalleryCellCollectionViewCell.xib; sourceTree = ""; }; + 5E633A712B28D7E1004D561C /* StorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageView.swift; sourceTree = ""; }; + 5E633A732B28D7EE004D561C /* StorageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageViewModel.swift; sourceTree = ""; }; 5E63944D29702F830043D952 /* ForgotPasswordResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgotPasswordResponse.swift; sourceTree = ""; }; 5E63B3E428A3EC3300067FAF /* SharedFilesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFilesUITests.swift; sourceTree = ""; }; 5E63B3E628A43EC400067FAF /* LeftSideMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftSideMenu.swift; sourceTree = ""; }; @@ -1081,6 +1094,9 @@ 5EA03CA427832D0200CE0320 /* BasicProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicProfileItem.swift; sourceTree = ""; }; 5EA04CE726AEE14A00BC00FB /* SignUpTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpTests.swift; sourceTree = ""; }; 5EA5B3D92A71026500C39217 /* BottomButtonsSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomButtonsSectionView.swift; sourceTree = ""; }; + 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewBadgeView.swift; sourceTree = ""; }; + 5EA70D8C2B2C791100175D9C /* CustomListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListItemView.swift; sourceTree = ""; }; + 5EA70D8E2B2C793A00175D9C /* GradientCustomBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientCustomBarView.swift; sourceTree = ""; }; 5EADF801262475D500D14E9C /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; 5EADF802262475D500D14E9C /* TagCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TagCollectionViewCell.xib; sourceTree = ""; }; 5EADF8062625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsNamesCollectionViewCell.swift; sourceTree = ""; }; @@ -1715,31 +1731,6 @@ path = Pages; sourceTree = ""; }; - 5E181B7C2AF0556F002DE69A /* GiftStorage */ = { - isa = PBXGroup; - children = ( - 5E181B7D2AF05590002DE69A /* Views */, - 5E181B802AF100EC002DE69A /* ViewModels */, - ); - path = GiftStorage; - sourceTree = ""; - }; - 5E181B7D2AF05590002DE69A /* Views */ = { - isa = PBXGroup; - children = ( - 5E181B7E2AF0640E002DE69A /* GiftStorageView.swift */, - ); - path = Views; - sourceTree = ""; - }; - 5E181B802AF100EC002DE69A /* ViewModels */ = { - isa = PBXGroup; - children = ( - 5E181B812AF10100002DE69A /* GiftStorageViewModel.swift */, - ); - path = ViewModels; - sourceTree = ""; - }; 5E1C3C4928ECA732007C1A63 /* Local */ = { isa = PBXGroup; children = ( @@ -2241,7 +2232,6 @@ 5ED3B3AA29F7D71E000CFF48 /* LegacyPlanning */, 5E4739B22A40FC5E00A20D85 /* PublicGallery */, 5E4739C32A41020A00A20D85 /* Storage */, - 5E181B7C2AF0556F002DE69A /* GiftStorage */, 5E4739C72A41034200A20D85 /* AccountSettings */, 5E4739C52A41026100A20D85 /* ActivityFeed */, 5E4739C82A4103A500A20D85 /* Archives */, @@ -2452,8 +2442,9 @@ 5E4739C32A41020A00A20D85 /* Storage */ = { isa = PBXGroup; children = ( - F58F314E27F32E2D00B89B99 /* DonateViewController.swift */, - F57CE595282BE9D000B06D95 /* DonateViewModel.swift */, + 5E633A6F2B28D768004D561C /* Views */, + 5E548E8B2B2AF43200DD2C59 /* ViewControllers */, + 5E633A702B28D777004D561C /* ViewModels */, ); path = Storage; sourceTree = ""; @@ -2625,6 +2616,14 @@ path = ViewModel; sourceTree = ""; }; + 5E548E8B2B2AF43200DD2C59 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + F58F314E27F32E2D00B89B99 /* DonateViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 5E5EF854273B23A3004F7EBC /* ViewController */ = { isa = PBXGroup; children = ( @@ -2659,6 +2658,28 @@ path = TagDetailsTableViewCells; sourceTree = ""; }; + 5E633A6F2B28D768004D561C /* Views */ = { + isa = PBXGroup; + children = ( + 5E633A712B28D7E1004D561C /* StorageView.swift */, + 5E548E832B29F9D600DD2C59 /* AddStorageView.swift */, + 5E548E892B2AF3EF00DD2C59 /* DonateStorageView.swift */, + 5E181B7E2AF0640E002DE69A /* GiftStorageView.swift */, + 5E548E852B29F9F000DD2C59 /* RedeemCodeView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 5E633A702B28D777004D561C /* ViewModels */ = { + isa = PBXGroup; + children = ( + 5E633A732B28D7EE004D561C /* StorageViewModel.swift */, + 5E181B812AF10100002DE69A /* GiftStorageViewModel.swift */, + F57CE595282BE9D000B06D95 /* DonateViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; 5E68830628902DBF0006F434 /* PermanentUITests */ = { isa = PBXGroup; children = ( @@ -2746,6 +2767,9 @@ 9229A3AA2A7A7773004DE31D /* PullDownButton.swift */, 5E37F2D42AA75248004F00E8 /* CustomDialogView.swift */, 5E181B862AF2EAF5002DE69A /* CustomStepperView.swift */, + 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */, + 5EA70D8C2B2C791100175D9C /* CustomListItemView.swift */, + 5EA70D8E2B2C793A00175D9C /* GradientCustomBarView.swift */, ); path = SwiftUIViews; sourceTree = ""; @@ -4205,7 +4229,9 @@ 5ED581B825EE4AB100A5A79E /* FileDetailsViewController.swift in Sources */, BC42EDBE25BB11B80031B965 /* InvitesViewController.swift in Sources */, BC6358B52536FD9200EEC48C /* FolderVO.swift in Sources */, + 5E633A742B28D7EE004D561C /* StorageViewModel.swift in Sources */, 5E1C3C5428F59703007C1A63 /* TagManagementViewController.swift in Sources */, + 5E633A722B28D7E1004D561C /* StorageView.swift in Sources */, 5EE0ADB325F8216D008ABDC6 /* TagVO.swift in Sources */, 5E980AB72657D3D400ED5764 /* BundleExtension.swift in Sources */, 5EBDBD4C26F1F698002142DB /* ArchiveScreenSectionTitleTableViewCell.swift in Sources */, @@ -4312,14 +4338,17 @@ 5E647334263C22F900E6566D /* NotificationName.swift in Sources */, BC581A4E255A98DB00171D7A /* MediaRecorder.swift in Sources */, 5E991EF62A53637E006229C0 /* SectionHeaderView.swift in Sources */, + 5EA70D8D2B2C791100175D9C /* CustomListItemView.swift in Sources */, 5E8A3E4B24F3AB4300812361 /* BasePageViewController.swift in Sources */, BC9DB37425516B27004F453B /* FolderInfo.swift in Sources */, + 5E548E862B29F9F000DD2C59 /* RedeemCodeView.swift in Sources */, 5E3E121F2A41902C00682DE5 /* LegacyPlanningDataSourceInterface.swift in Sources */, 5EA5B3DA2A71026500C39217 /* BottomButtonsSectionView.swift in Sources */, BCFB0C2925811757001D89E1 /* ManageLinkViewController.swift in Sources */, 924F16372A45CC6200B75D4E /* TextViewModifiers.swift in Sources */, F58EBC8425DFFB9600D2D383 /* MyFilesViewModel.swift in Sources */, BC752BCC253EBBE100EF7941 /* UploadFilesView.swift in Sources */, + 5EA70D8F2B2C793A00175D9C /* GradientCustomBarView.swift in Sources */, 5EE9D8C627A1EE8100CE5F9C /* EstablishedInfoProfileItem.swift in Sources */, 5EE17FF624EE94DA00496AF7 /* PageViewController.swift in Sources */, BC59BAD125C2B8BD005A45D3 /* NotificationProtocol.swift in Sources */, @@ -4381,6 +4410,7 @@ 5E7AE8AB291CE7FF00F2A41D /* ShareManagementSeparatorFooterCollectionViewCell.swift in Sources */, BCAEED8725C1B96200A13C0F /* InviteStatus.swift in Sources */, BC6AF9A925933EAF00483BBA /* MembersViewModel.swift in Sources */, + 5E548E842B29F9D600DD2C59 /* AddStorageView.swift in Sources */, BC30AFD925A46E7900D37BB5 /* ArchiveSharePayload.swift in Sources */, BC6AF9B92593675600483BBA /* ArchiveVOPayload.swift in Sources */, BCB0725925221494003E2F66 /* PreferencesManager.swift in Sources */, @@ -4423,6 +4453,7 @@ F50E135728F43368003DF4CC /* FilesRemoteDataSource.swift in Sources */, BC6D3B5A2514F62400390927 /* AuthenticationEndpoint.swift in Sources */, 5EBAFCE42A154606005DB527 /* LegacyPlanningLoadingViewController.swift in Sources */, + 5E548E8A2B2AF3EF00DD2C59 /* DonateStorageView.swift in Sources */, 5E7AE8B3291E97DA00F2A41D /* ShareManagementLinkAndShowSettingsCollectionViewCell.swift in Sources */, BCF906722589FA1700DF1B64 /* TooltipView.swift in Sources */, 5E63944E29702F830043D952 /* ForgotPasswordResponse.swift in Sources */, @@ -4461,6 +4492,7 @@ 5E991EDF2A48E05C006229C0 /* MetadataEditView.swift in Sources */, BC6D3B532514F0C100390927 /* APIOperation.swift in Sources */, BC648782251B9FC7009D7DE1 /* StoryboardName.swift in Sources */, + 5EA70D8B2B2C78E200175D9C /* NewBadgeView.swift in Sources */, 5E181B872AF2EAF5002DE69A /* CustomStepperView.swift in Sources */, 5E278898282D423F007EE374 /* AccountOnboardingPageOneWithPendingArchives.swift in Sources */, 5E0E3C302886EB69001C7F10 /* ShareExtensionViewModel.swift in Sources */, diff --git a/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift b/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift new file mode 100644 index 00000000..1d01b0d8 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift @@ -0,0 +1,49 @@ +// +// CustomListItemView.swift +// Permanent +// +// Created by Lucian Cerbu on 15.12.2023. + +import SwiftUI + +struct CustomListItemView: View { + var image: Image + var titleText: String + var descText: String + + var body: some View { + VStack { + HStack(alignment: .top, spacing: 24) { + image + .resizable() + .foregroundColor(.blue900) + .scaledToFit() + .frame(width: 24, height: 24) + .padding(.leading, 10) + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 10) { + Text(titleText) + .textStyle(SmallXSemiBoldTextStyle()) + .foregroundColor(.blue900) + if titleText == "Redeem code" { + NewBadgeView() + } + } + Text(descText) + .textStyle(SmallXXXRegularTextStyle()) + .foregroundColor(.blue400) + .lineLimit(2) + .multilineTextAlignment(.leading) + } + Spacer() + Image(systemName: "chevron.right") + .foregroundColor(.blue400) + .padding(.trailing, 10) + } + .padding(10) + if titleText != "Redeem code" { + Divider() + } + } + } +} diff --git a/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift new file mode 100644 index 00000000..facf6ee7 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift @@ -0,0 +1,42 @@ +// +// GradientCustomBarView.swift +// Permanent +// +// Created by Lucian Cerbu on 15.12.2023. + +import SwiftUI + +struct GradientProgressBarView: View { + var value: String + var maxValue: String + var sizeRatio: Double + + var body: some View { + VStack(spacing: 16) { + HStack { + HStack(spacing: 0) { + Text("\(value) ") + .textStyle(SmallXXXSemiBoldTextStyle()) + .foregroundColor(.white) + Text("used") + .textStyle(SmallXXXRegularTextStyle()) + .foregroundColor(.white) + } + Spacer() + Text("\(maxValue)") + .textStyle(SmallXXXSemiBoldTextStyle()) + .foregroundColor(.white) + } + .padding(.horizontal) + + ProgressView(value: sizeRatio) + .progressViewStyle(CustomBarProgressStyle(color: .white, height: 8, cornerRadius: 3)) + .frame(height: 12) + .padding(.horizontal) + } + .frame(maxHeight: 72) + .background(Gradient.purpleYellowGradient) + .cornerRadius(12) + .padding(16) + } +} diff --git a/Permanent/Common/Base/SwiftUIViews/NewBadgeView.swift b/Permanent/Common/Base/SwiftUIViews/NewBadgeView.swift new file mode 100644 index 00000000..0b7ce274 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/NewBadgeView.swift @@ -0,0 +1,31 @@ +// +// NewBadgeView.swift +// Permanent +// +// Created by Lucian Cerbu on 15.12.2023. + +import SwiftUI + +struct NewBadgeView: View { + var body: some View { + HStack { + if #available(iOS 16, *) { + Text("NEW") + .textStyle(SmallXXXXXSemiBoldTextStyle()) + .kerning(1.6) + .multilineTextAlignment(.center) + .foregroundColor(.white) + } else { + Text("NEW") + .textStyle(SmallXXXXXSemiBoldTextStyle()) + .multilineTextAlignment(.center) + .foregroundColor(.white) + } + } + .frame(height: 24) + .padding(.horizontal, 8) + .padding(.vertical, 0) + .background(Color(.yellow)) + .cornerRadius(20) + } +} diff --git a/Permanent/Common/Constants/Colors.swift b/Permanent/Common/Constants/Colors.swift index ae8b6862..d6433eb2 100644 --- a/Permanent/Common/Constants/Colors.swift +++ b/Permanent/Common/Constants/Colors.swift @@ -70,4 +70,18 @@ extension Color { static var error200 = Color("Error200") static var barneyPurple = Color(.barneyPurple) static var liniarBlue = Color(.liniarBlue) + static var blue900 = Color(.blue900) + static var blue400 = Color(.blue400) + static var yellow = Color(.yellow) +} + +extension Gradient { + static var purpleYellowGradient = LinearGradient(gradient: + Gradient(colors: [ + Color(red: 0.5, green: 0, blue: 0.5), + Color(red: 1, green: 0.6, blue: 0.2) + ]), + startPoint: .topLeading, + endPoint: .bottomTrailing + ) } diff --git a/Permanent/Common/Models/DrawerOption.swift b/Permanent/Common/Models/DrawerOption.swift index 6bf97763..20dba66a 100644 --- a/Permanent/Common/Models/DrawerOption.swift +++ b/Permanent/Common/Models/DrawerOption.swift @@ -19,6 +19,8 @@ enum DrawerOption { case publicGallery case addStorage case giftStorage + case redeemStorage + case storage case accountInfo case security case activityFeed @@ -38,6 +40,8 @@ enum DrawerOption { case .publicGallery: return UIImage(named: "publicGalleryIcon")! case .addStorage: return .storage case .giftStorage: return nil + case .redeemStorage: return nil + case .storage: return nil case .security: return .security case .accountInfo: return .accountInfo case .invitations: return .mail @@ -66,6 +70,8 @@ enum DrawerOption { case .activityFeed: return .activityFeed case .addStorage: return String.addStorage case .giftStorage: return "Gift Storage" + case .redeemStorage: return "Redeem Storage" + case .storage: return "Storage" case .accountInfo: return String.accountInfo case .security: return String.security case .logOut: return .logOut diff --git a/Permanent/Common/ViewModifiers/TextViewModifiers.swift b/Permanent/Common/ViewModifiers/TextViewModifiers.swift index c6e3b597..19b05a2d 100644 --- a/Permanent/Common/ViewModifiers/TextViewModifiers.swift +++ b/Permanent/Common/ViewModifiers/TextViewModifiers.swift @@ -81,6 +81,14 @@ struct SmallXXXRegularTextStyle: ViewModifier { } } +struct SmallXXXSemiBoldTextStyle: ViewModifier { + func body(content: Content) -> some View { + content.font(.custom(FontName.openSansSemiBold.rawValue, + size: FontSize.xxxSmall.rawValue, + relativeTo: .largeTitle)) + } +} + struct SmallBoldTextStyle: ViewModifier { func body(content: Content) -> some View { content.font(.custom(FontName.openSansBold.rawValue, @@ -121,6 +129,14 @@ struct SmallXRegularTextStyle: ViewModifier { } } +struct SmallXSemiBoldTextStyle: ViewModifier { + func body(content: Content) -> some View { + content.font(.custom(FontName.openSansSemiBold.rawValue, + size: FontSize.xSmall.rawValue, + relativeTo: .largeTitle)) + } +} + struct MediumSemiBoldTextStyle: ViewModifier { func body(content: Content) -> some View { content.font(.custom(FontName.openSansSemiBold.rawValue, diff --git a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift index c6831545..498172d8 100644 --- a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift +++ b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift @@ -24,8 +24,7 @@ class RightSideMenuViewController: BaseViewController { private let tableViewData: [RightDrawerSection: [DrawerOption]] = [ RightDrawerSection.rightSideMenu: [ - DrawerOption.addStorage, - DrawerOption.giftStorage, + DrawerOption.storage, DrawerOption.accountInfo, DrawerOption.legacyPlanning, DrawerOption.activityFeed, @@ -248,17 +247,12 @@ extension RightSideMenuViewController: UITableViewDataSource, UITableViewDelegat let newRootVC = UIViewController.create(withIdentifier: .accountSettings, from: .settings) AppDelegate.shared.rootViewController.changeDrawerRoot(viewController: newRootVC) - case .addStorage: - let newRootVC = UIViewController.create(withIdentifier: .donate, from: .donate) - AppDelegate.shared.rootViewController.changeDrawerRoot(viewController: newRootVC) - - case .giftStorage: - let hostingController = UIHostingController(rootView: GiftStorageView(viewModel: StateObject(wrappedValue: GiftStorageViewModel.init(accountData: self.viewModel?.accountData)))) + case .storage: + let hostingController = UIHostingController(rootView: StorageView(viewModel: StateObject(wrappedValue: StorageViewModel.init(accountData: self.viewModel?.accountData)))) hostingController.modalPresentationStyle = .fullScreen self.present(hostingController, animated: true, completion: nil) - // Add a way to call the completion block when the view is dismissed. hostingController.rootView.dismissAction = { hasUpdates in hostingController.dismiss(animated: true, completion: { self.updateAccountInfo() diff --git a/Permanent/Modules/Storage/DonateViewController.swift b/Permanent/Modules/Storage/ViewControllers/DonateViewController.swift similarity index 100% rename from Permanent/Modules/Storage/DonateViewController.swift rename to Permanent/Modules/Storage/ViewControllers/DonateViewController.swift diff --git a/Permanent/Modules/Storage/DonateViewModel.swift b/Permanent/Modules/Storage/ViewModels/DonateViewModel.swift similarity index 100% rename from Permanent/Modules/Storage/DonateViewModel.swift rename to Permanent/Modules/Storage/ViewModels/DonateViewModel.swift diff --git a/Permanent/Modules/GiftStorage/ViewModels/GiftStorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/GiftStorageViewModel.swift similarity index 100% rename from Permanent/Modules/GiftStorage/ViewModels/GiftStorageViewModel.swift rename to Permanent/Modules/Storage/ViewModels/GiftStorageViewModel.swift diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift new file mode 100644 index 00000000..50902ea5 --- /dev/null +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -0,0 +1,39 @@ +// +// StorageViewModel.swift +// Permanent +// +// Created by Lucian Cerbu on 12.12.2023. + +import Foundation + +class StorageViewModel: ObservableObject { + var accountData: AccountVOData? + var spaceRatio = 0.0 + var spaceTotal: Int = 0 + var spaceLeft: Int = 0 + var spaceUsed: Int = 0 + var spaceTotalReadable: String = "" + var spaceLeftReadable: String = "" + var spaceUsedReadable: String = "" + + + init(accountData: AccountVOData?) { + self.accountData = accountData + + getStorageSpaceDetails() + } + + func getStorageSpaceDetails() { + guard let accountData = accountData else { return } + + spaceTotal = (accountData.spaceTotal ?? 0) + spaceLeft = (accountData.spaceLeft ?? 0) + spaceUsed = spaceTotal - spaceLeft + + spaceRatio = Double(spaceUsed) / Double(spaceTotal) + + spaceTotalReadable = spaceTotal.bytesToReadableForm(useDecimal: false) + spaceLeftReadable = spaceLeft.bytesToReadableForm(useDecimal: true) + spaceUsedReadable = spaceUsed.bytesToReadableForm(useDecimal: true) + } +} diff --git a/Permanent/Modules/Storage/Views/AddStorageView.swift b/Permanent/Modules/Storage/Views/AddStorageView.swift new file mode 100644 index 00000000..80049602 --- /dev/null +++ b/Permanent/Modules/Storage/Views/AddStorageView.swift @@ -0,0 +1,57 @@ +// +// AddStorageView.swift +// Permanent +// +// Created by Lucian Cerbu on 13.12.2023. + +import SwiftUI +import UIKit + +struct AddStorageView: View { + @Environment(\.presentationMode) var presentationMode + + var body: some View { + ZStack { + CustomNavigationView { + ZStack { + backgroundView + contentView + } + .ignoresSafeArea(.all) + } leftButton: { + backButton + } rightButton: { + EmptyView() + } + } + } + + var backgroundView: some View { + Color.whiteGray + .frame(maxWidth: .infinity, maxHeight: .infinity) + .edgesIgnoringSafeArea(.all) + } + + var contentView: some View { + VStack { + DonateStorageView() + .ignoresSafeArea() + } + .navigationBarTitle("Add Storage", displayMode: .inline) + } + + var backButton: some View { + Button(action: { + dismissView() + }) { + HStack { + Image(.backArrowNewDesign) + .foregroundColor(.white) + } + } + } + + func dismissView() { + presentationMode.wrappedValue.dismiss() + } +} diff --git a/Permanent/Modules/Storage/Views/DonateStorageView.swift b/Permanent/Modules/Storage/Views/DonateStorageView.swift new file mode 100644 index 00000000..3cf9709e --- /dev/null +++ b/Permanent/Modules/Storage/Views/DonateStorageView.swift @@ -0,0 +1,21 @@ +// +// DonateStorageView.swift +// Permanent +// +// Created by Lucian Cerbu on 14.12.2023. + +import SwiftUI +import UIKit + +struct DonateStorageView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + let vc = DonateViewController() + let storyboard = UIStoryboard(name: "Donate", bundle: Bundle.main) + let controller = storyboard.instantiateViewController(identifier: "donate") + return controller + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + + } +} diff --git a/Permanent/Modules/GiftStorage/Views/GiftStorageView.swift b/Permanent/Modules/Storage/Views/GiftStorageView.swift similarity index 100% rename from Permanent/Modules/GiftStorage/Views/GiftStorageView.swift rename to Permanent/Modules/Storage/Views/GiftStorageView.swift diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift new file mode 100644 index 00000000..7f1a6f95 --- /dev/null +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -0,0 +1,59 @@ +// +// RedeemCodeView.swift +// Permanent +// +// Created by Lucian Cerbu on 13.12.2023. + +import SwiftUI + +struct RedeemCodeView: View { + var accountData: AccountVOData? + @Environment(\.presentationMode) var presentationMode + + var dismissAction: ((Bool) -> Void)? + + var body: some View { + ZStack { + CustomNavigationView { + ZStack { + backgroundView + contentView + } + .ignoresSafeArea(.all) + } leftButton: { + backButton + } rightButton: { + EmptyView() + } + } + } + + var backgroundView: some View { + Color.whiteGray + .frame(maxWidth: .infinity, maxHeight: .infinity) + .edgesIgnoringSafeArea(.all) + } + + var contentView: some View { + VStack { + Text("Redeem View") + } + .navigationBarTitle("Redeem Storage", displayMode: .inline) + } + + var backButton: some View { + Button(action: { + dismissView() + }) { + HStack { + Image(.backArrowNewDesign) + .foregroundColor(.white) + } + } + } + + func dismissView() { + dismissAction?(false) + presentationMode.wrappedValue.dismiss() + } +} diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift new file mode 100644 index 00000000..118db43c --- /dev/null +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -0,0 +1,95 @@ +// +// StorageView.swift +// Permanent +// +// Created by Lucian Cerbu on 12.12.2023. + +import SwiftUI + +struct StorageView: View { + @Environment(\.presentationMode) var presentationMode + @StateObject var viewModel: StorageViewModel + @State var addStorageIsPresented: Bool = false + @State var giftStorageIsPresented: Bool = false + @State var redeemStorageIspresented: Bool = false + + init(viewModel: StateObject) { + self._viewModel = viewModel + } + + var dismissAction: ((Bool) -> Void)? + + var body: some View { + ZStack { + CustomNavigationView { + ZStack { + backgroundView + contentView + } + .ignoresSafeArea(.all) + } leftButton: { + backButton + } rightButton: { + EmptyView() + } + } + .sheet(isPresented: $addStorageIsPresented) { + } content: { + AddStorageView() + } + .sheet(isPresented: $giftStorageIsPresented) { + } content: { + GiftStorageView(viewModel: StateObject(wrappedValue: GiftStorageViewModel(accountData: viewModel.accountData))) + } + .sheet(isPresented: $redeemStorageIspresented) { + } content: { + RedeemCodeView() + } + } + + var backgroundView: some View { + Color.whiteGray + .frame(maxWidth: .infinity, maxHeight: .infinity) + .edgesIgnoringSafeArea(.all) + } + + var contentView: some View { + VStack { + GradientProgressBarView(value: viewModel.spaceUsedReadable, maxValue: viewModel.spaceTotalReadable, sizeRatio: viewModel.spaceRatio) + Button { + addStorageIsPresented = true + } label: { + CustomListItemView(image: Image(.storagePlus), titleText: "Add storage", descText: "Increase your space easily by adding more storage.") + } + Button { + giftStorageIsPresented = true + } label: { + CustomListItemView(image: Image(.storageGift), titleText: "Gift storage", descText: "Share storage with others by gifting it to friends or collaborators.") + } + Button { + redeemStorageIspresented = true + } label: { + CustomListItemView(image: Image(.storageRedeem), titleText: "Redeem code", descText: "Enter codes to unlock special storage benefits just for you.") + } + Spacer() + } + .navigationBarTitle("Storage", displayMode: .inline) + .padding(.top, 10) + } + + var backButton: some View { + Button(action: { + dismissView() + }) { + HStack { + Image(.backArrowNewDesign) + .foregroundColor(.white) + } + } + } + + func dismissView() { + dismissAction?(false) + presentationMode.wrappedValue.dismiss() + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/BarneyPurple.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/BarneyPurple.colorset/Contents.json index 41a14bbd..a160da77 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/BarneyPurple.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/BarneyPurple.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/BilbaoGreen.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/BilbaoGreen.colorset/Contents.json index 55eef2c5..ff552029 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/BilbaoGreen.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/BilbaoGreen.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Black.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Black.colorset/Contents.json index 15925006..95a50e5d 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/Black.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Black.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue400.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue400.colorset/Contents.json new file mode 100644 index 00000000..c86381f0 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue400.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xA4", + "green" : "0x8D", + "red" : "0x89" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue900.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue900.colorset/Contents.json new file mode 100644 index 00000000..47a2e6e3 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue900.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x4A", + "green" : "0x1B", + "red" : "0x13" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/BlueGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/BlueGray.colorset/Contents.json index 76275c3a..259cbf23 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/BlueGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/BlueGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/BrightRed.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/BrightRed.colorset/Contents.json index 89fd1753..b30e6bf3 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/BrightRed.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/BrightRed.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Yellow.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Yellow.colorset/Contents.json new file mode 100644 index 00000000..85a4561d --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Yellow.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x33", + "green" : "0x99", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/Contents.json new file mode 100644 index 00000000..08ae5504 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "storageGift.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/storageGift.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/storageGift.svg new file mode 100644 index 00000000..e51aec24 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageGift.imageset/storageGift.svg @@ -0,0 +1,3 @@ + + + diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/Contents.json new file mode 100644 index 00000000..a6f96183 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "storagePlus.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/storagePlus.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/storagePlus.svg new file mode 100644 index 00000000..2a223cb1 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storagePlus.imageset/storagePlus.svg @@ -0,0 +1,3 @@ + + + diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/Contents.json new file mode 100644 index 00000000..36c03503 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "storageRedeem.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/storageRedeem.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/storageRedeem.svg new file mode 100644 index 00000000..45e264d7 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/StorageMenu/storageRedeem.imageset/storageRedeem.svg @@ -0,0 +1,3 @@ + + + diff --git a/Permanent/Resources/Storyboards/Donate.storyboard b/Permanent/Resources/Storyboards/Donate.storyboard index cd1c570b..f815526b 100644 --- a/Permanent/Resources/Storyboards/Donate.storyboard +++ b/Permanent/Resources/Storyboards/Donate.storyboard @@ -4,6 +4,7 @@ + @@ -38,7 +39,7 @@ - + @@ -239,7 +240,7 @@ - + @@ -269,6 +270,9 @@ + + + From 712d72fb206142afafa75ae53af10d3077315d46 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Fri, 15 Dec 2023 18:35:23 +0200 Subject: [PATCH 02/13] Resolved PR review comments. --- Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift | 3 --- Permanent/Modules/Storage/Views/StorageView.swift | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift b/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift index 1d01b0d8..436a9f26 100644 --- a/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift +++ b/Permanent/Common/Base/SwiftUIViews/CustomListItemView.swift @@ -41,9 +41,6 @@ struct CustomListItemView: View { .padding(.trailing, 10) } .padding(10) - if titleText != "Redeem code" { - Divider() - } } } } diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index 118db43c..89f1f8bc 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -61,11 +61,13 @@ struct StorageView: View { } label: { CustomListItemView(image: Image(.storagePlus), titleText: "Add storage", descText: "Increase your space easily by adding more storage.") } + Divider() Button { giftStorageIsPresented = true } label: { CustomListItemView(image: Image(.storageGift), titleText: "Gift storage", descText: "Share storage with others by gifting it to friends or collaborators.") } + Divider() Button { redeemStorageIspresented = true } label: { From 4b4115751428fc9b67aed96fde13ea48f2540076 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Tue, 19 Dec 2023 16:48:07 +0200 Subject: [PATCH 03/13] Implemented UI for the new Redeem Storage screen. --- Permanent.xcodeproj/project.pbxproj | 32 ++++++++++++- .../{ => ButtonViews}/PullDownButton.swift | 0 .../{ => ButtonViews}/RightButtonView.swift | 0 .../ButtonViews/RoundButtonView.swift | 36 +++++++++++++++ .../RoundStyledTextFieldView.swift | 46 +++++++++++++++++++ Permanent/Common/Constants/Colors.swift | 12 ++++- .../ViewModifiers/TextViewModifiers.swift | 16 +++++++ .../ViewModels/RedeemCodeViewModel.swift | 23 ++++++++++ .../Storage/Views/RedeemCodeView.swift | 34 ++++++++++++-- .../Modules/Storage/Views/StorageView.swift | 2 +- .../Colors/Blue200.colorset/Contents.json | 20 ++++++++ .../Colors/Blue25.colorset/Contents.json | 20 ++++++++ .../Colors/Blue300.colorset/Contents.json | 20 ++++++++ .../Colors/Blue50.colorset/Contents.json | 20 ++++++++ .../Colors/Blue600.colorset/Contents.json | 20 ++++++++ .../Colors/Blue700.colorset/Contents.json | 20 ++++++++ .../Colors/DarkBlue.colorset/Contents.json | 18 -------- .../Colors/DeepRed.colorset/Contents.json | 18 -------- .../Colors/DotGreen.colorset/Contents.json | 18 -------- .../Colors/DoveGray.colorset/Contents.json | 18 -------- .../Colors/DullGreen.colorset/Contents.json | 18 -------- .../Colors/DustyGray.colorset/Contents.json | 18 -------- .../Colors/Error200.colorset/Contents.json | 18 -------- .../Colors/Error25.colorset/Contents.json | 20 ++++++++ .../Colors/Error500.colorset/Contents.json | 20 ++++++++ .../Colors/GalleryGray.colorset/Contents.json | 18 -------- .../IndianSaffron.colorset/Contents.json | 18 -------- .../Colors/LightBlue.colorset/Contents.json | 18 -------- .../Colors/LightGray.colorset/Contents.json | 18 -------- .../Colors/LightPurple.colorset/Contents.json | 18 -------- .../Colors/LightRed.colorset/Contents.json | 18 -------- .../LightWarning.colorset/Contents.json | 18 -------- .../Colors/LiniarBlue.colorset/Contents.json | 18 -------- .../Colors/MainPink.colorset/Contents.json | 18 -------- .../Colors/MainPurple.colorset/Contents.json | 18 -------- .../Colors/MiddleGray.colorset/Contents.json | 18 -------- .../Colors/PaleGreen.colorset/Contents.json | 18 -------- .../Colors/PaleOrange.colorset/Contents.json | 18 -------- .../Colors/PaleRed.colorset/Contents.json | 18 -------- .../Colors/PaleYellow.colorset/Contents.json | 18 -------- .../Colors/Tangerine.colorset/Contents.json | 18 -------- .../Colors/Warning.colorset/Contents.json | 18 -------- .../Colors/White.colorset/Contents.json | 18 -------- .../Colors/WhiteGray.colorset/Contents.json | 18 -------- 44 files changed, 353 insertions(+), 476 deletions(-) rename Permanent/Common/Base/SwiftUIViews/{ => ButtonViews}/PullDownButton.swift (100%) rename Permanent/Common/Base/SwiftUIViews/{ => ButtonViews}/RightButtonView.swift (100%) create mode 100644 Permanent/Common/Base/SwiftUIViews/ButtonViews/RoundButtonView.swift create mode 100644 Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift create mode 100644 Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue200.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue25.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue300.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue50.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue600.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Blue700.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Error25.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Error500.colorset/Contents.json diff --git a/Permanent.xcodeproj/project.pbxproj b/Permanent.xcodeproj/project.pbxproj index d71e8710..4d6699ff 100644 --- a/Permanent.xcodeproj/project.pbxproj +++ b/Permanent.xcodeproj/project.pbxproj @@ -288,6 +288,9 @@ 5EADF80C262EDFCA00D14E9C /* TagsCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADF80B262EDFC900D14E9C /* TagsCollectionViewLayout.swift */; }; 5EB3EDBF2B04D18600D76B83 /* ResponseGiftingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB3EDBE2B04D18600D76B83 /* ResponseGiftingModel.swift */; }; 5EB45A462943403600277800 /* UIDeviceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB45A452943403600277800 /* UIDeviceExtension.swift */; }; + 5EB569A92B305F2300C35543 /* RedeemCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB569A82B305F2300C35543 /* RedeemCodeViewModel.swift */; }; + 5EB569AD2B307F3400C35543 /* RoundButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB569AC2B307F3400C35543 /* RoundButtonView.swift */; }; + 5EB569AF2B31E41B00C35543 /* RoundStyledTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB569AE2B31E41B00C35543 /* RoundStyledTextFieldView.swift */; }; 5EB6201F278445B6001B9AFD /* BlurbProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB6201E278445B6001B9AFD /* BlurbProfileItem.swift */; }; 5EB6202127844A46001B9AFD /* DescriptionProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB6202027844A46001B9AFD /* DescriptionProfileItem.swift */; }; 5EB620252784871B001B9AFD /* EmailProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB620242784871B001B9AFD /* EmailProfileItem.swift */; }; @@ -1104,6 +1107,9 @@ 5EADF80B262EDFC900D14E9C /* TagsCollectionViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsCollectionViewLayout.swift; sourceTree = ""; }; 5EB3EDBE2B04D18600D76B83 /* ResponseGiftingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseGiftingModel.swift; sourceTree = ""; }; 5EB45A452943403600277800 /* UIDeviceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDeviceExtension.swift; sourceTree = ""; }; + 5EB569A82B305F2300C35543 /* RedeemCodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedeemCodeViewModel.swift; sourceTree = ""; }; + 5EB569AC2B307F3400C35543 /* RoundButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundButtonView.swift; sourceTree = ""; }; + 5EB569AE2B31E41B00C35543 /* RoundStyledTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundStyledTextFieldView.swift; sourceTree = ""; }; 5EB6201E278445B6001B9AFD /* BlurbProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurbProfileItem.swift; sourceTree = ""; }; 5EB6202027844A46001B9AFD /* DescriptionProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionProfileItem.swift; sourceTree = ""; }; 5EB620242784871B001B9AFD /* EmailProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailProfileItem.swift; sourceTree = ""; }; @@ -2676,6 +2682,7 @@ 5E633A732B28D7EE004D561C /* StorageViewModel.swift */, 5E181B812AF10100002DE69A /* GiftStorageViewModel.swift */, F57CE595282BE9D000B06D95 /* DonateViewModel.swift */, + 5EB569A82B305F2300C35543 /* RedeemCodeViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -2760,11 +2767,11 @@ 5E991EE02A4AD29F006229C0 /* SwiftUIViews */ = { isa = PBXGroup; children = ( + 5EB569AB2B307DE500C35543 /* ButtonViews */, + 5EB569AA2B307DDB00C35543 /* TextFieldViews */, 92430A592AF2A1F00098597D /* ChipView */, 5E991EF52A53637E006229C0 /* SectionHeaderView.swift */, 5E991EE32A4ADDE3006229C0 /* SectionView.swift */, - 5E991EE12A4AD2F3006229C0 /* RightButtonView.swift */, - 9229A3AA2A7A7773004DE31D /* PullDownButton.swift */, 5E37F2D42AA75248004F00E8 /* CustomDialogView.swift */, 5E181B862AF2EAF5002DE69A /* CustomStepperView.swift */, 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */, @@ -2805,6 +2812,24 @@ path = TextViews; sourceTree = ""; }; + 5EB569AA2B307DDB00C35543 /* TextFieldViews */ = { + isa = PBXGroup; + children = ( + 5EB569AE2B31E41B00C35543 /* RoundStyledTextFieldView.swift */, + ); + path = TextFieldViews; + sourceTree = ""; + }; + 5EB569AB2B307DE500C35543 /* ButtonViews */ = { + isa = PBXGroup; + children = ( + 5E991EE12A4AD2F3006229C0 /* RightButtonView.swift */, + 9229A3AA2A7A7773004DE31D /* PullDownButton.swift */, + 5EB569AC2B307F3400C35543 /* RoundButtonView.swift */, + ); + path = ButtonViews; + sourceTree = ""; + }; 5ECBAF9F2A1B5F0200FACFDF /* LegacyPlanning */ = { isa = PBXGroup; children = ( @@ -4132,6 +4157,7 @@ 5E2CFA25274EEB570055941C /* ProfileItemVO.swift in Sources */, 5E181B852AF14EE8002DE69A /* CustomBarProgressStyle.swift in Sources */, 5E4D0AF428328AC500C6439C /* AccountOnboardingHeaderTableViewCell.swift in Sources */, + 5EB569AD2B307F3400C35543 /* RoundButtonView.swift in Sources */, BC11C80C25595BA3008BDEFA /* RecordVOPayload.swift in Sources */, BCD414E82580BFFE0019548F /* ShareLinkOption.swift in Sources */, 5ECBAFA72A1B731B00FACFDF /* LegacyPlanningArchiveDetails.swift in Sources */, @@ -4265,6 +4291,7 @@ 5EC9C694290B323000AD2E37 /* LoginViewController.swift in Sources */, 5E048FD7292308B10023C929 /* ShareMangementAdditionalOptionCollectionViewCell.swift in Sources */, 5E62F7BA27F5E5910046F6C8 /* PublicGalleryHeaderCollectionViewCell.swift in Sources */, + 5EB569AF2B31E41B00C35543 /* RoundStyledTextFieldView.swift in Sources */, BCFB0C2F25822758001D89E1 /* InputSettingsView.swift in Sources */, BC42EDD925C0192C0031B965 /* InviteViewModel.swift in Sources */, 5E218BF925A86C9E00B56625 /* PasswordElementView.swift in Sources */, @@ -4281,6 +4308,7 @@ 5E181B822AF10100002DE69A /* GiftStorageViewModel.swift in Sources */, 5E9977B0285098FD003E0C46 /* AVCaptureDeviceExtension.swift in Sources */, BCB0726225235DB9003E2F66 /* NavigationBarView.swift in Sources */, + 5EB569A92B305F2300C35543 /* RedeemCodeViewModel.swift in Sources */, BC42ED8825B6EB630031B965 /* FileDownloadInfoVM.swift in Sources */, F5C8D247273204CA00707301 /* SearchVO.swift in Sources */, BC915F50254718FA00C86012 /* PHAssetExtension.swift in Sources */, diff --git a/Permanent/Common/Base/SwiftUIViews/PullDownButton.swift b/Permanent/Common/Base/SwiftUIViews/ButtonViews/PullDownButton.swift similarity index 100% rename from Permanent/Common/Base/SwiftUIViews/PullDownButton.swift rename to Permanent/Common/Base/SwiftUIViews/ButtonViews/PullDownButton.swift diff --git a/Permanent/Common/Base/SwiftUIViews/RightButtonView.swift b/Permanent/Common/Base/SwiftUIViews/ButtonViews/RightButtonView.swift similarity index 100% rename from Permanent/Common/Base/SwiftUIViews/RightButtonView.swift rename to Permanent/Common/Base/SwiftUIViews/ButtonViews/RightButtonView.swift diff --git a/Permanent/Common/Base/SwiftUIViews/ButtonViews/RoundButtonView.swift b/Permanent/Common/Base/SwiftUIViews/ButtonViews/RoundButtonView.swift new file mode 100644 index 00000000..6e1d9dcb --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/ButtonViews/RoundButtonView.swift @@ -0,0 +1,36 @@ +// +// RoundButtonView.swift +// Permanent +// +// Created by Lucian Cerbu on 18.12.2023. + +import SwiftUI + +struct RoundButtonView: View { + var isDisabled: Bool + var isLoading: Bool + let text: String + let action: () -> Void + + var body: some View { + Button(action: action, label: { + ZStack { + Color(isDisabled ? .blue200 : .blue900) + HStack() { + if isLoading { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .frame(width: 10, height: 10) + } else { + Text(text) + .textStyle(RegularSemiBoldTextStyle()) + .foregroundColor(.white) + } + } + } + .frame(height: 56) + .cornerRadius(12) + }) + .disabled(isDisabled || isLoading) + } +} diff --git a/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift b/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift new file mode 100644 index 00000000..e0db9323 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift @@ -0,0 +1,46 @@ +// +// RoundStyledTextFieldView.swift +// Permanent +// +// Created by Lucian Cerbu on 19.12.2023. + +import SwiftUI + +struct RoundStyledTextFieldView: View { + @Binding var text: String + var placeholderText: String + var invalidField: Bool + var doneAction: (() -> Void) + + var body: some View { + ZStack { + Color(.white) + if #available(iOS 16.0, *) { + TextField(placeholderText, text: $text) + .modifier(RegularTextStyle()) + .foregroundColor(Color.darkBlue) + .frame(height: 18) + .autocorrectionDisabled(true) + .autocapitalization(.none) + .padding(.horizontal) + .submitLabel(.done) + .onSubmit(doneAction) + } else { + TextField(placeholderText, text: $text) + .modifier(RegularTextStyle()) + .foregroundColor(Color.darkBlue) + .frame(height: 18) + .autocorrectionDisabled(true) + .autocapitalization(.none) + .padding(.horizontal) + } + } + .frame(minHeight: 48, maxHeight: 48, alignment: .leading) + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .inset(by: 0.5) + .stroke(invalidField ? Color.error200 : Color.blue50) + ) + } +} diff --git a/Permanent/Common/Constants/Colors.swift b/Permanent/Common/Constants/Colors.swift index d6433eb2..a6876dad 100644 --- a/Permanent/Common/Constants/Colors.swift +++ b/Permanent/Common/Constants/Colors.swift @@ -67,11 +67,19 @@ extension Color { static var paleOrange = Color("PaleOrange") static var lightGray = Color("LightGray") static var lightRed = Color("LightRed") - static var error200 = Color("Error200") + static var error25 = Color(.error25) + static var error200 = Color(.error200) + static var error500 = Color(.error500) static var barneyPurple = Color(.barneyPurple) static var liniarBlue = Color(.liniarBlue) - static var blue900 = Color(.blue900) + static var blue25 = Color(.blue25) + static var blue50 = Color(.blue50) + static var blue200 = Color(.blue200) + static var blue300 = Color(.blue300) static var blue400 = Color(.blue400) + static var blue700 = Color(.blue700) + static var blue600 = Color(.blue600) + static var blue900 = Color(.blue900) static var yellow = Color(.yellow) } diff --git a/Permanent/Common/ViewModifiers/TextViewModifiers.swift b/Permanent/Common/ViewModifiers/TextViewModifiers.swift index 19b05a2d..5092c2f9 100644 --- a/Permanent/Common/ViewModifiers/TextViewModifiers.swift +++ b/Permanent/Common/ViewModifiers/TextViewModifiers.swift @@ -144,3 +144,19 @@ struct MediumSemiBoldTextStyle: ViewModifier { relativeTo: .largeTitle)) } } + +struct RegularTextStyle: ViewModifier { + func body(content: Content) -> some View { + content.font(.custom(FontName.openSansRegular.rawValue, + size: FontSize.regular.rawValue, + relativeTo: .largeTitle)) + } +} + +struct RegularSemiBoldTextStyle: ViewModifier { + func body(content: Content) -> some View { + content.font(.custom(FontName.openSansSemiBold.rawValue, + size: FontSize.regular.rawValue, + relativeTo: .largeTitle)) + } +} diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift new file mode 100644 index 00000000..738c6a4f --- /dev/null +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -0,0 +1,23 @@ +// +// RedeemCodeViewModel.swift +// Permanent +// +// Created by Lucian Cerbu on 18.12.2023. + +import SwiftUI + +class RedeemCodeViewModel: ObservableObject { + var accountData: AccountVOData? + @Published var invalidDataInserted: Bool = false + @Published var redeemCode: String + @Published var isLoading: Bool = false + + init(accountData: AccountVOData? = nil) { + self.accountData = accountData + self.redeemCode = "" + } + + func redeemCodeRequest() { + ///To do, API call for redeem code. + } +} diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift index 7f1a6f95..e499413b 100644 --- a/Permanent/Modules/Storage/Views/RedeemCodeView.swift +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -7,8 +7,8 @@ import SwiftUI struct RedeemCodeView: View { - var accountData: AccountVOData? @Environment(\.presentationMode) var presentationMode + @ObservedObject var viewModel: RedeemCodeViewModel var dismissAction: ((Bool) -> Void)? @@ -35,9 +35,37 @@ struct RedeemCodeView: View { } var contentView: some View { - VStack { - Text("Redeem View") + VStack(alignment: .leading, spacing: 8) { + Text("Redeem gift code for free storage") + .textStyle(RegularSemiBoldTextStyle()) + .foregroundColor(.blue900) + Text("If you have a gift code, redeem it for complimentary storage below.") + .textStyle(SmallXRegularTextStyle()) + .foregroundColor(.blue700) + .lineLimit(2) + .multilineTextAlignment(.leading) + if #available(iOS 16.0, *) { + Text("Enter code".uppercased()) + .textStyle(SmallXXXXXRegularTextStyle()) + .foregroundColor(.blue700) + .padding(.top, 16) + .kerning(1.6) + } else { + Text("Enter code".uppercased()) + .textStyle(SmallXXXXXRegularTextStyle()) + .foregroundColor(.blue700) + .padding(.top, 16) } + RoundStyledTextFieldView(text: $viewModel.redeemCode, placeholderText: "Enter redeem code...", invalidField: viewModel.invalidDataInserted) { + viewModel.redeemCodeRequest() + } + RoundButtonView(isDisabled: viewModel.invalidDataInserted, isLoading: viewModel.isLoading, text: "Redeem") { + viewModel.redeemCodeRequest() + } + .padding(.top, 16) + Spacer() + } + .padding() .navigationBarTitle("Redeem Storage", displayMode: .inline) } diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index 89f1f8bc..543c4c58 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -43,7 +43,7 @@ struct StorageView: View { } .sheet(isPresented: $redeemStorageIspresented) { } content: { - RedeemCodeView() + RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData)) } } diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue200.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue200.colorset/Contents.json new file mode 100644 index 00000000..98ed2263 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue200.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xC9", + "green" : "0xBB", + "red" : "0xB8" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue25.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue25.colorset/Contents.json new file mode 100644 index 00000000..9f4b3e8e --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue25.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFD", + "green" : "0xF6", + "red" : "0xF4" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue300.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue300.colorset/Contents.json new file mode 100644 index 00000000..5608ab7d --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue300.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xB7", + "green" : "0xA4", + "red" : "0xA1" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue50.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue50.colorset/Contents.json new file mode 100644 index 00000000..266af644 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue50.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xED", + "green" : "0xE8", + "red" : "0xE7" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue600.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue600.colorset/Contents.json new file mode 100644 index 00000000..ee698222 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue600.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x80", + "green" : "0x5F", + "red" : "0x5A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue700.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue700.colorset/Contents.json new file mode 100644 index 00000000..dc066828 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Blue700.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x6E", + "green" : "0x49", + "red" : "0x42" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DarkBlue.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DarkBlue.colorset/Contents.json index b99a6585..47a2e6e3 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DarkBlue.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DarkBlue.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DeepRed.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DeepRed.colorset/Contents.json index f1e26751..c22c207e 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DeepRed.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DeepRed.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DotGreen.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DotGreen.colorset/Contents.json index b2071c04..413b8df6 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DotGreen.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DotGreen.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DoveGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DoveGray.colorset/Contents.json index fdd40d5c..87051a90 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DoveGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DoveGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DullGreen.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DullGreen.colorset/Contents.json index df494cb7..890950fb 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DullGreen.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DullGreen.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/DustyGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/DustyGray.colorset/Contents.json index 88365047..a4402816 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/DustyGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/DustyGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Error200.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error200.colorset/Contents.json index 39c42615..3eb2b8f8 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/Error200.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error200.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xFF", - "green" : "0xFF", - "red" : "0xFE" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Error25.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error25.colorset/Contents.json new file mode 100644 index 00000000..291429c8 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error25.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFA", + "green" : "0xFB", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Error500.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error500.colorset/Contents.json new file mode 100644 index 00000000..dbfce93d --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Error500.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x38", + "green" : "0x44", + "red" : "0xF0" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/GalleryGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/GalleryGray.colorset/Contents.json index 050d01db..b8c6d9e0 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/GalleryGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/GalleryGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/IndianSaffron.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/IndianSaffron.colorset/Contents.json index 1a0e3257..a7d274e5 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/IndianSaffron.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/IndianSaffron.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightBlue.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightBlue.colorset/Contents.json index 0e8f6a07..c511c4a7 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightBlue.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightBlue.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightGray.colorset/Contents.json index 0e167c13..e5b634c2 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightPurple.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightPurple.colorset/Contents.json index 698ccb01..dc3520aa 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightPurple.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightPurple.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightRed.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightRed.colorset/Contents.json index 5ba01284..dbfce93d 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightRed.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightRed.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightWarning.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightWarning.colorset/Contents.json index 8f062d39..0bb215fe 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LightWarning.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LightWarning.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/LiniarBlue.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/LiniarBlue.colorset/Contents.json index d8354bcc..77954d41 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/LiniarBlue.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/LiniarBlue.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPink.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPink.colorset/Contents.json index 613f17d6..b124d4d0 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPink.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPink.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xFF", - "green" : "0xFF", - "red" : "0xFF" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPurple.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPurple.colorset/Contents.json index 41a14bbd..a160da77 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPurple.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/MainPurple.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/MiddleGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/MiddleGray.colorset/Contents.json index 88365047..a4402816 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/MiddleGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/MiddleGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleGreen.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleGreen.colorset/Contents.json index b75f2944..1fa4d0c3 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleGreen.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleGreen.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleOrange.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleOrange.colorset/Contents.json index 20c6f156..dddc22a2 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleOrange.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleOrange.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleRed.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleRed.colorset/Contents.json index 74c6897b..517a203f 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleRed.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleRed.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleYellow.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleYellow.colorset/Contents.json index 2ed6c401..215ff0e6 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleYellow.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/PaleYellow.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Tangerine.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Tangerine.colorset/Contents.json index 1a0e3257..a7d274e5 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/Tangerine.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Tangerine.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Warning.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Warning.colorset/Contents.json index 5a989d30..3f0c114d 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/Warning.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Warning.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/White.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/White.colorset/Contents.json index c60cea5b..60650a68 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/White.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/White.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/WhiteGray.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/WhiteGray.colorset/Contents.json index c2e49dbd..9f4b3e8e 100644 --- a/Permanent/Resources/Assets/Assets.xcassets/Colors/WhiteGray.colorset/Contents.json +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/WhiteGray.colorset/Contents.json @@ -11,24 +11,6 @@ } }, "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "255", - "green" : "255", - "red" : "255" - } - }, - "idiom" : "universal" } ], "info" : { From 928b475c303354837aa4bd3c891ec603968880bf Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Wed, 20 Dec 2023 11:11:44 +0200 Subject: [PATCH 04/13] Resolved PR review comments. --- .../SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift b/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift index e0db9323..308841ce 100644 --- a/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift +++ b/Permanent/Common/Base/SwiftUIViews/TextFieldViews/RoundStyledTextFieldView.swift @@ -26,7 +26,7 @@ struct RoundStyledTextFieldView: View { .submitLabel(.done) .onSubmit(doneAction) } else { - TextField(placeholderText, text: $text) + TextField(placeholderText, text: $text, onCommit: doneAction) .modifier(RegularTextStyle()) .foregroundColor(Color.darkBlue) .frame(height: 18) From be02fa66390ed2c521a51c4ff7ac8f84f96e9fd4 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Fri, 22 Dec 2023 16:01:47 +0200 Subject: [PATCH 05/13] Added new error notification for wrong redeem code. Added API call for redeem code request. --- Permanent.xcodeproj/project.pbxproj | 20 +++++ .../BottomInvalidAlertMessageView.swift | 46 +++++++++++ .../Common/Helpers/KeyboardResponder.swift | 46 +++++++++++ Permanent/Common/Models/Data/RedeemVO.swift | 32 ++++++++ .../Common/Network/AccountEndpoint.swift | 21 +++++ .../ViewModels/RedeemCodeViewModel.swift | 55 ++++++++++++- .../Storage/Views/RedeemCodeView.swift | 82 +++++++++++++------ .../Images/BottomAlert/Contents.json | 6 ++ .../closeNavigationRed.imageset/Contents.json | 12 +++ .../closeNavigationRed.svg | 3 + .../explanationMarkRed.imageset/Contents.json | 12 +++ .../explanationMarkRed.svg | 3 + 12 files changed, 310 insertions(+), 28 deletions(-) create mode 100644 Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInvalidAlertMessageView.swift create mode 100644 Permanent/Common/Helpers/KeyboardResponder.swift create mode 100644 Permanent/Common/Models/Data/RedeemVO.swift create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/closeNavigationRed.svg create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/explanationMarkRed.svg diff --git a/Permanent.xcodeproj/project.pbxproj b/Permanent.xcodeproj/project.pbxproj index 4d6699ff..efb348cc 100644 --- a/Permanent.xcodeproj/project.pbxproj +++ b/Permanent.xcodeproj/project.pbxproj @@ -281,6 +281,7 @@ 5EA70D8B2B2C78E200175D9C /* NewBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */; }; 5EA70D8D2B2C791100175D9C /* CustomListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8C2B2C791100175D9C /* CustomListItemView.swift */; }; 5EA70D8F2B2C793A00175D9C /* GradientCustomBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA70D8E2B2C793A00175D9C /* GradientCustomBarView.swift */; }; + 5EA94A122B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA94A112B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift */; }; 5EADF803262475D500D14E9C /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADF801262475D500D14E9C /* TagCollectionViewCell.swift */; }; 5EADF804262475D500D14E9C /* TagCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5EADF802262475D500D14E9C /* TagCollectionViewCell.xib */; }; 5EADF8082625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADF8062625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift */; }; @@ -296,6 +297,8 @@ 5EB620252784871B001B9AFD /* EmailProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB620242784871B001B9AFD /* EmailProfileItem.swift */; }; 5EB620272784B01D001B9AFD /* GenderProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB620262784B01D001B9AFD /* GenderProfileItem.swift */; }; 5EB620292784B038001B9AFD /* BirthInfoProfileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB620282784B038001B9AFD /* BirthInfoProfileItem.swift */; }; + 5EB721132B35C9DC0036B28F /* RedeemVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB721122B35C9DC0036B28F /* RedeemVO.swift */; }; + 5EB721152B35CDAB0036B28F /* KeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB721142B35CDAB0036B28F /* KeyboardResponder.swift */; }; 5EBA2B8326009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EBA2B8126009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.swift */; }; 5EBA2B8426009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5EBA2B8226009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.xib */; }; 5EBAFCE42A154606005DB527 /* LegacyPlanningLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EBAFCE32A154606005DB527 /* LegacyPlanningLoadingViewController.swift */; }; @@ -1100,6 +1103,7 @@ 5EA70D8A2B2C78E200175D9C /* NewBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewBadgeView.swift; sourceTree = ""; }; 5EA70D8C2B2C791100175D9C /* CustomListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomListItemView.swift; sourceTree = ""; }; 5EA70D8E2B2C793A00175D9C /* GradientCustomBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientCustomBarView.swift; sourceTree = ""; }; + 5EA94A112B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomInvalidAlertMessageView.swift; sourceTree = ""; }; 5EADF801262475D500D14E9C /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; 5EADF802262475D500D14E9C /* TagCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TagCollectionViewCell.xib; sourceTree = ""; }; 5EADF8062625BCCD00D14E9C /* TagsNamesCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsNamesCollectionViewCell.swift; sourceTree = ""; }; @@ -1115,6 +1119,8 @@ 5EB620242784871B001B9AFD /* EmailProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailProfileItem.swift; sourceTree = ""; }; 5EB620262784B01D001B9AFD /* GenderProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenderProfileItem.swift; sourceTree = ""; }; 5EB620282784B038001B9AFD /* BirthInfoProfileItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BirthInfoProfileItem.swift; sourceTree = ""; }; + 5EB721122B35C9DC0036B28F /* RedeemVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedeemVO.swift; sourceTree = ""; }; + 5EB721142B35CDAB0036B28F /* KeyboardResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardResponder.swift; sourceTree = ""; }; 5EBA2B8126009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDetailsMapViewCellCollectionViewCell.swift; sourceTree = ""; }; 5EBA2B8226009D92005C7B12 /* FileDetailsMapViewCellCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FileDetailsMapViewCellCollectionViewCell.xib; sourceTree = ""; }; 5EBAFCE32A154606005DB527 /* LegacyPlanningLoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyPlanningLoadingViewController.swift; sourceTree = ""; }; @@ -2767,6 +2773,7 @@ 5E991EE02A4AD29F006229C0 /* SwiftUIViews */ = { isa = PBXGroup; children = ( + 5EA94A102B34842300AD67F5 /* AlertsViews */, 5EB569AB2B307DE500C35543 /* ButtonViews */, 5EB569AA2B307DDB00C35543 /* TextFieldViews */, 92430A592AF2A1F00098597D /* ChipView */, @@ -2812,6 +2819,14 @@ path = TextViews; sourceTree = ""; }; + 5EA94A102B34842300AD67F5 /* AlertsViews */ = { + isa = PBXGroup; + children = ( + 5EA94A112B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift */, + ); + path = AlertsViews; + sourceTree = ""; + }; 5EB569AA2B307DDB00C35543 /* TextFieldViews */ = { isa = PBXGroup; children = ( @@ -3218,6 +3233,7 @@ 5ED73B3726143ED5002F9861 /* TagLinkVO.swift */, F5C8D246273204CA00707301 /* SearchVO.swift */, 5E62F7C827F79FF40046F6C8 /* PopularArchive.swift */, + 5EB721122B35C9DC0036B28F /* RedeemVO.swift */, ); path = Data; sourceTree = ""; @@ -3235,6 +3251,7 @@ 5E96F03E279223D90099C0E7 /* NetworkLogger.swift */, 5E744F9027E36EAA00A47D27 /* RCValues.swift */, 5ED3B3B529F7F4B2000CFF48 /* AppEnvironment.swift */, + 5EB721142B35CDAB0036B28F /* KeyboardResponder.swift */, ); path = Helpers; sourceTree = ""; @@ -4283,6 +4300,7 @@ 5E048FCB2923057F0023C929 /* ShareManagementToggleCollectionViewCell.swift in Sources */, 5E1DE53C27E1245000FBD7FA /* UpdateNecessaryViewController.swift in Sources */, BC6AF9B0259342E100483BBA /* Account.swift in Sources */, + 5EB721132B35C9DC0036B28F /* RedeemVO.swift in Sources */, 5E2114922B026CB300944386 /* ViewExtension.swift in Sources */, 5E83CC47286C5F8D00A5775B /* ExtensionUploadManager.swift in Sources */, BC6E70F3252C74B600113D5F /* PermanentLocalAuthentication.swift in Sources */, @@ -4452,6 +4470,7 @@ BC6358A92536FC8A00EEC48C /* FolderSizeVO.swift in Sources */, BC4526ED251CD62C00E24A51 /* MainViewController.swift in Sources */, 5E66731B2A7A4224001C49CC /* SequenceFilenameView.swift in Sources */, + 5EB721152B35CDAB0036B28F /* KeyboardResponder.swift in Sources */, BC59BAB725C2B7D6005A45D3 /* File.swift in Sources */, BC4526F3251CDB7000E24A51 /* UINavigationControllerExtension.swift in Sources */, 5E5EF858273B2416004F7EBC /* PublicProfilePageViewModel.swift in Sources */, @@ -4560,6 +4579,7 @@ BC59BAB425C2B7D6005A45D3 /* SharePreviewViewModelDelegate.swift in Sources */, 5E6FB7582A73E15800984B84 /* MetadataEditFileNamesView.swift in Sources */, 5EDDAE5A2A9E312E00415D9A /* EditDateAndTimeView.swift in Sources */, + 5EA94A122B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift in Sources */, 5ED3B3BC29FAB2BE000CFF48 /* LegacyPlanningSaveButton.swift in Sources */, 5E559EC829BF438200F129BF /* IntExtension.swift in Sources */, 5EF0B6E927F43D62000CBAF6 /* PublicGalleryViewModel.swift in Sources */, diff --git a/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInvalidAlertMessageView.swift b/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInvalidAlertMessageView.swift new file mode 100644 index 00000000..62b03bf5 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInvalidAlertMessageView.swift @@ -0,0 +1,46 @@ +// +// BottomInvalidAlertMessageView.swift +// Permanent +// +// Created by Lucian Cerbu on 21.12.2023. + +import SwiftUI + +struct BottomInvalidAlertMessageView: View { + var alertTextTitle: String + var alertTextDescription: String + var closeAction: (() -> Void) + + var body: some View { + ZStack { + Color(.error25) + HStack(alignment: .top) { + Image(.explanationMarkRed) + VStack(alignment: .leading) { + Text(alertTextTitle) + .textStyle(SmallXRegularTextStyle()) + .foregroundColor(.error500) + Text(alertTextDescription) + .textStyle(SmallXXXRegularTextStyle()) + .foregroundColor(.blue600) + } + Spacer() + Button(action: closeAction) { + Image(.closeNavigationRed) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 16) + } + .cornerRadius(12) + .frame(height: 80) + .shadow(color: Color(red: 0.07, green: 0.11, blue: 0.29).opacity(0.12), radius: 16, x: 0, y: 24) + .overlay( + RoundedRectangle(cornerRadius: 12) + .inset(by: 0.5) + .stroke(Color(.error200), lineWidth: 1) + ) + .padding(.bottom, 24) + } +} + diff --git a/Permanent/Common/Helpers/KeyboardResponder.swift b/Permanent/Common/Helpers/KeyboardResponder.swift new file mode 100644 index 00000000..340fe13f --- /dev/null +++ b/Permanent/Common/Helpers/KeyboardResponder.swift @@ -0,0 +1,46 @@ +// +// KeyboardResponder.swift +// Permanent +// +// Created by Lucian Cerbu on 22.12.2023. + +import Combine +import SwiftUI + +class KeyboardResponder: ObservableObject { + let willset = PassthroughSubject() + private var _center: NotificationCenter + @Published var currentHeight: CGFloat = 0 + var keyboardDuration: TimeInterval = 0 + + init(center: NotificationCenter = .default) { + _center = center + _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) + _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + deinit { + _center.removeObserver(self) + } + + @objc func keyBoardWillShow(notification: Notification) { + if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { + + guard let duration:TimeInterval = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return } + keyboardDuration = duration + + withAnimation(.easeInOut(duration: duration)) { + self.currentHeight = keyboardSize.height + } + + } + } + + @objc func keyBoardWillHide(notification: Notification) { + guard let duration:TimeInterval = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return } + + withAnimation(.easeInOut(duration: duration)) { + currentHeight = 0 + } + } +} diff --git a/Permanent/Common/Models/Data/RedeemVO.swift b/Permanent/Common/Models/Data/RedeemVO.swift new file mode 100644 index 00000000..f372bbe0 --- /dev/null +++ b/Permanent/Common/Models/Data/RedeemVO.swift @@ -0,0 +1,32 @@ +// +// RedeemVO.swift +// Permanent +// +// Created by Lucian Cerbu on 22.12.2023. + +import Foundation + +struct RedeemVO: Codable { + let results: [RedeemResult]? + let isSuccessful: Bool + let actionFailKeys: [JSONAny]? + let isSystemUp: Bool? + let systemMessage: String? + let sessionID: JSONAny? + let createdDT, updatedDT: JSONAny? + + enum CodingKeys: String, CodingKey { + case results = "Results" + case isSuccessful, actionFailKeys, isSystemUp, systemMessage + case sessionID = "sessionId" + case createdDT, updatedDT + } +} + +struct RedeemResult: Codable { + let data: JSONAny? + let message: [String] + let status: Bool? + let resultDT: String? + let createdDT, updatedDT: JSONAny? +} diff --git a/Permanent/Common/Network/AccountEndpoint.swift b/Permanent/Common/Network/AccountEndpoint.swift index 97c3e4ac..6a1d8cf9 100644 --- a/Permanent/Common/Network/AccountEndpoint.swift +++ b/Permanent/Common/Network/AccountEndpoint.swift @@ -38,6 +38,8 @@ enum AccountEndpoint { case updateShareArchiveRequest(archiveVO: MinArchiveVO) /// Revoke Share request case deleteShareRequest(shareId: Int, folderLinkId: Int, archiveId: Int) + /// Redeem code request + case redeemCode(code: String) case getSessionAccount } @@ -66,6 +68,8 @@ extension AccountEndpoint: RequestProtocol { return "/share/delete" case .getSessionAccount: return "/account/getsessionaccount" + case .redeemCode: + return "/promo/entry" } } @@ -109,6 +113,9 @@ extension AccountEndpoint: RequestProtocol { case .getSessionAccount: return getSessionAccount() + + case .redeemCode(code: let code): + return redeemCode(code: code) } } @@ -365,4 +372,18 @@ extension AccountEndpoint { ] ] } + + func redeemCode(code: String) -> RequestParameters { + return [ + "RequestVO": [ + "data": [ + [ + "PromoVO": [ + "code": code + ] + ] + ] + ] + ] + } } diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift index 738c6a4f..afb92437 100644 --- a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -9,8 +9,31 @@ import SwiftUI class RedeemCodeViewModel: ObservableObject { var accountData: AccountVOData? @Published var invalidDataInserted: Bool = false - @Published var redeemCode: String + @Published var isConfirmButtonDisabled: Bool = true + @Published var redeemCode: String { + didSet { + if redeemCode.isEmpty { + if firstTextFieldInput { + invalidDataInserted = false + isConfirmButtonDisabled = true + } else { + invalidDataInserted = true + isConfirmButtonDisabled = true + } + } else { + if redeemCode.count > 0 { + firstTextFieldInput = false + } + isConfirmButtonDisabled = false + invalidDataInserted = false + } + } + } @Published var isLoading: Bool = false + @Published var showAlert: Bool = false + @Published var storageRedeemed: String = "" + @Published var codeRedeemed: Bool = false + var firstTextFieldInput: Bool = true init(accountData: AccountVOData? = nil) { self.accountData = accountData @@ -18,6 +41,34 @@ class RedeemCodeViewModel: ObservableObject { } func redeemCodeRequest() { - ///To do, API call for redeem code. + isLoading = true + let apiOperation = APIOperation(AccountEndpoint.redeemCode(code: redeemCode)) + + apiOperation.execute(in: APIRequestDispatcher()) { [weak self] result in + DispatchQueue.main.async { + self?.isLoading = false + switch result { + case .json(let response, _): + guard + let model: RedeemVO = JSONHelper.convertToModel(from: response), + model.isSuccessful + else { + self?.showAlert = true + self?.invalidDataInserted = true + return + } + + self?.showAlert = false + self?.invalidDataInserted = false + self?.codeRedeemed = true + case .error(_, _): + self?.showAlert = true + self?.invalidDataInserted = true + default: + self?.showAlert = true + self?.invalidDataInserted = true + } + } + } } } diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift index e499413b..d0ff8c6f 100644 --- a/Permanent/Modules/Storage/Views/RedeemCodeView.swift +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -5,10 +5,13 @@ // Created by Lucian Cerbu on 13.12.2023. import SwiftUI +import Combine struct RedeemCodeView: View { @Environment(\.presentationMode) var presentationMode @ObservedObject var viewModel: RedeemCodeViewModel + @State private var showView = false + @ObservedObject var keyboard = KeyboardResponder() var dismissAction: ((Bool) -> Void)? @@ -17,6 +20,9 @@ struct RedeemCodeView: View { CustomNavigationView { ZStack { backgroundView + .onTapGesture { + dismissKeyboard() + } contentView } .ignoresSafeArea(.all) @@ -26,6 +32,14 @@ struct RedeemCodeView: View { EmptyView() } } + .onChange(of: viewModel.showAlert) { showAlert in + withAnimation { + showView = showAlert + } + } + .onChange(of: viewModel.codeRedeemed, perform: { value in + dismissView() + }) } var backgroundView: some View { @@ -35,35 +49,47 @@ struct RedeemCodeView: View { } var contentView: some View { - VStack(alignment: .leading, spacing: 8) { - Text("Redeem gift code for free storage") - .textStyle(RegularSemiBoldTextStyle()) - .foregroundColor(.blue900) - Text("If you have a gift code, redeem it for complimentary storage below.") - .textStyle(SmallXRegularTextStyle()) - .foregroundColor(.blue700) - .lineLimit(2) - .multilineTextAlignment(.leading) - if #available(iOS 16.0, *) { - Text("Enter code".uppercased()) - .textStyle(SmallXXXXXRegularTextStyle()) - .foregroundColor(.blue700) - .padding(.top, 16) - .kerning(1.6) - } else { - Text("Enter code".uppercased()) - .textStyle(SmallXXXXXRegularTextStyle()) - .foregroundColor(.blue700) - .padding(.top, 16) + ZStack(alignment: .bottom) { + if showView { + BottomInvalidAlertMessageView(alertTextTitle: "The code is invalid!", alertTextDescription: "Enter a new code.") { + viewModel.showAlert = false + } + .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .opacity)) + .padding(.bottom, keyboard.currentHeight == .zero ? 16 : keyboard.currentHeight - 10) } - RoundStyledTextFieldView(text: $viewModel.redeemCode, placeholderText: "Enter redeem code...", invalidField: viewModel.invalidDataInserted) { - viewModel.redeemCodeRequest() + VStack(alignment: .leading, spacing: 8) { + Text("Redeem gift code for free storage") + .textStyle(RegularSemiBoldTextStyle()) + .foregroundColor(.blue900) + Text("If you have a gift code, redeem it for complimentary storage below.") + .textStyle(SmallXRegularTextStyle()) + .foregroundColor(.blue700) + .lineLimit(2) + .multilineTextAlignment(.leading) + if #available(iOS 16.0, *) { + Text("Enter code".uppercased()) + .textStyle(SmallXXXXXRegularTextStyle()) + .foregroundColor(.blue700) + .padding(.top, 16) + .kerning(1.6) + } else { + Text("Enter code".uppercased()) + .textStyle(SmallXXXXXRegularTextStyle()) + .foregroundColor(.blue700) + .padding(.top, 16) + } + RoundStyledTextFieldView(text: $viewModel.redeemCode, placeholderText: "Enter redeem code...", invalidField: viewModel.invalidDataInserted) { + viewModel.redeemCodeRequest() + } + RoundButtonView(isDisabled: viewModel.isConfirmButtonDisabled, isLoading: viewModel.isLoading, text: "Redeem") { + viewModel.redeemCodeRequest() + } + .padding(.top, 16) + Spacer() } - RoundButtonView(isDisabled: viewModel.invalidDataInserted, isLoading: viewModel.isLoading, text: "Redeem") { - viewModel.redeemCodeRequest() + .onTapGesture { + dismissKeyboard() } - .padding(.top, 16) - Spacer() } .padding() .navigationBarTitle("Redeem Storage", displayMode: .inline) @@ -84,4 +110,8 @@ struct RedeemCodeView: View { dismissAction?(false) presentationMode.wrappedValue.dismiss() } + + private func dismissKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } } diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/Contents.json new file mode 100644 index 00000000..a025299d --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "closeNavigationRed.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/closeNavigationRed.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/closeNavigationRed.svg new file mode 100644 index 00000000..322bf6cf --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeNavigationRed.imageset/closeNavigationRed.svg @@ -0,0 +1,3 @@ + + + diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/Contents.json new file mode 100644 index 00000000..afda93b4 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "explanationMarkRed.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/explanationMarkRed.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/explanationMarkRed.svg new file mode 100644 index 00000000..9a4b08f0 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/explanationMarkRed.imageset/explanationMarkRed.svg @@ -0,0 +1,3 @@ + + + From c9ba5c7739197ab459d3e21c167154d4745cc051 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Wed, 10 Jan 2024 19:45:56 +0200 Subject: [PATCH 06/13] Added a new notification alert for the amount of storage added by a redeem code. --- Permanent.xcodeproj/project.pbxproj | 4 ++ .../AlertsViews/BottomInfoMessageView.swift | 45 +++++++++++++++ Permanent/Common/Constants/Colors.swift | 3 + Permanent/Common/Models/Data/RedeemVO.swift | 8 ++- .../ViewModels/RedeemCodeViewModel.swift | 5 +- .../Storage/ViewModels/StorageViewModel.swift | 12 +++- .../Storage/Views/RedeemCodeView.swift | 10 ++-- .../Modules/Storage/Views/StorageView.swift | 56 ++++++++++++------- .../Colors/Success200.colorset/Contents.json | 20 +++++++ .../Colors/Success25.colorset/Contents.json | 20 +++++++ .../Colors/Success500.colorset/Contents.json | 20 +++++++ .../checkmarkGreen.imageset/Contents.json | 12 ++++ .../checkmarkGreen.svg | 3 + .../closeGreen.imageset/Contents.json | 12 ++++ .../closeGreen.imageset/closeGreen.svg | 3 + 15 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInfoMessageView.swift create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Success200.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Success25.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Colors/Success500.colorset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/checkmarkGreen.svg create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/Contents.json create mode 100644 Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/closeGreen.svg diff --git a/Permanent.xcodeproj/project.pbxproj b/Permanent.xcodeproj/project.pbxproj index efb348cc..52245b09 100644 --- a/Permanent.xcodeproj/project.pbxproj +++ b/Permanent.xcodeproj/project.pbxproj @@ -198,6 +198,7 @@ 5E5EF858273B2416004F7EBC /* PublicProfilePageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5EF857273B2416004F7EBC /* PublicProfilePageViewModel.swift */; }; 5E60451D251206F9002E47CB /* OnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E60451C251206F9002E47CB /* OnboardingViewModel.swift */; }; 5E60451E25120826002E47CB /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0EB2424E67B6A003D90C6 /* SignUpViewController.swift */; }; + 5E604E552B4EADD8008C5034 /* BottomInfoMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E604E542B4EADD8008C5034 /* BottomInfoMessageView.swift */; }; 5E611E33261C681700B17491 /* TagDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E611E32261C681700B17491 /* TagDetailsViewController.swift */; }; 5E624DBF29475A34002D6ECB /* FusionAuthRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E624DBE29475A34002D6ECB /* FusionAuthRepository.swift */; }; 5E624DC32947BECE002D6ECB /* FusionLoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E624DC22947BECE002D6ECB /* FusionLoginResponse.swift */; }; @@ -1026,6 +1027,7 @@ 5E5EF855273B23C3004F7EBC /* PublicProfilePageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicProfilePageViewController.swift; sourceTree = ""; }; 5E5EF857273B2416004F7EBC /* PublicProfilePageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicProfilePageViewModel.swift; sourceTree = ""; }; 5E60451C251206F9002E47CB /* OnboardingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingViewModel.swift; sourceTree = ""; }; + 5E604E542B4EADD8008C5034 /* BottomInfoMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomInfoMessageView.swift; sourceTree = ""; }; 5E611E32261C681700B17491 /* TagDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagDetailsViewController.swift; sourceTree = ""; }; 5E624DBE29475A34002D6ECB /* FusionAuthRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FusionAuthRepository.swift; sourceTree = ""; }; 5E624DC22947BECE002D6ECB /* FusionLoginResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FusionLoginResponse.swift; sourceTree = ""; }; @@ -2823,6 +2825,7 @@ isa = PBXGroup; children = ( 5EA94A112B3486B000AD67F5 /* BottomInvalidAlertMessageView.swift */, + 5E604E542B4EADD8008C5034 /* BottomInfoMessageView.swift */, ); path = AlertsViews; sourceTree = ""; @@ -4540,6 +4543,7 @@ BC6D3B532514F0C100390927 /* APIOperation.swift in Sources */, BC648782251B9FC7009D7DE1 /* StoryboardName.swift in Sources */, 5EA70D8B2B2C78E200175D9C /* NewBadgeView.swift in Sources */, + 5E604E552B4EADD8008C5034 /* BottomInfoMessageView.swift in Sources */, 5E181B872AF2EAF5002DE69A /* CustomStepperView.swift in Sources */, 5E278898282D423F007EE374 /* AccountOnboardingPageOneWithPendingArchives.swift in Sources */, 5E0E3C302886EB69001C7F10 /* ShareExtensionViewModel.swift in Sources */, diff --git a/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInfoMessageView.swift b/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInfoMessageView.swift new file mode 100644 index 00000000..753af026 --- /dev/null +++ b/Permanent/Common/Base/SwiftUIViews/AlertsViews/BottomInfoMessageView.swift @@ -0,0 +1,45 @@ +// +// BottomInfoMessageView.swift +// Permanent +// +// Created by Lucian Cerbu on 10.01.2024. + +import SwiftUI + +struct BottomInfoMessageView: View { + var alertTextTitle: String + var alertTextDescription: String + var closeAction: (() -> Void) + + var body: some View { + ZStack { + Color(.success25) + HStack(alignment: .top) { + Image(.checkmarkGreen) + VStack(alignment: .leading) { + Text(alertTextTitle) + .textStyle(SmallXRegularTextStyle()) + .foregroundColor(.blue900) + Text(alertTextDescription) + .textStyle(SmallXXXRegularTextStyle()) + .foregroundColor(.blue600) + } + Spacer() + Button(action: closeAction) { + Image(.closeGreen) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 16) + } + .cornerRadius(12) + .frame(height: 80) + .shadow(color: Color(red: 0.07, green: 0.11, blue: 0.29).opacity(0.12), radius: 16, x: 0, y: 24) + .overlay( + RoundedRectangle(cornerRadius: 12) + .inset(by: 0.5) + .stroke(Color(.success200), lineWidth: 1) + ) + .padding(.bottom, 24) + } +} diff --git a/Permanent/Common/Constants/Colors.swift b/Permanent/Common/Constants/Colors.swift index a6876dad..b007500e 100644 --- a/Permanent/Common/Constants/Colors.swift +++ b/Permanent/Common/Constants/Colors.swift @@ -81,6 +81,9 @@ extension Color { static var blue600 = Color(.blue600) static var blue900 = Color(.blue900) static var yellow = Color(.yellow) + static var success25 = Color(.success25) + static var success200 = Color(.success200) + static var success500 = Color(.success500) } extension Gradient { diff --git a/Permanent/Common/Models/Data/RedeemVO.swift b/Permanent/Common/Models/Data/RedeemVO.swift index f372bbe0..b02aea8b 100644 --- a/Permanent/Common/Models/Data/RedeemVO.swift +++ b/Permanent/Common/Models/Data/RedeemVO.swift @@ -6,7 +6,7 @@ import Foundation -struct RedeemVO: Codable { +struct RedeemVO: Model { let results: [RedeemResult]? let isSuccessful: Bool let actionFailKeys: [JSONAny]? @@ -24,9 +24,13 @@ struct RedeemVO: Codable { } struct RedeemResult: Codable { - let data: JSONAny? + let data: PromoVO? let message: [String] let status: Bool? let resultDT: String? let createdDT, updatedDT: JSONAny? } + +struct PromoVO: Codable { + let code: String +} diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift index afb92437..99cdb2d5 100644 --- a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -33,6 +33,7 @@ class RedeemCodeViewModel: ObservableObject { @Published var showAlert: Bool = false @Published var storageRedeemed: String = "" @Published var codeRedeemed: Bool = false + @Published var storageRedeemedResponse: String = "" var firstTextFieldInput: Bool = true init(accountData: AccountVOData? = nil) { @@ -43,14 +44,14 @@ class RedeemCodeViewModel: ObservableObject { func redeemCodeRequest() { isLoading = true let apiOperation = APIOperation(AccountEndpoint.redeemCode(code: redeemCode)) - apiOperation.execute(in: APIRequestDispatcher()) { [weak self] result in DispatchQueue.main.async { self?.isLoading = false switch result { case .json(let response, _): guard - let model: RedeemVO = JSONHelper.convertToModel(from: response), + // model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder) + let model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder), model.isSuccessful else { self?.showAlert = true diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift index 50902ea5..2d20dceb 100644 --- a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -15,7 +15,17 @@ class StorageViewModel: ObservableObject { var spaceTotalReadable: String = "" var spaceLeftReadable: String = "" var spaceUsedReadable: String = "" - + @Published var showRedeemNotif: Bool = false + @Published var reddemAmmountConverted: String = "" + @Published var redeemAmmountString: String = "" { + didSet { + if let reddemAmmountInt = Int(redeemAmmountString), + reddemAmmountInt > 0 { + reddemAmmountConverted = (reddemAmmountInt * 1024 * 1024).bytesToReadableForm(useDecimal: false) + showRedeemNotif = true + } + } + } init(accountData: AccountVOData?) { self.accountData = accountData diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift index d0ff8c6f..39858665 100644 --- a/Permanent/Modules/Storage/Views/RedeemCodeView.swift +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -10,10 +10,10 @@ import Combine struct RedeemCodeView: View { @Environment(\.presentationMode) var presentationMode @ObservedObject var viewModel: RedeemCodeViewModel - @State private var showView = false + @State private var showInvalidAlertView = false @ObservedObject var keyboard = KeyboardResponder() - var dismissAction: ((Bool) -> Void)? + var dismissAction: ((String) -> Void)? var body: some View { ZStack { @@ -34,7 +34,7 @@ struct RedeemCodeView: View { } .onChange(of: viewModel.showAlert) { showAlert in withAnimation { - showView = showAlert + showInvalidAlertView = showAlert } } .onChange(of: viewModel.codeRedeemed, perform: { value in @@ -50,7 +50,7 @@ struct RedeemCodeView: View { var contentView: some View { ZStack(alignment: .bottom) { - if showView { + if showInvalidAlertView { BottomInvalidAlertMessageView(alertTextTitle: "The code is invalid!", alertTextDescription: "Enter a new code.") { viewModel.showAlert = false } @@ -107,7 +107,7 @@ struct RedeemCodeView: View { } func dismissView() { - dismissAction?(false) + dismissAction?(viewModel.storageRedeemedResponse) presentationMode.wrappedValue.dismiss() } diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index 543c4c58..c4f9a025 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -12,6 +12,7 @@ struct StorageView: View { @State var addStorageIsPresented: Bool = false @State var giftStorageIsPresented: Bool = false @State var redeemStorageIspresented: Bool = false + @State private var showRedeemNotifView = false init(viewModel: StateObject) { self._viewModel = viewModel @@ -33,6 +34,11 @@ struct StorageView: View { EmptyView() } } + .onChange(of: viewModel.showRedeemNotif) { showNotif in + withAnimation { + showRedeemNotifView = showNotif + } + } .sheet(isPresented: $addStorageIsPresented) { } content: { AddStorageView() @@ -43,7 +49,9 @@ struct StorageView: View { } .sheet(isPresented: $redeemStorageIspresented) { } content: { - RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData)) + RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData)) { ammount in + viewModel.redeemAmmountString = ammount + } } } @@ -54,26 +62,36 @@ struct StorageView: View { } var contentView: some View { - VStack { - GradientProgressBarView(value: viewModel.spaceUsedReadable, maxValue: viewModel.spaceTotalReadable, sizeRatio: viewModel.spaceRatio) - Button { - addStorageIsPresented = true - } label: { - CustomListItemView(image: Image(.storagePlus), titleText: "Add storage", descText: "Increase your space easily by adding more storage.") - } - Divider() - Button { - giftStorageIsPresented = true - } label: { - CustomListItemView(image: Image(.storageGift), titleText: "Gift storage", descText: "Share storage with others by gifting it to friends or collaborators.") + ZStack(alignment: .bottom) { + if showRedeemNotifView { + BottomInfoMessageView(alertTextTitle: "Gift code redeemed!", alertTextDescription: "\(viewModel.reddemAmmountConverted) of storage") { + viewModel.showRedeemNotif = false + } + .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .opacity)) + .padding(.bottom, 10) + .padding(.horizontal) } - Divider() - Button { - redeemStorageIspresented = true - } label: { - CustomListItemView(image: Image(.storageRedeem), titleText: "Redeem code", descText: "Enter codes to unlock special storage benefits just for you.") + VStack { + GradientProgressBarView(value: viewModel.spaceUsedReadable, maxValue: viewModel.spaceTotalReadable, sizeRatio: viewModel.spaceRatio) + Button { + addStorageIsPresented = true + } label: { + CustomListItemView(image: Image(.storagePlus), titleText: "Add storage", descText: "Increase your space easily by adding more storage.") + } + Divider() + Button { + giftStorageIsPresented = true + } label: { + CustomListItemView(image: Image(.storageGift), titleText: "Gift storage", descText: "Share storage with others by gifting it to friends or collaborators.") + } + Divider() + Button { + redeemStorageIspresented = true + } label: { + CustomListItemView(image: Image(.storageRedeem), titleText: "Redeem code", descText: "Enter codes to unlock special storage benefits just for you.") + } + Spacer() } - Spacer() } .navigationBarTitle("Storage", displayMode: .inline) .padding(.top, 10) diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Success200.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success200.colorset/Contents.json new file mode 100644 index 00000000..2f50bf01 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success200.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xC5", + "green" : "0xF4", + "red" : "0xA6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Success25.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success25.colorset/Contents.json new file mode 100644 index 00000000..d3ac934a --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success25.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF9", + "green" : "0xFE", + "red" : "0xF6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Colors/Success500.colorset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success500.colorset/Contents.json new file mode 100644 index 00000000..25eeb389 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Colors/Success500.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x6A", + "green" : "0xB7", + "red" : "0x12" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/Contents.json new file mode 100644 index 00000000..045b55fe --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checkmarkGreen.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/checkmarkGreen.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/checkmarkGreen.svg new file mode 100644 index 00000000..e5682c17 --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/checkmarkGreen.imageset/checkmarkGreen.svg @@ -0,0 +1,3 @@ + + + diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/Contents.json b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/Contents.json new file mode 100644 index 00000000..4d9106ee --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "closeGreen.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/closeGreen.svg b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/closeGreen.svg new file mode 100644 index 00000000..59d0282a --- /dev/null +++ b/Permanent/Resources/Assets/Assets.xcassets/Images/BottomAlert/closeGreen.imageset/closeGreen.svg @@ -0,0 +1,3 @@ + + + From c0365f1ed0a17530cc19adf0fddf39b38bb829f4 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Wed, 10 Jan 2024 20:08:57 +0200 Subject: [PATCH 07/13] Resolved a problem where the storage redeemed was not reported by the viewModel. --- Permanent/Common/Models/Data/RedeemVO.swift | 23 +++---------------- .../ViewModels/RedeemCodeViewModel.swift | 10 +++++--- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/Permanent/Common/Models/Data/RedeemVO.swift b/Permanent/Common/Models/Data/RedeemVO.swift index b02aea8b..5b04cd51 100644 --- a/Permanent/Common/Models/Data/RedeemVO.swift +++ b/Permanent/Common/Models/Data/RedeemVO.swift @@ -7,30 +7,13 @@ import Foundation struct RedeemVO: Model { - let results: [RedeemResult]? - let isSuccessful: Bool - let actionFailKeys: [JSONAny]? - let isSystemUp: Bool? - let systemMessage: String? - let sessionID: JSONAny? - let createdDT, updatedDT: JSONAny? + let promoVO: PromoVOdata? enum CodingKeys: String, CodingKey { - case results = "Results" - case isSuccessful, actionFailKeys, isSystemUp, systemMessage - case sessionID = "sessionId" - case createdDT, updatedDT + case promoVO = "PromoVO" } } -struct RedeemResult: Codable { - let data: PromoVO? - let message: [String] - let status: Bool? - let resultDT: String? - let createdDT, updatedDT: JSONAny? -} - -struct PromoVO: Codable { +struct PromoVOdata: Codable { let code: String } diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift index 99cdb2d5..63882da5 100644 --- a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -49,19 +49,23 @@ class RedeemCodeViewModel: ObservableObject { self?.isLoading = false switch result { case .json(let response, _): - guard - // model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder) + guard let model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder), - model.isSuccessful + let data = model.results.first?.data?.first, + let amountRedeemed = data.promoVO?.code, + model.isSuccessful else { self?.showAlert = true self?.invalidDataInserted = true return } + self?.storageRedeemed = amountRedeemed self?.showAlert = false self?.invalidDataInserted = false self?.codeRedeemed = true + return + case .error(_, _): self?.showAlert = true self?.invalidDataInserted = true From eb0b85dfb6dbe49b7248f1ae43089f63ab5fe586 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Mon, 15 Jan 2024 18:18:20 +0200 Subject: [PATCH 08/13] Added updates to work with current code redeem API response. --- Permanent/Common/Models/Data/RedeemVO.swift | 9 ++++++++- .../Storage/ViewModels/RedeemCodeViewModel.swift | 5 ++--- .../Storage/ViewModels/StorageViewModel.swift | 16 +++++++++++----- .../Modules/Storage/Views/RedeemCodeView.swift | 4 ++-- .../Modules/Storage/Views/StorageView.swift | 4 ++-- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Permanent/Common/Models/Data/RedeemVO.swift b/Permanent/Common/Models/Data/RedeemVO.swift index 5b04cd51..798e4fa6 100644 --- a/Permanent/Common/Models/Data/RedeemVO.swift +++ b/Permanent/Common/Models/Data/RedeemVO.swift @@ -15,5 +15,12 @@ struct RedeemVO: Model { } struct PromoVOdata: Codable { - let code: String + let promoId: Int? + let code: String? + let sizeInMB: Int? + let expiresDT: String? + let remainingUses: Int? + let status: String? + let type: String? + let createdDT, updatedDT: String? } diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift index 63882da5..65d57438 100644 --- a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -31,9 +31,8 @@ class RedeemCodeViewModel: ObservableObject { } @Published var isLoading: Bool = false @Published var showAlert: Bool = false - @Published var storageRedeemed: String = "" + @Published var storageRedeemed: Int = 0 @Published var codeRedeemed: Bool = false - @Published var storageRedeemedResponse: String = "" var firstTextFieldInput: Bool = true init(accountData: AccountVOData? = nil) { @@ -52,7 +51,7 @@ class RedeemCodeViewModel: ObservableObject { guard let model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder), let data = model.results.first?.data?.first, - let amountRedeemed = data.promoVO?.code, + let amountRedeemed = data.promoVO?.sizeInMB, model.isSuccessful else { self?.showAlert = true diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift index 2d20dceb..86005bb8 100644 --- a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -16,12 +16,13 @@ class StorageViewModel: ObservableObject { var spaceLeftReadable: String = "" var spaceUsedReadable: String = "" @Published var showRedeemNotif: Bool = false - @Published var reddemAmmountConverted: String = "" - @Published var redeemAmmountString: String = "" { + @Published var redeemAmmountConverted: String = "" + @Published var redeemAmmountInt: Int = 0 { didSet { - if let reddemAmmountInt = Int(redeemAmmountString), - reddemAmmountInt > 0 { - reddemAmmountConverted = (reddemAmmountInt * 1024 * 1024).bytesToReadableForm(useDecimal: false) + let ammountAdded = redeemAmmountInt * 1024 * 1024 + if redeemAmmountInt > 0 { + redeemAmmountConverted = ammountAdded.bytesToReadableForm(useDecimal: false) + addInTotalSpace(spaceToAdd: ammountAdded) showRedeemNotif = true } } @@ -46,4 +47,9 @@ class StorageViewModel: ObservableObject { spaceLeftReadable = spaceLeft.bytesToReadableForm(useDecimal: true) spaceUsedReadable = spaceUsed.bytesToReadableForm(useDecimal: true) } + + func addInTotalSpace(spaceToAdd: Int) { + spaceTotal = spaceTotal + spaceToAdd + spaceTotalReadable = spaceTotal.bytesToReadableForm(useDecimal: false) + } } diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift index 39858665..dcb426d2 100644 --- a/Permanent/Modules/Storage/Views/RedeemCodeView.swift +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -13,7 +13,7 @@ struct RedeemCodeView: View { @State private var showInvalidAlertView = false @ObservedObject var keyboard = KeyboardResponder() - var dismissAction: ((String) -> Void)? + var dismissAction: ((Int) -> Void)? var body: some View { ZStack { @@ -107,7 +107,7 @@ struct RedeemCodeView: View { } func dismissView() { - dismissAction?(viewModel.storageRedeemedResponse) + dismissAction?(viewModel.storageRedeemed) presentationMode.wrappedValue.dismiss() } diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index c4f9a025..418abe53 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -50,7 +50,7 @@ struct StorageView: View { .sheet(isPresented: $redeemStorageIspresented) { } content: { RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData)) { ammount in - viewModel.redeemAmmountString = ammount + viewModel.redeemAmmountInt = ammount } } } @@ -64,7 +64,7 @@ struct StorageView: View { var contentView: some View { ZStack(alignment: .bottom) { if showRedeemNotifView { - BottomInfoMessageView(alertTextTitle: "Gift code redeemed!", alertTextDescription: "\(viewModel.reddemAmmountConverted) of storage") { + BottomInfoMessageView(alertTextTitle: "Gift code redeemed!", alertTextDescription: "\(viewModel.redeemAmmountConverted) of storage") { viewModel.showRedeemNotif = false } .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .opacity)) From 2a9c37af8cb795a7014c1bab7d62b5e8190abfa6 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Fri, 19 Jan 2024 15:22:11 +0200 Subject: [PATCH 09/13] Added deep link mechanism for redeem code link received from email. --- Permanent/App/AppDelegate.swift | 29 ++++++-- .../SwiftUIViews/GradientCustomBarView.swift | 10 +-- .../RightSideMenuViewController.swift | 2 +- .../ViewModels/RedeemCodeViewModel.swift | 11 ++- .../Storage/ViewModels/StorageViewModel.swift | 71 ++++++++++++++++--- .../Modules/Storage/Views/StorageView.swift | 23 +++--- 6 files changed, 112 insertions(+), 34 deletions(-) diff --git a/Permanent/App/AppDelegate.swift b/Permanent/App/AppDelegate.swift index b4e60cfe..513c966e 100644 --- a/Permanent/App/AppDelegate.swift +++ b/Permanent/App/AppDelegate.swift @@ -11,6 +11,8 @@ import UIKit import GooglePlaces import GoogleMaps import StripeApplePay +import SwiftUI +import KeychainSwift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -47,11 +49,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { switch url.pathComponents[1] { case "share": - if rootViewController.isDrawerRootActive { - return navigateFromUniversalLink(url: url) + if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), + let redeemCode = components.queryItems?.first(where: { $0.name == "promoCode" })?.value { + if let authData = KeychainSwift().getData(SessionKeychainHandler.keychainAuthDataKey), + let session = try? JSONDecoder().decode(PermSession.self, from: authData), + let archiveId = session.selectedArchive?.archiveID, + archiveId != .zero { + let storageViewModel = StateObject(wrappedValue: StorageViewModel(reddemCode: redeemCode)) + let storageView = StorageView(viewModel: storageViewModel) + + let host = UIHostingController(rootView: storageView) + self.window?.rootViewController?.present(host, animated: true, completion: nil) + + return true + } else { + + return false + } } else { - saveUnivesalLinkToken(url.lastPathComponent) - return false + if rootViewController.isDrawerRootActive { + return navigateFromUniversalLink(url: url) + } else { + saveUnivesalLinkToken(url.lastPathComponent) + return false + } } case "p": diff --git a/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift index facf6ee7..fd6ab33e 100644 --- a/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift +++ b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift @@ -18,9 +18,11 @@ struct GradientProgressBarView: View { Text("\(value) ") .textStyle(SmallXXXSemiBoldTextStyle()) .foregroundColor(.white) - Text("used") - .textStyle(SmallXXXRegularTextStyle()) - .foregroundColor(.white) + if value.isNotEmpty { + Text("used") + .textStyle(SmallXXXRegularTextStyle()) + .foregroundColor(.white) + } } Spacer() Text("\(maxValue)") @@ -29,7 +31,7 @@ struct GradientProgressBarView: View { } .padding(.horizontal) - ProgressView(value: sizeRatio) + ProgressView(value: sizeRatio, total: 1.0) .progressViewStyle(CustomBarProgressStyle(color: .white, height: 8, cornerRadius: 3)) .frame(height: 12) .padding(.horizontal) diff --git a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift index 498172d8..fe7289dc 100644 --- a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift +++ b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift @@ -248,7 +248,7 @@ extension RightSideMenuViewController: UITableViewDataSource, UITableViewDelegat AppDelegate.shared.rootViewController.changeDrawerRoot(viewController: newRootVC) case .storage: - let hostingController = UIHostingController(rootView: StorageView(viewModel: StateObject(wrappedValue: StorageViewModel.init(accountData: self.viewModel?.accountData)))) + let hostingController = UIHostingController(rootView: StorageView(viewModel: StateObject(wrappedValue: StorageViewModel.init()))) hostingController.modalPresentationStyle = .fullScreen self.present(hostingController, animated: true, completion: nil) diff --git a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift index 63882da5..045f3d96 100644 --- a/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/RedeemCodeViewModel.swift @@ -36,9 +36,16 @@ class RedeemCodeViewModel: ObservableObject { @Published var storageRedeemedResponse: String = "" var firstTextFieldInput: Bool = true - init(accountData: AccountVOData? = nil) { + init(accountData: AccountVOData? = nil, redeemCode: String? = nil) { + if let redeemCode = redeemCode { + self.redeemCode = redeemCode + isConfirmButtonDisabled = false + invalidDataInserted = false + } else { + self.redeemCode = "" + } + self.accountData = accountData - self.redeemCode = "" } func redeemCodeRequest() { diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift index 2d20dceb..f3099e3b 100644 --- a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -8,13 +8,22 @@ import Foundation class StorageViewModel: ObservableObject { var accountData: AccountVOData? - var spaceRatio = 0.0 - var spaceTotal: Int = 0 - var spaceLeft: Int = 0 - var spaceUsed: Int = 0 - var spaceTotalReadable: String = "" - var spaceLeftReadable: String = "" - var spaceUsedReadable: String = "" + + @Published var addStorageIsPresented: Bool = false + @Published var giftStorageIsPresented: Bool = false + @Published var redeemStorageIspresented: Bool = false + @Published var showRedeemNotifView = false + + @Published var redeemCodeFromUrl: String? + @Published var spaceRatio = 0.0 + @Published var spaceTotal: Int = 0 + @Published var spaceLeft: Int = 0 + @Published var spaceUsed: Int = 0 + @Published var spaceTotalReadable: String = "" + @Published var spaceLeftReadable: String = "" + @Published var spaceUsedReadable: String = "" + @Published var showError: Bool = false + @Published var showRedeemCodeView: Bool = false @Published var showRedeemNotif: Bool = false @Published var reddemAmmountConverted: String = "" @Published var redeemAmmountString: String = "" { @@ -27,10 +36,52 @@ class StorageViewModel: ObservableObject { } } - init(accountData: AccountVOData?) { - self.accountData = accountData + init(reddemCode: String? = nil) { + self.redeemCodeFromUrl = reddemCode + if reddemCode != nil { + redeemStorageIspresented = true + } - getStorageSpaceDetails() + getAccountInfo { error in + if error != nil { + self.showError = true + } else { + self.getStorageSpaceDetails() + } + } + } + + func getAccountInfo(_ completionBlock: @escaping ((Error?) -> Void) ) { + guard let accountId: Int = AuthenticationManager.shared.session?.account.accountID else { + completionBlock(APIError.unknown) + return + } + + let getUserDataOperation = APIOperation(AccountEndpoint.getUserData(accountId: accountId)) + getUserDataOperation.execute(in: APIRequestDispatcher()) { result in + switch result { + case .json(let response, _): + guard + let model: APIResults = JSONHelper.decoding(from: response, with: APIResults.decoder), + model.isSuccessful + else { + completionBlock(APIError.invalidResponse) + return + } + self.accountData = model.results[0].data?[0].accountVO + completionBlock(nil) + + return + + case .error: + completionBlock(APIError.invalidResponse) + return + + default: + completionBlock(APIError.invalidResponse) + return + } + } } func getStorageSpaceDetails() { diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index c4f9a025..743f3a8e 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -9,10 +9,6 @@ import SwiftUI struct StorageView: View { @Environment(\.presentationMode) var presentationMode @StateObject var viewModel: StorageViewModel - @State var addStorageIsPresented: Bool = false - @State var giftStorageIsPresented: Bool = false - @State var redeemStorageIspresented: Bool = false - @State private var showRedeemNotifView = false init(viewModel: StateObject) { self._viewModel = viewModel @@ -36,21 +32,22 @@ struct StorageView: View { } .onChange(of: viewModel.showRedeemNotif) { showNotif in withAnimation { - showRedeemNotifView = showNotif + viewModel.showRedeemNotifView = showNotif } } - .sheet(isPresented: $addStorageIsPresented) { + .sheet(isPresented: $viewModel.addStorageIsPresented) { } content: { AddStorageView() } - .sheet(isPresented: $giftStorageIsPresented) { + .sheet(isPresented: $viewModel.giftStorageIsPresented) { } content: { GiftStorageView(viewModel: StateObject(wrappedValue: GiftStorageViewModel(accountData: viewModel.accountData))) } - .sheet(isPresented: $redeemStorageIspresented) { + .sheet(isPresented: $viewModel.redeemStorageIspresented) { } content: { - RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData)) { ammount in + RedeemCodeView(viewModel: RedeemCodeViewModel(accountData: viewModel.accountData, redeemCode: viewModel.redeemCodeFromUrl)) { ammount in viewModel.redeemAmmountString = ammount + viewModel.redeemCodeFromUrl = nil } } } @@ -63,7 +60,7 @@ struct StorageView: View { var contentView: some View { ZStack(alignment: .bottom) { - if showRedeemNotifView { + if viewModel.showRedeemNotifView { BottomInfoMessageView(alertTextTitle: "Gift code redeemed!", alertTextDescription: "\(viewModel.reddemAmmountConverted) of storage") { viewModel.showRedeemNotif = false } @@ -74,19 +71,19 @@ struct StorageView: View { VStack { GradientProgressBarView(value: viewModel.spaceUsedReadable, maxValue: viewModel.spaceTotalReadable, sizeRatio: viewModel.spaceRatio) Button { - addStorageIsPresented = true + viewModel.addStorageIsPresented = true } label: { CustomListItemView(image: Image(.storagePlus), titleText: "Add storage", descText: "Increase your space easily by adding more storage.") } Divider() Button { - giftStorageIsPresented = true + viewModel.giftStorageIsPresented = true } label: { CustomListItemView(image: Image(.storageGift), titleText: "Gift storage", descText: "Share storage with others by gifting it to friends or collaborators.") } Divider() Button { - redeemStorageIspresented = true + viewModel.redeemStorageIspresented = true } label: { CustomListItemView(image: Image(.storageRedeem), titleText: "Redeem code", descText: "Enter codes to unlock special storage benefits just for you.") } From 5e1871ed83b9b026a109b8964485a6491f0e75a2 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Fri, 19 Jan 2024 16:50:27 +0200 Subject: [PATCH 10/13] Resolved the bug where the right-side menu values weren't updated after a successfully redeem code was entered. --- .../ViewController/RightSideMenuViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift index ffa1cd68..5d41a212 100644 --- a/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift +++ b/Permanent/Modules/SideMenus/ViewController/RightSideMenuViewController.swift @@ -248,13 +248,12 @@ extension RightSideMenuViewController: UITableViewDataSource, UITableViewDelegat AppDelegate.shared.rootViewController.changeDrawerRoot(viewController: newRootVC) case .storage: - var storageView = StorageView(viewModel: StateObject(wrappedValue: StorageViewModel.init())) - let hostingController = UIHostingController(rootView: storageView) + let hostingController = UIHostingController(rootView: StorageView(viewModel: StateObject(wrappedValue: StorageViewModel.init()))) hostingController.modalPresentationStyle = .fullScreen self.present(hostingController, animated: true, completion: nil) - storageView.dismissAction = { [weak self] hasUpdates in + hostingController.rootView.dismissAction = { [weak self] hasUpdates in hostingController.dismiss(animated: true, completion: { [weak self] in self?.updateAccountInfo() self?.selectedMenuOption = .none @@ -263,6 +262,7 @@ extension RightSideMenuViewController: UITableViewDataSource, UITableViewDelegat }) } + case .activityFeed: let newRootVC = ActivityFeedViewController() newRootVC.viewModel = ActivityFeedViewModel() From 1ffd1b622beae9dc60ab30e0e5cddb48d12eaa02 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Mon, 22 Jan 2024 10:38:51 +0200 Subject: [PATCH 11/13] Implemented remarks from the demo : - Removed "free" and "complimentary" from redeem screen - Round the amount of storage added by one decimal point(in GB) Resolved a bug where the storage progress bar view would not update after a successful redeem code was added. Changed the path for the deep link to correspond with the real one. --- Permanent/App/AppDelegate.swift | 55 +++++++++---------- .../SwiftUIViews/GradientCustomBarView.swift | 33 +++++------ .../Storage/ViewModels/StorageViewModel.swift | 3 +- .../Storage/Views/RedeemCodeView.swift | 6 +- .../Modules/Storage/Views/StorageView.swift | 4 +- 5 files changed, 51 insertions(+), 50 deletions(-) diff --git a/Permanent/App/AppDelegate.swift b/Permanent/App/AppDelegate.swift index 513c966e..ad1c3782 100644 --- a/Permanent/App/AppDelegate.swift +++ b/Permanent/App/AppDelegate.swift @@ -49,30 +49,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { switch url.pathComponents[1] { case "share": - if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), - let redeemCode = components.queryItems?.first(where: { $0.name == "promoCode" })?.value { - if let authData = KeychainSwift().getData(SessionKeychainHandler.keychainAuthDataKey), - let session = try? JSONDecoder().decode(PermSession.self, from: authData), - let archiveId = session.selectedArchive?.archiveID, - archiveId != .zero { - let storageViewModel = StateObject(wrappedValue: StorageViewModel(reddemCode: redeemCode)) - let storageView = StorageView(viewModel: storageViewModel) - - let host = UIHostingController(rootView: storageView) - self.window?.rootViewController?.present(host, animated: true, completion: nil) - - return true - } else { - - return false - } + if rootViewController.isDrawerRootActive { + return navigateFromUniversalLink(url: url) } else { - if rootViewController.isDrawerRootActive { - return navigateFromUniversalLink(url: url) - } else { - saveUnivesalLinkToken(url.lastPathComponent) - return false - } + saveUnivesalLinkToken(url.lastPathComponent) + return false } case "p": @@ -105,15 +86,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } case "app": - if url.pathComponents.count >= 3, url.pathComponents[2] == "pr" && url.pathComponents[3] == "manage" { - if rootViewController.isDrawerRootActive { - return navigateFromSharedArchive() + if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), + let redeemCode = components.queryItems?.first(where: { $0.name == "promoCode" })?.value { + if let authData = KeychainSwift().getData(SessionKeychainHandler.keychainAuthDataKey), + let session = try? JSONDecoder().decode(PermSession.self, from: authData), + let archiveId = session.selectedArchive?.archiveID, + archiveId != .zero { + let storageViewModel = StateObject(wrappedValue: StorageViewModel(reddemCode: redeemCode)) + let storageView = StorageView(viewModel: storageViewModel) + + let host = UIHostingController(rootView: storageView) + self.window?.rootViewController?.present(host, animated: true, completion: nil) + + return true } else { - saveSharedArchiveToken() return false } + } else { + if url.pathComponents.count >= 3, url.pathComponents[2] == "pr" && url.pathComponents[3] == "manage" { + if rootViewController.isDrawerRootActive { + return navigateFromSharedArchive() + } else { + saveSharedArchiveToken() + return false + } + } + return false } - return false default: return false } diff --git a/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift index ca65971d..f2096c00 100644 --- a/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift +++ b/Permanent/Common/Base/SwiftUIViews/GradientCustomBarView.swift @@ -7,12 +7,13 @@ import SwiftUI struct GradientProgressBarView: View { - @Binding var value: String - @Binding var maxValue: String - @Binding var sizeRatio: Double + var value: String + var maxValue: String + var sizeRatio: Double + @State private var redraw = UUID() var body: some View { - VStack(spacing: 16) { + LazyVStack(spacing: 16) { HStack { HStack(spacing: 0) { Text("\(value) ") @@ -30,22 +31,22 @@ struct GradientProgressBarView: View { .foregroundColor(.white) } .padding(.horizontal) - - if sizeRatio != .zero { - ProgressView(value: sizeRatio) - .progressViewStyle(CustomBarProgressStyle(color: .white, height: 8, cornerRadius: 3)) - .frame(height: 12) - .padding(.horizontal) - } else { - ProgressView(value: 0.0) - .progressViewStyle(CustomBarProgressStyle(color: .white, height: 8, cornerRadius: 3)) - .frame(height: 12) - .padding(.horizontal) - } + ProgressView(value: sizeRatio) + .progressViewStyle(CustomBarProgressStyle(color: .white, height: 8, cornerRadius: 3)) + .frame(height: 12) + .padding(.horizontal) + .id(redraw) } + .onChange(of: sizeRatio, perform: { newValue in + updateRedraw() + }) .frame(maxHeight: 72) .background(Gradient.purpleYellowGradient) .cornerRadius(12) .padding(16) } + + func updateRedraw() { + redraw = UUID() + } } diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift index 18abc68b..066f0927 100644 --- a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -30,7 +30,7 @@ class StorageViewModel: ObservableObject { didSet { let ammountAdded = redeemAmmountInt * 1024 * 1024 if redeemAmmountInt > 0 { - redeemAmmountConverted = ammountAdded.bytesToReadableForm(useDecimal: false) + redeemAmmountConverted = ammountAdded.bytesToReadableForm(useDecimal: true) addInTotalSpace(spaceToAdd: ammountAdded) showRedeemNotif = true } @@ -101,6 +101,7 @@ class StorageViewModel: ObservableObject { func addInTotalSpace(spaceToAdd: Int) { spaceTotal = spaceTotal + spaceToAdd + spaceRatio = Double(spaceUsed) / Double(spaceTotal) spaceTotalReadable = spaceTotal.bytesToReadableForm(useDecimal: false) } } diff --git a/Permanent/Modules/Storage/Views/RedeemCodeView.swift b/Permanent/Modules/Storage/Views/RedeemCodeView.swift index dcb426d2..b5d36931 100644 --- a/Permanent/Modules/Storage/Views/RedeemCodeView.swift +++ b/Permanent/Modules/Storage/Views/RedeemCodeView.swift @@ -51,17 +51,17 @@ struct RedeemCodeView: View { var contentView: some View { ZStack(alignment: .bottom) { if showInvalidAlertView { - BottomInvalidAlertMessageView(alertTextTitle: "The code is invalid!", alertTextDescription: "Enter a new code.") { + BottomInvalidAlertMessageView(alertTextTitle: "The code is invalid.", alertTextDescription: "Enter a new code.") { viewModel.showAlert = false } .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .opacity)) .padding(.bottom, keyboard.currentHeight == .zero ? 16 : keyboard.currentHeight - 10) } VStack(alignment: .leading, spacing: 8) { - Text("Redeem gift code for free storage") + Text("Redeem gift code for storage") .textStyle(RegularSemiBoldTextStyle()) .foregroundColor(.blue900) - Text("If you have a gift code, redeem it for complimentary storage below.") + Text("If you have a gift code, redeem it for storage below.") .textStyle(SmallXRegularTextStyle()) .foregroundColor(.blue700) .lineLimit(2) diff --git a/Permanent/Modules/Storage/Views/StorageView.swift b/Permanent/Modules/Storage/Views/StorageView.swift index 9eb159ef..07c0b1f8 100644 --- a/Permanent/Modules/Storage/Views/StorageView.swift +++ b/Permanent/Modules/Storage/Views/StorageView.swift @@ -61,7 +61,7 @@ struct StorageView: View { var contentView: some View { ZStack(alignment: .bottom) { if viewModel.showRedeemNotifView { - BottomInfoMessageView(alertTextTitle: "Gift code redeemed!", alertTextDescription: "\(viewModel.redeemAmmountConverted) of storage") { + BottomInfoMessageView(alertTextTitle: "Gift code redeemed.", alertTextDescription: "\(viewModel.redeemAmmountConverted) of storage") { viewModel.showRedeemNotif = false } .transition(.asymmetric(insertion: .move(edge: .bottom), removal: .opacity)) @@ -69,7 +69,7 @@ struct StorageView: View { .padding(.horizontal) } VStack { - GradientProgressBarView(value: $viewModel.spaceUsedReadable, maxValue: $viewModel.spaceTotalReadable, sizeRatio: $viewModel.spaceRatio) + GradientProgressBarView(value: viewModel.spaceUsedReadable, maxValue: viewModel.spaceTotalReadable, sizeRatio: viewModel.spaceRatio) Button { viewModel.addStorageIsPresented = true } label: { From d47a9874ddbbfb3067862f631467c94059a6ee20 Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Mon, 22 Jan 2024 17:11:22 +0100 Subject: [PATCH 12/13] Resolved PR remarks. --- .../Modules/Storage/ViewModels/StorageViewModel.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift index 066f0927..d1d3db83 100644 --- a/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift +++ b/Permanent/Modules/Storage/ViewModels/StorageViewModel.swift @@ -69,12 +69,11 @@ class StorageViewModel: ObservableObject { completionBlock(APIError.invalidResponse) return } - self.accountData = model.results[0].data?[0].accountVO - completionBlock(nil) - - return - - case .error: + if let accountDataVO = model.results[0].data?[0].accountVO { + self.accountData = accountDataVO + completionBlock(nil) + return + } completionBlock(APIError.invalidResponse) return From a9b76fa68f04fc43ab490bcd2be21234492df48f Mon Sep 17 00:00:00 2001 From: Lucian Cerbu Date: Tue, 23 Jan 2024 19:29:37 +0100 Subject: [PATCH 13/13] Implemented pull request remarks. --- Permanent/App/AppDelegate.swift | 35 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Permanent/App/AppDelegate.swift b/Permanent/App/AppDelegate.swift index ad1c3782..e63b2057 100644 --- a/Permanent/App/AppDelegate.swift +++ b/Permanent/App/AppDelegate.swift @@ -86,24 +86,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } case "app": - if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), - let redeemCode = components.queryItems?.first(where: { $0.name == "promoCode" })?.value { - if let authData = KeychainSwift().getData(SessionKeychainHandler.keychainAuthDataKey), - let session = try? JSONDecoder().decode(PermSession.self, from: authData), - let archiveId = session.selectedArchive?.archiveID, - archiveId != .zero { - let storageViewModel = StateObject(wrappedValue: StorageViewModel(reddemCode: redeemCode)) - let storageView = StorageView(viewModel: storageViewModel) - - let host = UIHostingController(rootView: storageView) - self.window?.rootViewController?.present(host, animated: true, completion: nil) - - return true - } else { - return false + if let components = URLComponents(url: url, resolvingAgainstBaseURL: true) + { + if let redeemCode = components.queryItems?.first(where: { $0.name == "promoCode" })?.value { + if let authData = KeychainSwift().getData(SessionKeychainHandler.keychainAuthDataKey), + let session = try? JSONDecoder().decode(PermSession.self, from: authData), + let archiveId = session.selectedArchive?.archiveID, + archiveId != .zero { + let storageViewModel = StateObject(wrappedValue: StorageViewModel(reddemCode: redeemCode)) + let storageView = StorageView(viewModel: storageViewModel) + + let host = UIHostingController(rootView: storageView) + self.window?.rootViewController?.present(host, animated: true, completion: nil) + + return true + } } - } else { - if url.pathComponents.count >= 3, url.pathComponents[2] == "pr" && url.pathComponents[3] == "manage" { + if components.path == "/app/pr/manage" { if rootViewController.isDrawerRootActive { return navigateFromSharedArchive() } else { @@ -111,8 +110,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return false } } - return false } + return false default: return false }