[ios] Switched OSM profile to SwiftUI and redesigned it

Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
This commit is contained in:
Yannik Bloscheck
2025-07-04 21:20:43 +02:00
committed by Yannik Bloscheck
parent db1ef9b148
commit 15c7e71866
127 changed files with 1753 additions and 2503 deletions

View File

@@ -1,30 +0,0 @@
#include <string>
#include "editor/osm_auth.hpp"
namespace osm_auth_ios
{
enum class AuthorizationButtonType
{
AuthorizationButtonTypeGoogle,
AuthorizationButtonTypeFacebook,
AuthorizationButtonTypeOSM
};
// Deletes any stored credentials if called with empty key or secret.
void AuthorizationStoreCredentials(std::string const & oauthToken);
BOOL AuthorizationHaveOAuth1Credentials();
void AuthorizationClearOAuth1Credentials();
BOOL AuthorizationHaveCredentials();
void AuthorizationClearCredentials();
// Returns empty key and secret if user has not beed authorized.
std::string const AuthorizationGetCredentials();
void AuthorizationSetNeedCheck(BOOL needCheck);
BOOL AuthorizationIsNeedCheck();
/// Returns nil if not logged in.
NSString * OSMUserName();
/// Returns 0 if not logged in.
NSInteger OSMUserChangesetsCount();
} // namespace osm_auth_ios

View File

@@ -1,124 +0,0 @@
#import "MWMAuthorizationCommon.h"
#import "MWMNetworkPolicy+UI.h"
#import "UIButton+RuntimeAttributes.h"
#include "base/logging.hpp"
#include "editor/server_api.hpp"
namespace osm_auth_ios
{
NSString * const kOSMRequestToken = @"OSMRequestToken"; // Unused after migration from OAuth1 to OAuth2
NSString * const kOSMRequestSecret = @"OSMRequestSecret"; // Unused after migration from OAuth1 to OAuth2
NSString * const kAuthNeedCheck = @"AuthNeedCheck";
NSString * const kOSMAuthToken = @"OSMAuthToken";
NSString * const kOSMUserName = @"UDOsmUserName";
NSString * const kOSMChangesetsCount = @"OSMUserChangesetsCount";
BOOL LoadOsmUserPreferences(osm::UserPreferences & prefs)
{
__block BOOL success = false;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
{
try
{
osm::ServerApi06 const api {osm::OsmOAuth::ServerAuth(AuthorizationGetCredentials())};
prefs = api.GetUserPreferences();
success = true;
}
catch (std::exception const & ex)
{
LOG(LWARNING, ("Can't load user preferences from OSM server:", ex.what()));
}
});
return success;
}
void AuthorizationStoreCredentials(std::string const & oauthToken)
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
[ud setObject:@(oauthToken.c_str()) forKey:kOSMAuthToken];
osm::UserPreferences prefs;
if (LoadOsmUserPreferences(prefs)) {
[ud setObject:@(prefs.m_displayName.c_str()) forKey:kOSMUserName];
// To also see # of edits when offline.
[ud setInteger:prefs.m_changesets forKey:kOSMChangesetsCount];
}
}
BOOL AuthorizationHaveOAuth1Credentials()
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
NSString * requestToken = [ud stringForKey:kOSMRequestToken];
NSString * requestSecret = [ud stringForKey:kOSMRequestSecret];
return requestToken.length && requestSecret.length;
}
void AuthorizationClearOAuth1Credentials()
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
[ud removeObjectForKey:kOSMRequestToken];
[ud removeObjectForKey:kOSMRequestSecret];
}
BOOL AuthorizationHaveCredentials()
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
NSString * oauthToken = [ud stringForKey:kOSMAuthToken];
return oauthToken.length;
}
void AuthorizationClearCredentials()
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
[ud removeObjectForKey:kOSMAuthToken];
[ud removeObjectForKey:kOSMRequestToken];
[ud removeObjectForKey:kOSMRequestSecret];
[ud removeObjectForKey:kOSMUserName];
[ud removeObjectForKey:kOSMChangesetsCount];
}
std::string const AuthorizationGetCredentials()
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
NSString * oauthToken = [ud stringForKey:kOSMAuthToken];
if (oauthToken)
return std::string(oauthToken.UTF8String);
return {};
}
void AuthorizationSetNeedCheck(BOOL needCheck)
{
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
[ud setBool:needCheck forKey:kAuthNeedCheck];
}
BOOL AuthorizationIsNeedCheck()
{
return [NSUserDefaults.standardUserDefaults boolForKey:kAuthNeedCheck];
}
NSString * OSMUserName()
{
return [NSUserDefaults.standardUserDefaults stringForKey:kOSMUserName];
}
NSInteger OSMUserChangesetsCount()
{
__block NSInteger count = -1;
[[MWMNetworkPolicy sharedPolicy] callOnlineApi:^(BOOL permitted) {
if (permitted)
if (osm::UserPreferences prefs; YES == LoadOsmUserPreferences(prefs))
count = prefs.m_changesets;
}];
NSUserDefaults * ud = NSUserDefaults.standardUserDefaults;
if (count >= 0)
{
[ud setInteger:count forKey:kOSMChangesetsCount];
return count;
}
return [ud integerForKey:kOSMChangesetsCount];
}
} // namespace osm_auth_ios

View File

@@ -1,5 +0,0 @@
#import "MWMViewController.h"
@interface MWMAuthorizationLoginViewController : MWMViewController
@end

View File

@@ -1,149 +0,0 @@
#import "MWMAlertViewController.h"
#import "MWMAuthorizationCommon.h"
#import "MWMAuthorizationLoginViewController.h"
#include <CoreApi/Framework.h>
namespace
{
NSString * const kWebViewAuthSegue = @"Authorization2WebViewAuthorizationSegue";
NSString * const kOSMAuthSegue = @"Authorization2OSMAuthorizationSegue";
NSString * const kCancel = L(@"cancel");
NSString * const kLogout = L(@"logout");
NSString * const kRefresh = L(@"refresh");
} // namespace
using namespace osm;
using namespace osm_auth_ios;
@interface MWMAuthorizationLoginViewController ()
@property (weak, nonatomic) IBOutlet UIView * authView;
@property (weak, nonatomic) IBOutlet UIView * accountView;
@property (weak, nonatomic) IBOutlet UIButton * loginOSMButton;
@property (weak, nonatomic) IBOutlet UIButton * signupButton;
@property (weak, nonatomic) IBOutlet UILabel * changesCountLabel;
@property (weak, nonatomic) IBOutlet UILabel * lastUpdateLabel;
@end
@implementation MWMAuthorizationLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self checkConnection];
if (AuthorizationHaveCredentials())
[self configHaveAuth];
else
[self configNoAuth];
AuthorizationSetNeedCheck(NO);
}
- (void)checkConnection
{
self.signupButton.enabled = Platform::IsConnected();
}
- (void)configHaveAuth
{
NSString * osmUserName = OSMUserName();
self.title = osmUserName.length > 0 ? osmUserName : L(@"osm_account");
self.authView.hidden = YES;
self.accountView.hidden = NO;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"•••" style:UIBarButtonItemStylePlain target:self action:@selector(showActionSheet)];
[self refresh:NO];
}
- (void)configNoAuth
{
self.title = L(@"profile");
self.authView.hidden = NO;
self.accountView.hidden = YES;
}
#pragma mark - Actions
- (void)performOnlineAction:(MWMVoidBlock)block
{
if (Platform::IsConnected())
block();
else
[self.alertController presentNoConnectionAlert];
}
- (IBAction)loginOSM
{
[self performOnlineAction:^
{
[self openUrl:@(OsmOAuth::ServerAuth().BuildOAuth2Url().c_str()) externally:NO skipEncoding:YES];
}];
}
- (IBAction)signup
{
[self performOnlineAction:^
{
[self openUrl:@(OsmOAuth::ServerAuth().GetRegistrationURL().c_str()) externally:YES];
}];
}
- (IBAction)osmTap
{
[self openUrl:L(@"osm_wiki_about_url")];
}
- (IBAction)historyTap
{
[self openUrl:@(OsmOAuth::ServerAuth().GetHistoryURL([OSMUserName() UTF8String]).c_str())];
}
- (void)logout
{
AuthorizationClearCredentials();
[self.navigationController popViewControllerAnimated:YES];
}
- (void)refresh:(BOOL)force
{
self.changesCountLabel.text = @(OSMUserChangesetsCount()).stringValue;
}
#pragma mark - ActionSheet
- (void)showActionSheet
{
UIAlertController * alertController =
[UIAlertController alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
alertController.popoverPresentationController.barButtonItem =
self.navigationItem.rightBarButtonItem;
[alertController addAction:[UIAlertAction actionWithTitle:kRefresh
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self refresh:YES];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:kLogout
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self logout];
}]];
[alertController
addAction:[UIAlertAction actionWithTitle:kCancel style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}
@end

View File

@@ -1,5 +0,0 @@
#import "MWMViewController.h"
@interface MWMAuthorizationOSMLoginViewController : MWMViewController
@end

View File

@@ -1,147 +0,0 @@
#import "MWMAuthorizationOSMLoginViewController.h"
#import "MWMAlertViewController.h"
#import "MWMAuthorizationCommon.h"
#import "MWMCircularProgress.h"
#import "MWMSettingsViewController.h"
#import "UITextField+RuntimeAttributes.h"
#include "base/logging.hpp"
#include "editor/server_api.hpp"
#include "platform/platform.hpp"
#include "private.h"
using namespace osm;
@interface MWMAuthorizationOSMLoginViewController ()<UITextFieldDelegate>
@property(weak, nonatomic) IBOutlet UITextField * loginTextField;
@property(weak, nonatomic) IBOutlet UITextField * passwordTextField;
@property(weak, nonatomic) IBOutlet UIButton * loginButton;
@property(weak, nonatomic) IBOutlet UIButton * forgotButton;
@property(weak, nonatomic) IBOutlet UIView * spinnerView;
@property(nonatomic) MWMCircularProgress * spinner;
@end
@implementation MWMAuthorizationOSMLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = L(@"osm_account");
[self checkConnection];
[self stopSpinner];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (!self.loginTextField.text.length && !self.passwordTextField.text.length)
[self.loginTextField becomeFirstResponder];
}
- (BOOL)shouldAutorotate { return NO; }
- (void)checkConnection { self.forgotButton.enabled = Platform::IsConnected(); }
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ([textField isEqual:self.loginTextField])
{
[self.passwordTextField becomeFirstResponder];
}
else if ([textField isEqual:self.passwordTextField])
{
[textField resignFirstResponder];
[self login];
}
return YES;
}
- (void)startSpinner
{
self.spinnerView.hidden = NO;
self.spinner = [[MWMCircularProgress alloc] initWithParentView:self.spinnerView];
[self.spinner setInvertColor:YES];
self.spinner.state = MWMCircularProgressStateSpinner;
self.loginTextField.enabled = NO;
self.passwordTextField.enabled = NO;
self.forgotButton.enabled = NO;
[self.loginButton setTitle:@"" forState:UIControlStateNormal];
}
- (void)stopSpinner
{
self.spinnerView.hidden = YES;
self.spinner = nil;
self.loginTextField.enabled = YES;
self.passwordTextField.enabled = YES;
self.forgotButton.enabled = YES;
[self.loginButton setTitle:L(@"login") forState:UIControlStateNormal];
}
#pragma mark - Actions
- (IBAction)login
{
if (!self.loginButton.enabled || self.spinner)
return;
if (Platform::IsConnected())
{
NSString * username = self.loginTextField.text;
NSString * password = self.passwordTextField.text;
[self startSpinner];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
OsmOAuth auth = OsmOAuth::ServerAuth();
try
{
auth.AuthorizePassword(username.UTF8String, password.UTF8String);
}
catch (std::exception const & ex)
{
LOG(LWARNING, ("Error login", ex.what()));
}
dispatch_async(dispatch_get_main_queue(), ^{
[self stopSpinner];
if (auth.IsAuthorized())
{
osm_auth_ios::AuthorizationStoreCredentials(auth.GetAuthToken());
UIViewController * svc = nil;
for (UIViewController * vc in self.navigationController.viewControllers)
{
if ([vc isKindOfClass:[MWMSettingsViewController class]])
{
svc = vc;
break;
}
}
if (svc)
[self.navigationController popToViewController:svc animated:YES];
else
[self.navigationController popToRootViewControllerAnimated:YES];
}
else
{
[self.alertController presentInvalidUserNameOrPasswordAlert];
}
});
});
}
else
{
[self.alertController presentNoConnectionAlert];
}
}
- (IBAction)cancel { [self.navigationController popViewControllerAnimated:YES]; }
- (IBAction)forgotPassword
{
[self openUrl:@(OsmOAuth::ServerAuth().GetResetPasswordURL().c_str())];
}
@end