forked from organicmaps/organicmaps
backup
This commit is contained in:
parent
91c544bfeb
commit
6ea723b0f5
14 changed files with 224 additions and 158 deletions
|
@ -181,7 +181,6 @@
|
|||
3D15ACEE2155117000F725D5 /* MWMObjectsCategorySelectorDataSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D15ACED2155117000F725D5 /* MWMObjectsCategorySelectorDataSource.mm */; };
|
||||
3D2D79BA2C7C508E0062BC3D /* SingleEntityCoreDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79B92C7C508E0062BC3D /* SingleEntityCoreDataController.swift */; };
|
||||
3D2D79BC2C7C5E300062BC3D /* ProfileRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79BB2C7C5E300062BC3D /* ProfileRepositoryImpl.swift */; };
|
||||
3D2D79C12C7C7EA00062BC3D /* PersonalData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79BF2C7C7EA00062BC3D /* PersonalData.xcdatamodeld */; };
|
||||
3D2D79C32C7C80E60062BC3D /* PersonalDataPersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79C22C7C80E60062BC3D /* PersonalDataPersistenceController.swift */; };
|
||||
3D2D79CC2C7C8C350062BC3D /* ProfileService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79CB2C7C8C350062BC3D /* ProfileService.swift */; };
|
||||
3D2D79D32C7CF4F70062BC3D /* PersonalDataViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79D22C7CF4F70062BC3D /* PersonalDataViewController.swift */; };
|
||||
|
@ -191,7 +190,6 @@
|
|||
3D2D79DB2C7D15410062BC3D /* SecondaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79DA2C7D15410062BC3D /* SecondaryButton.swift */; };
|
||||
3D2D79DD2C7DE34B0062BC3D /* PhotoPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2D79DC2C7DE34B0062BC3D /* PhotoPickerView.swift */; };
|
||||
3D585BF42C760850005DF71F /* UIScreenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D585BF32C760850005DF71F /* UIScreenExtensions.swift */; };
|
||||
3D585BFA2C768C3A005DF71F /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D585BF92C768C3A005DF71F /* ToastView.swift */; };
|
||||
3DA3FC992C75ED2A0065E4D6 /* changeTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA3FC982C75ED2A0065E4D6 /* changeTheme.swift */; };
|
||||
3DBD7BE42425015C00ED9FE8 /* ParntersStyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBD7BE32425015C00ED9FE8 /* ParntersStyleSheet.swift */; };
|
||||
3DEE1AEB21F72CD300054A91 /* MWMPowerManagmentViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3DEE1AEA21F72CD300054A91 /* MWMPowerManagmentViewController.mm */; };
|
||||
|
@ -304,7 +302,9 @@
|
|||
527D5E822C60EFEE00736A85 /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 527D5E812C60EFEE00736A85 /* UIViewExtensions.swift */; };
|
||||
528D72A12C5BBBF700D53210 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 528D72A02C5BBBF700D53210 /* Colors.xcassets */; };
|
||||
5292123D2C7359FC007B97E1 /* CountryPickerView in Frameworks */ = {isa = PBXBuildFile; productRef = 5292123C2C7359FC007B97E1 /* CountryPickerView */; };
|
||||
529212422C735A61007B97E1 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 529212412C735A61007B97E1 /* SDWebImageSwiftUI */; };
|
||||
529A5F0A2C858F82004FE4A1 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 529A5F092C858F82004FE4A1 /* SDWebImageSwiftUI */; };
|
||||
529A5F162C8595BB004FE4A1 /* PersonalData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 529A5F112C859535004FE4A1 /* PersonalData.xcdatamodeld */; };
|
||||
529A5F192C85BFF0004FE4A1 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529A5F182C85BFF0004FE4A1 /* ToastView.swift */; };
|
||||
52B573EC2C61E1C10047FAC9 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B573EB2C61E1C10047FAC9 /* SignInViewController.swift */; };
|
||||
52B573F02C61E4110047FAC9 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B573EF2C61E4110047FAC9 /* Constants.swift */; };
|
||||
52B573F22C61E8980047FAC9 /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B573F12C61E8980047FAC9 /* SignUpViewController.swift */; };
|
||||
|
@ -1149,7 +1149,6 @@
|
|||
3D15ACEF2155118800F725D5 /* MWMObjectsCategorySelectorDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMObjectsCategorySelectorDataSource.h; sourceTree = "<group>"; };
|
||||
3D2D79B92C7C508E0062BC3D /* SingleEntityCoreDataController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleEntityCoreDataController.swift; sourceTree = "<group>"; };
|
||||
3D2D79BB2C7C5E300062BC3D /* ProfileRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileRepositoryImpl.swift; sourceTree = "<group>"; };
|
||||
3D2D79C02C7C7EA00062BC3D /* PersonalData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = PersonalData.xcdatamodel; sourceTree = "<group>"; };
|
||||
3D2D79C22C7C80E60062BC3D /* PersonalDataPersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalDataPersistenceController.swift; sourceTree = "<group>"; };
|
||||
3D2D79CB2C7C8C350062BC3D /* ProfileService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileService.swift; sourceTree = "<group>"; };
|
||||
3D2D79D22C7CF4F70062BC3D /* PersonalDataViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalDataViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1159,7 +1158,6 @@
|
|||
3D2D79DA2C7D15410062BC3D /* SecondaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryButton.swift; sourceTree = "<group>"; };
|
||||
3D2D79DC2C7DE34B0062BC3D /* PhotoPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPickerView.swift; sourceTree = "<group>"; };
|
||||
3D585BF32C760850005DF71F /* UIScreenExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreenExtensions.swift; sourceTree = "<group>"; };
|
||||
3D585BF92C768C3A005DF71F /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ToastView.swift; path = ../../../../../../../../Documents/ToastView.swift; sourceTree = "<group>"; };
|
||||
3DA3FC982C75ED2A0065E4D6 /* changeTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = changeTheme.swift; sourceTree = "<group>"; };
|
||||
3DBD7BE32425015C00ED9FE8 /* ParntersStyleSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParntersStyleSheet.swift; sourceTree = "<group>"; };
|
||||
3DEE1AE921F72CD300054A91 /* MWMPowerManagmentViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMPowerManagmentViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -1303,6 +1301,8 @@
|
|||
527D5E7E2C60E69C00736A85 /* Layouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layouting.swift; sourceTree = "<group>"; };
|
||||
527D5E812C60EFEE00736A85 /* UIViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = "<group>"; };
|
||||
528D72A02C5BBBF700D53210 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
|
||||
529A5F122C859535004FE4A1 /* PersonalData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = PersonalData.xcdatamodel; sourceTree = "<group>"; };
|
||||
529A5F182C85BFF0004FE4A1 /* ToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||
52B573EB2C61E1C10047FAC9 /* SignInViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewController.swift; sourceTree = "<group>"; };
|
||||
52B573EF2C61E4110047FAC9 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
52B573F12C61E8980047FAC9 /* SignUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1894,7 +1894,7 @@
|
|||
FA853BDB26BC54CD0026D455 /* libtraffic.a in Frameworks */,
|
||||
FA853BD926BC54C80026D455 /* libexpat.a in Frameworks */,
|
||||
FA853BD726BC54650026D455 /* libpugixml.a in Frameworks */,
|
||||
529212422C735A61007B97E1 /* SDWebImageSwiftUI in Frameworks */,
|
||||
529A5F0A2C858F82004FE4A1 /* SDWebImageSwiftUI in Frameworks */,
|
||||
FA853BD526BC545D0026D455 /* libagg.a in Frameworks */,
|
||||
FA853BD326BC54530026D455 /* libtransit.a in Frameworks */,
|
||||
FA853BA926BC3B8A0026D455 /* libbase.a in Frameworks */,
|
||||
|
@ -2718,14 +2718,6 @@
|
|||
path = Buttons;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3D585BF82C768C2C005DF71F /* Toast */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3D585BF92C768C3A005DF71F /* ToastView.swift */,
|
||||
);
|
||||
path = Toast;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
447DB4B72BA7826D000DF4C2 /* ReauthAlert */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3089,7 +3081,7 @@
|
|||
527D5E762C60D92900736A85 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3D585BF82C768C2C005DF71F /* Toast */,
|
||||
529A5F172C85BF99004FE4A1 /* ToastView */,
|
||||
3D585BF72C768BED005DF71F /* Buttons */,
|
||||
52522F442C6DFD220015709C /* Nav */,
|
||||
52B573F32C61F10B0047FAC9 /* TextFields */,
|
||||
|
@ -3129,6 +3121,14 @@
|
|||
path = Theme;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
529A5F172C85BF99004FE4A1 /* ToastView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
529A5F182C85BFF0004FE4A1 /* ToastView.swift */,
|
||||
);
|
||||
path = ToastView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
52B189972C53B9E900B5B6F9 /* Home */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3213,8 +3213,8 @@
|
|||
52ED91A02C72007C000EE25B /* DataModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
529A5F112C859535004FE4A1 /* PersonalData.xcdatamodeld */,
|
||||
52ED91A12C7200C4000EE25B /* Currency.xcdatamodeld */,
|
||||
3D2D79BF2C7C7EA00062BC3D /* PersonalData.xcdatamodeld */,
|
||||
);
|
||||
path = DataModels;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4373,7 +4373,7 @@
|
|||
name = OMaps;
|
||||
packageProductDependencies = (
|
||||
5292123C2C7359FC007B97E1 /* CountryPickerView */,
|
||||
529212412C735A61007B97E1 /* SDWebImageSwiftUI */,
|
||||
529A5F092C858F82004FE4A1 /* SDWebImageSwiftUI */,
|
||||
);
|
||||
productName = Maps;
|
||||
productReference = 6741AA5D1BF340DE002C974C /* Organic Maps (Debug).app */;
|
||||
|
@ -4473,7 +4473,7 @@
|
|||
mainGroup = 29B97314FDCFA39411CA2CEA /* Maps */;
|
||||
packageReferences = (
|
||||
5292123B2C7359FC007B97E1 /* XCRemoteSwiftPackageReference "CountryPickerView" */,
|
||||
529212402C735A61007B97E1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */,
|
||||
529A5F082C858F82004FE4A1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */,
|
||||
);
|
||||
productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -4738,6 +4738,7 @@
|
|||
34AB665F1FC5AA330078E451 /* TransportTransitIntermediatePoint.swift in Sources */,
|
||||
34B846A82029E8110081ECCD /* BMCDefaultViewModel.swift in Sources */,
|
||||
993DF12123F6BDB100AC231A /* UIViewControllerRenderer.swift in Sources */,
|
||||
529A5F192C85BFF0004FE4A1 /* ToastView.swift in Sources */,
|
||||
34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */,
|
||||
4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */,
|
||||
6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */,
|
||||
|
@ -4833,6 +4834,7 @@
|
|||
ED79A5D42BDF8D6100952D1F /* MetadataItem.swift in Sources */,
|
||||
34AB667D1FC5AA330078E451 /* MWMRoutePreview.mm in Sources */,
|
||||
993DF11B23F6BDB100AC231A /* UIViewRenderer.swift in Sources */,
|
||||
529A5F162C8595BB004FE4A1 /* PersonalData.xcdatamodeld in Sources */,
|
||||
99C964302428C27A00E41723 /* PlacePageHeaderView.swift in Sources */,
|
||||
9989273A2449E60200260CE2 /* BottomMenuViewController.swift in Sources */,
|
||||
47E3C72D2111E6A2008B3B27 /* FadeTransitioning.swift in Sources */,
|
||||
|
@ -4917,7 +4919,6 @@
|
|||
990128562449A82500C72B10 /* BottomTabBarView.swift in Sources */,
|
||||
F6E2FD711E097BA00083EBEC /* MWMMapDownloaderTableViewCell.m in Sources */,
|
||||
F6E2FE4F1E097BA00083EBEC /* MWMActionBarButton.m in Sources */,
|
||||
3D2D79C12C7C7EA00062BC3D /* PersonalData.xcdatamodeld in Sources */,
|
||||
47F86CFF20C936FC00FEE291 /* TabView.swift in Sources */,
|
||||
5260D3CE2C64F60200C673B4 /* APIEndpoints.swift in Sources */,
|
||||
34AB66741FC5AA330078E451 /* BaseRoutePreviewStatus.swift in Sources */,
|
||||
|
@ -4974,7 +4975,6 @@
|
|||
3404F4992028A20D0090E401 /* BMCCategoryCell.swift in Sources */,
|
||||
F62607FD207B790300176C5A /* SpinnerAlert.swift in Sources */,
|
||||
3444DFD21F17620C00E73099 /* MWMMapWidgetsHelper.mm in Sources */,
|
||||
3D585BFA2C768C3A005DF71F /* ToastView.swift in Sources */,
|
||||
3472B5E1200F86C800DC6CD5 /* MWMEditorHelper.mm in Sources */,
|
||||
3D2D79CC2C7C8C350062BC3D /* ProfileService.swift in Sources */,
|
||||
99F3EB1123F418C900C713F8 /* PlacePageBuilder.swift in Sources */,
|
||||
|
@ -5618,12 +5618,12 @@
|
|||
minimumVersion = 3.0.0;
|
||||
};
|
||||
};
|
||||
529212402C735A61007B97E1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = {
|
||||
529A5F082C858F82004FE4A1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SDWebImage/SDWebImageSwiftUI.git";
|
||||
requirement = {
|
||||
kind = upToNextMinorVersion;
|
||||
minimumVersion = 3.0.0;
|
||||
kind = exactVersion;
|
||||
version = 2.0.2;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
@ -5634,22 +5634,21 @@
|
|||
package = 5292123B2C7359FC007B97E1 /* XCRemoteSwiftPackageReference "CountryPickerView" */;
|
||||
productName = CountryPickerView;
|
||||
};
|
||||
529212412C735A61007B97E1 /* SDWebImageSwiftUI */ = {
|
||||
529A5F092C858F82004FE4A1 /* SDWebImageSwiftUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 529212402C735A61007B97E1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */;
|
||||
package = 529A5F082C858F82004FE4A1 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */;
|
||||
productName = SDWebImageSwiftUI;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
3D2D79BF2C7C7EA00062BC3D /* PersonalData.xcdatamodeld */ = {
|
||||
529A5F112C859535004FE4A1 /* PersonalData.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
3D2D79C02C7C7EA00062BC3D /* PersonalData.xcdatamodel */,
|
||||
529A5F122C859535004FE4A1 /* PersonalData.xcdatamodel */,
|
||||
);
|
||||
currentVersion = 3D2D79C02C7C7EA00062BC3D /* PersonalData.xcdatamodel */;
|
||||
name = PersonalData.xcdatamodeld;
|
||||
path = /Users/llcrebus/Projects/Tourism/iphone/Maps/Tourism/Data/Db/DataModels/PersonalData.xcdatamodeld;
|
||||
currentVersion = 529A5F122C859535004FE4A1 /* PersonalData.xcdatamodel */;
|
||||
path = PersonalData.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//
|
||||
// PersonalData.swift
|
||||
// OMaps
|
||||
//
|
||||
// Created by LLC Rebus on 26/08/24.
|
||||
// Copyright © 2024 Organic Maps. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
|
@ -8,7 +8,7 @@ protocol ProfileService {
|
|||
func updateProfile(
|
||||
fullName: String,
|
||||
country: String,
|
||||
email: String,
|
||||
email: String?,
|
||||
pfpUrl: UIImage?
|
||||
) -> AnyPublisher<PersonalDataDTO, ResourceError>
|
||||
|
||||
|
@ -18,6 +18,12 @@ protocol ProfileService {
|
|||
}
|
||||
|
||||
class ProfileServiceImpl: ProfileService {
|
||||
let userPreferences: UserPreferences
|
||||
|
||||
init(userPreferences: UserPreferences) {
|
||||
self.userPreferences = userPreferences
|
||||
}
|
||||
|
||||
func getPersonalData() -> AnyPublisher<PersonalDataDTO, ResourceError> {
|
||||
return CombineNetworkHelper.get(path: APIEndpoints.getUserUrl)
|
||||
}
|
||||
|
@ -25,17 +31,87 @@ class ProfileServiceImpl: ProfileService {
|
|||
func updateProfile(
|
||||
fullName: String,
|
||||
country: String,
|
||||
email: String,
|
||||
email: String?,
|
||||
pfpUrl: UIImage?
|
||||
) -> AnyPublisher<PersonalDataDTO, ResourceError> {
|
||||
let body = createMultipartFormData(fullName: fullName, country: country, email: email, pfpUrl: pfpUrl)
|
||||
var parameters = [
|
||||
[
|
||||
"key": "full_name",
|
||||
"value": fullName,
|
||||
"type": "text"
|
||||
],
|
||||
[
|
||||
"key": "country",
|
||||
"value": country,
|
||||
"type": "text"
|
||||
],
|
||||
[
|
||||
"key": "_method",
|
||||
"value": "PUT",
|
||||
"type": "text"
|
||||
]] as [[String: Any]]
|
||||
|
||||
let boundary = UUID().uuidString
|
||||
let headers = ["Content-Type": "multipart/form-data; boundary=\(boundary)"]
|
||||
if let newEmail = email {
|
||||
parameters.append([
|
||||
"key": "email",
|
||||
"value": newEmail,
|
||||
"type": "text"])
|
||||
}
|
||||
|
||||
return CombineNetworkHelper.post(path: APIEndpoints.updateProfileUrl, body: body, headers: headers)
|
||||
let theme = userPreferences.getTheme()
|
||||
parameters.append([
|
||||
"key": "theme",
|
||||
"value": theme?.code ?? "light",
|
||||
"type": "text"])
|
||||
|
||||
let language = userPreferences.getLanguage()
|
||||
parameters.append([
|
||||
"key": "language",
|
||||
"value": language?.code ?? "ru",
|
||||
"type": "text"])
|
||||
|
||||
let boundary = "Boundary-\(UUID().uuidString)"
|
||||
var body = Data()
|
||||
|
||||
for param in parameters {
|
||||
let paramName = param["key"] as! String
|
||||
body += Data("--\(boundary)\r\n".utf8)
|
||||
body += Data("Content-Disposition: form-data; name=\"\(paramName)\"\r\n\r\n".utf8)
|
||||
body += Data("\(param["value"] as! String)\r\n".utf8)
|
||||
}
|
||||
|
||||
// Add image file data if it exists
|
||||
if let image = pfpUrl, let imageData = image.jpegData(compressionQuality: 0.01) {
|
||||
body += Data("--\(boundary)\r\n".utf8)
|
||||
body += Data("Content-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"\r\n".utf8)
|
||||
body += Data("Content-Type: image/jpeg\r\n\r\n".utf8)
|
||||
body += imageData
|
||||
body += Data("\r\n".utf8)
|
||||
}
|
||||
|
||||
body += Data("--\(boundary)--\r\n".utf8)
|
||||
|
||||
var request = URLRequest(url: URL(string: "https://product.rebus.tj/api/profile")!, timeoutInterval: Double.infinity)
|
||||
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
if let token = userPreferences.getToken() {
|
||||
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
||||
}
|
||||
|
||||
request.httpMethod = "POST"
|
||||
request.httpBody = body
|
||||
|
||||
return URLSession.shared.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
try CombineNetworkHelper.handleResponse(data: data, response: response)
|
||||
}
|
||||
.mapError { error in
|
||||
CombineNetworkHelper.handleMappingError(error)
|
||||
}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
func updateLanguage(code: String) {
|
||||
// TODO: cmon
|
||||
}
|
||||
|
@ -44,44 +120,3 @@ class ProfileServiceImpl: ProfileService {
|
|||
// TODO: cmon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func createMultipartFormData(fullName: String, country: String, email: String?, pfpUrl: UIImage?) -> Data {
|
||||
let boundary = UUID().uuidString
|
||||
var body = Data()
|
||||
|
||||
let boundaryPrefix = "--\(boundary)\r\n"
|
||||
|
||||
body.appendString(boundaryPrefix)
|
||||
body.appendString("Content-Disposition: form-data; name=\"fullName\"\r\n\r\n")
|
||||
body.appendString("\(fullName)\r\n")
|
||||
|
||||
body.appendString(boundaryPrefix)
|
||||
body.appendString("Content-Disposition: form-data; name=\"country\"\r\n\r\n")
|
||||
body.appendString("\(country)\r\n")
|
||||
|
||||
if let email = email {
|
||||
body.appendString(boundaryPrefix)
|
||||
body.appendString("Content-Disposition: form-data; name=\"email\"\r\n\r\n")
|
||||
body.appendString("\(email)\r\n")
|
||||
}
|
||||
|
||||
if let image = pfpUrl, let imageData = image.jpegData(compressionQuality: 0.5) {
|
||||
body.appendString(boundaryPrefix)
|
||||
body.appendString("Content-Disposition: form-data; name=\"pfpUrl\"; filename=\"profile.jpg\"\r\n")
|
||||
body.appendString("Content-Type: image/jpeg\r\n\r\n")
|
||||
body.append(imageData)
|
||||
body.appendString("\r\n")
|
||||
}
|
||||
|
||||
body.appendString("--\(boundary)--\r\n")
|
||||
return body
|
||||
}
|
||||
|
||||
extension Data {
|
||||
mutating func appendString(_ string: String) {
|
||||
if let data = string.data(using: .utf8) {
|
||||
append(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
// EminoFire is a kind of "library" for the abstraction of http code.
|
||||
// It is named after the inventor of this piece - Emin
|
||||
|
||||
class CombineNetworkHelper {
|
||||
// MARK: - Lower level code
|
||||
static func createRequest(url: URL, method: String, headers: [String: String] = [:], body: Data? = nil) -> URLRequest {
|
||||
|
@ -102,6 +99,14 @@ class CombineNetworkHelper {
|
|||
return Fail(error: ResourceError.other(message: "Encoding error: \(error)")).eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
static func postt<T: Decodable>(path: String, body: Data, headers: [String: String] = [:], decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, ResourceError> {
|
||||
guard let url = URL(string: path) else {
|
||||
print("Invalid url")
|
||||
return Fail(error: ResourceError.other(message: "Invalid url")).eraseToAnyPublisher()
|
||||
}
|
||||
return performRequest(url: url, method: "POST", body: body, headers: headers, decoder: decoder)
|
||||
}
|
||||
|
||||
static func postWithoutBody<T: Decodable>(path: String, headers: [String: String] = [:], decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, ResourceError> {
|
||||
guard let url = URL(string: path) else {
|
||||
|
|
|
@ -57,7 +57,7 @@ class ProfileRepositoryImpl: ProfileRepository {
|
|||
func updateProfile(
|
||||
fullName: String,
|
||||
country: String,
|
||||
email: String,
|
||||
email: String?,
|
||||
pfpUrl: UIImage?
|
||||
) -> AnyPublisher<PersonalData, ResourceError> {
|
||||
return profileService.updateProfile(
|
||||
|
|
|
@ -9,7 +9,7 @@ protocol ProfileRepository {
|
|||
func updateProfile(
|
||||
fullName: String,
|
||||
country: String,
|
||||
email: String,
|
||||
email: String?,
|
||||
pfpUrl: UIImage?
|
||||
) -> AnyPublisher<PersonalData, ResourceError>
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class SignInViewController: UIViewController {
|
|||
|
||||
// Back Button
|
||||
backButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
|
||||
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
|
||||
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
|
||||
// Container View
|
||||
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
|
||||
|
|
|
@ -56,7 +56,11 @@ class SignUpViewController: UIViewController {
|
|||
return textField
|
||||
}()
|
||||
|
||||
private let cpv: CountryPickerView = getCountryPickerView()
|
||||
private let cpv: CountryPickerView = {
|
||||
let cpv = getCountryPickerView()
|
||||
cpv.textColor = .white
|
||||
return cpv
|
||||
}()
|
||||
|
||||
private let underline: UIView = {
|
||||
let underline = UIView()
|
||||
|
@ -131,12 +135,12 @@ class SignUpViewController: UIViewController {
|
|||
|
||||
// Back Button
|
||||
backButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
|
||||
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
|
||||
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
|
||||
// Container View
|
||||
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
|
||||
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
|
||||
containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -60),
|
||||
containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -32),
|
||||
|
||||
// Blur View
|
||||
blurView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
|
|
|
@ -9,9 +9,8 @@ struct LoadImageView: View {
|
|||
var body: some View {
|
||||
if let urlString = url {
|
||||
let errorImage = Image(systemName: "error_centered")
|
||||
WebImage(url: URL(string: urlString)) { image in
|
||||
image.image?.resizable()
|
||||
}
|
||||
WebImage(url: URL(string: urlString))
|
||||
.resizable()
|
||||
.onSuccess(perform: { image, data, cacheType in
|
||||
isError = false
|
||||
})
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ToastView: View {
|
||||
let message: String
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text(message)
|
||||
.padding()
|
||||
.foregroundColor(Color.onSurface)
|
||||
.background(Color.surface)
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: 5)
|
||||
}
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
withAnimation {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ struct PersonalDataScreen: View {
|
|||
@ObservedObject var profileVM: ProfileViewModel
|
||||
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
||||
|
||||
@State private var showSheet = false
|
||||
@State private var showPhotoPicker = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
|
@ -55,7 +55,7 @@ struct PersonalDataScreen: View {
|
|||
|
||||
Spacer().frame(width: 32)
|
||||
|
||||
// upload photo button
|
||||
// photo picker
|
||||
Group {
|
||||
Image(systemName: "photo.badge.arrow.down")
|
||||
.foregroundColor(Color.onBackground)
|
||||
|
@ -65,7 +65,7 @@ struct PersonalDataScreen: View {
|
|||
.textStyle(TextStyle.h4)
|
||||
}
|
||||
.onTapGesture {
|
||||
showSheet = true
|
||||
showPhotoPicker = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,15 +97,24 @@ struct PersonalDataScreen: View {
|
|||
PrimaryButton(
|
||||
label: L("save"),
|
||||
onClick: {
|
||||
|
||||
profileVM.save()
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.sheet(isPresented: $showSheet) {
|
||||
.sheet(isPresented: $showPhotoPicker) {
|
||||
ImagePicker(sourceType: .photoLibrary, selectedImage: $profileVM.pfpToUpload)
|
||||
}
|
||||
}
|
||||
.overlay(
|
||||
Group {
|
||||
if profileVM.shouldShowMessage {
|
||||
ToastView(message: profileVM.messageToShow, isPresented: $profileVM.shouldShowMessage)
|
||||
.padding(.bottom)
|
||||
}
|
||||
},
|
||||
alignment: .bottom
|
||||
)
|
||||
.background(Color.background)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class ProfileViewController: UIViewController {
|
|||
currencyPersistenceController: CurrencyPersistenceController.shared
|
||||
),
|
||||
profileRepository: ProfileRepositoryImpl(
|
||||
personalDataService: ProfileServiceImpl(),
|
||||
personalDataService: ProfileServiceImpl(userPreferences: UserPreferences.shared),
|
||||
personalDataPersistenceController: PersonalDataPersistenceController.shared
|
||||
),
|
||||
authRepository: AuthRepositoryImpl(authService: AuthServiceImpl()),
|
||||
|
@ -37,7 +37,6 @@ struct ProfileScreen: View {
|
|||
@State private var signOutLoading = false
|
||||
|
||||
@State private var navigateToPersonalData = false
|
||||
@State private var showToast = false
|
||||
|
||||
func onLanguageClick () {
|
||||
navigateToLanguageSettings()
|
||||
|
@ -58,51 +57,41 @@ struct ProfileScreen: View {
|
|||
}
|
||||
VerticalSpace(height: 32)
|
||||
|
||||
if let currencyRates = profileVM.currencyRates {
|
||||
CurrencyRatesView(currencyRates: currencyRates)
|
||||
VerticalSpace(height: 20)
|
||||
VStack(spacing: 20) {
|
||||
if let currencyRates = profileVM.currencyRates {
|
||||
CurrencyRatesView(currencyRates: currencyRates)
|
||||
}
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("personal_data"),
|
||||
icon: "person.circle",
|
||||
onClick: {
|
||||
onPersonalDataClick()
|
||||
}
|
||||
)
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("language"),
|
||||
icon: "globe",
|
||||
onClick: {
|
||||
onLanguageClick()
|
||||
}
|
||||
)
|
||||
|
||||
ThemeSwitch(themeViewModel: themeVM)
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("sign_out"),
|
||||
icon: "rectangle.portrait.and.arrow.right",
|
||||
isLoading: signOutLoading,
|
||||
onClick: {
|
||||
isSheetOpen = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("personal_data"),
|
||||
icon: "person.circle",
|
||||
onClick: {
|
||||
onPersonalDataClick()
|
||||
}
|
||||
)
|
||||
VerticalSpace(height: 20)
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("language"),
|
||||
icon: "globe",
|
||||
onClick: {
|
||||
onLanguageClick()
|
||||
}
|
||||
)
|
||||
VerticalSpace(height: 20)
|
||||
ThemeSwitch(themeViewModel: themeVM)
|
||||
VerticalSpace(height: 20)
|
||||
|
||||
GenericProfileItem(
|
||||
label: L("sign_out"),
|
||||
icon: "rectangle.portrait.and.arrow.right",
|
||||
isLoading: signOutLoading,
|
||||
onClick: {
|
||||
isSheetOpen = true
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding(16)
|
||||
}
|
||||
.overlay(
|
||||
Group {
|
||||
if showToast {
|
||||
ToastView(message: "This is a toast message", isPresented: $showToast)
|
||||
.padding(.bottom)
|
||||
}
|
||||
},
|
||||
alignment: .bottom
|
||||
)
|
||||
.sheet(isPresented: $isSheetOpen) {
|
||||
SignOutWarning(
|
||||
onSignOutClick: {
|
||||
|
|
|
@ -7,14 +7,17 @@ class ProfileViewModel: ObservableObject {
|
|||
private let profileRepository: ProfileRepository
|
||||
private let authRepository: AuthRepository
|
||||
private let userPreferences: UserPreferences
|
||||
var onMessageToUserRequested: ((String) -> Void)? = nil
|
||||
var onSignOutCompleted: (() -> Void)? = nil
|
||||
|
||||
@Published var messageToShow = ""
|
||||
@Published var shouldShowMessage = false
|
||||
|
||||
@Published var pfpFromRemote: URL? = nil
|
||||
@Published var pfpToUpload = UIImage()
|
||||
@Published var isImagePickerUsed: Bool = false
|
||||
|
||||
@Published var fullName: String = ""
|
||||
var currentEmail: String = "" // it changes only when confirmed by server
|
||||
@Published var email: String = ""
|
||||
@Published var countryCodeName: String? = nil
|
||||
@Published var personalData: PersonalData? = nil
|
||||
|
@ -65,7 +68,7 @@ class ProfileViewModel: ObservableObject {
|
|||
profileRepository.personalDataPassThroughSubject
|
||||
.sink { completion in
|
||||
if case let .failure(error) = completion {
|
||||
self.onMessageToUserRequested?(error.errorDescription)
|
||||
self.showMessage(error.errorDescription)
|
||||
}
|
||||
} receiveValue: { resource in
|
||||
self.personalData = resource
|
||||
|
@ -73,6 +76,7 @@ class ProfileViewModel: ObservableObject {
|
|||
self.pfpFromRemote = URL(string: pfpUrl)
|
||||
}
|
||||
self.fullName = resource.fullName
|
||||
self.currentEmail = resource.email
|
||||
self.email = resource.email
|
||||
self.countryCodeName = resource.country
|
||||
}
|
||||
|
@ -86,20 +90,21 @@ class ProfileViewModel: ObservableObject {
|
|||
profileRepository.updateProfile(
|
||||
fullName: fullName,
|
||||
country: countryCodeName!,
|
||||
email: email,
|
||||
// We shouldn't send email field if there's no change
|
||||
email: email == currentEmail ? nil : email,
|
||||
pfpUrl: pfpToUpload
|
||||
)
|
||||
.sink { completion in
|
||||
if case let .failure(error) = completion {
|
||||
self.onMessageToUserRequested?(error.errorDescription)
|
||||
self.showMessage(error.errorDescription)
|
||||
}
|
||||
} receiveValue: { resource in
|
||||
self.updatePersonalDataInMemory(personalData: resource)
|
||||
self.onMessageToUserRequested?(L("saved"))
|
||||
self.showMessage(L("saved"))
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
} else {
|
||||
self.onMessageToUserRequested?(L("please_fill_all_fields"))
|
||||
self.showMessage(L("please_fill_all_fields"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +118,7 @@ class ProfileViewModel: ObservableObject {
|
|||
currencyRepository.currencyPassThroughSubject
|
||||
.sink { completion in
|
||||
if case let .failure(error) = completion {
|
||||
self.onMessageToUserRequested?(error.errorDescription)
|
||||
self.showMessage(error.errorDescription)
|
||||
}
|
||||
} receiveValue: { resource in
|
||||
self.currencyRates = resource
|
||||
|
@ -127,14 +132,19 @@ class ProfileViewModel: ObservableObject {
|
|||
authRepository.signOut()
|
||||
.sink { completion in
|
||||
if case let .failure(error) = completion {
|
||||
self.onMessageToUserRequested?(error.errorDescription)
|
||||
self.showMessage(error.errorDescription)
|
||||
}
|
||||
} receiveValue: { response in
|
||||
self.signOutResponse = response
|
||||
self.userPreferences.setToken(value: nil)
|
||||
self.onSignOutCompleted?()
|
||||
self.onMessageToUserRequested?(response.message)
|
||||
self.showMessage(response.message)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func showMessage(_ message: String) {
|
||||
messageToShow = message
|
||||
shouldShowMessage = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,9 +97,10 @@ struct TextStyle {
|
|||
|
||||
extension Text {
|
||||
func textStyle(_ style: TextStyle) -> some View {
|
||||
self
|
||||
let calibrationFactor: CGFloat = 0.2
|
||||
return self
|
||||
.font(style.font)
|
||||
.lineSpacing(style.lineHeight)
|
||||
.lineSpacing(style.lineHeight * calibrationFactor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue