Get servers and choose one randomly for signup (IOS-171)

Server list from backend has a weight which plays a role in which server gets selected.
mastodon.social is the fallback in case something goes wrong
This commit is contained in:
Nathan Mattes 2023-07-02 14:43:49 +02:00
parent fcb5275dc8
commit 4b5151bb39
7 changed files with 93 additions and 28 deletions

View File

@ -270,7 +270,7 @@
"welcome": { "welcome": {
"log_in": "Log In", "log_in": "Log In",
"learn_more": "Learn more", "learn_more": "Learn more",
"join_default_server": "Join mastodon.social", "join_default_server": "Join %@",
"pick_server": "Pick another server", "pick_server": "Pick another server",
"separator": { "separator": {
"or": "or" "or": "or"

View File

@ -47,10 +47,6 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
private(set) lazy var joinDefaultServerButton: UIButton = { private(set) lazy var joinDefaultServerButton: UIButton = {
var buttonConfiguration = UIButton.Configuration.filled() var buttonConfiguration = UIButton.Configuration.filled()
buttonConfiguration.attributedTitle = AttributedString(
L10n.Scene.Welcome.joinDefaultServer,
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
)
buttonConfiguration.baseForegroundColor = .white buttonConfiguration.baseForegroundColor = .white
buttonConfiguration.background.backgroundColor = Asset.Colors.Brand.blurple.color buttonConfiguration.background.backgroundColor = Asset.Colors.Brand.blurple.color
buttonConfiguration.background.cornerRadius = 14 buttonConfiguration.background.cornerRadius = 14
@ -217,6 +213,23 @@ extension WelcomeViewController {
.store(in: &disposeBag) .store(in: &disposeBag)
setupIllustrationLayout() setupIllustrationLayout()
joinDefaultServerButton.configuration?.showsActivityIndicator = true
joinDefaultServerButton.isEnabled = false
joinDefaultServerButton.configuration?.title = nil
viewModel.downloadDefaultServer { [weak self] in
guard let selectedDefaultServer = self?.viewModel.randomDefaultServer else { return }
DispatchQueue.main.async {
self?.joinDefaultServerButton.configuration?.showsActivityIndicator = false
self?.joinDefaultServerButton.isEnabled = true
self?.joinDefaultServerButton.configuration?.attributedTitle = AttributedString(
L10n.Scene.Welcome.joinDefaultServer(selectedDefaultServer.domain),
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
)
}
}
} }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
@ -262,12 +275,12 @@ extension WelcomeViewController {
//MARK: - Actions //MARK: - Actions
@objc @objc
private func joinDefaultServer(_ sender: UIButton) { private func joinDefaultServer(_ sender: UIButton) {
guard let server = viewModel.randomDefaultServer else { return }
sender.configuration?.title = nil sender.configuration?.title = nil
sender.isEnabled = false sender.isEnabled = false
sender.configuration?.showsActivityIndicator = true sender.configuration?.showsActivityIndicator = true
let server = Mastodon.Entity.Server.mastodonDotSocial
authenticationViewModel.isAuthenticating.send(true) authenticationViewModel.isAuthenticating.send(true)
context.apiService.instance(domain: server.domain) context.apiService.instance(domain: server.domain)
@ -320,19 +333,26 @@ extension WelcomeViewController {
self.authenticationViewModel.isAuthenticating.send(false) self.authenticationViewModel.isAuthenticating.send(false)
switch completion { switch completion {
case .failure(let error): case .failure(let error ):
//TODO: show an alert or something guard let randomServer = self.viewModel.pickRandomDefaultServer() else { return }
break
case .finished: viewModel.randomDefaultServer = randomServer
break
sender.isEnabled = true
sender.configuration?.showsActivityIndicator = false
sender.configuration?.attributedTitle = AttributedString(
L10n.Scene.Welcome.joinDefaultServer(randomServer.domain),
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
)
case .finished:
sender.isEnabled = true
sender.configuration?.showsActivityIndicator = false
sender.configuration?.attributedTitle = AttributedString(
L10n.Scene.Welcome.joinDefaultServer(server.domain),
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
)
} }
sender.isEnabled = true
sender.configuration?.showsActivityIndicator = false
sender.configuration?.attributedTitle = AttributedString(
L10n.Scene.Welcome.joinDefaultServer,
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
)
} receiveValue: { [weak self] response in } receiveValue: { [weak self] response in
guard let self = self else { return } guard let self = self else { return }
if let rules = response.instance.value.rules, !rules.isEmpty { if let rules = response.instance.value.rules, !rules.isEmpty {

View File

@ -8,11 +8,14 @@
import Foundation import Foundation
import Combine import Combine
import MastodonCore import MastodonCore
import MastodonSDK
final class WelcomeViewModel { final class WelcomeViewModel {
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
private(set) var defaultServers: [Mastodon.Entity.DefaultServer]?
var randomDefaultServer: Mastodon.Entity.Server?
// input // input
let context: AppContext let context: AppContext
@ -26,5 +29,40 @@ final class WelcomeViewModel {
.map { !$0.isEmpty } .map { !$0.isEmpty }
.assign(to: &$needsShowDismissEntry) .assign(to: &$needsShowDismissEntry)
} }
func downloadDefaultServer(completion: (() -> Void)? = nil) {
context.apiService.defaultServers()
.timeout(.milliseconds(500) , scheduler: DispatchQueue.main)
.sink { [weak self] result in
switch result {
case .finished:
if let defaultServers = self?.defaultServers, defaultServers.isEmpty == false {
self?.randomDefaultServer = self?.pickRandomDefaultServer()
} else {
self?.randomDefaultServer = Mastodon.Entity.Server.mastodonDotSocial
}
case .failure(_):
self?.randomDefaultServer = Mastodon.Entity.Server.mastodonDotSocial
}
completion?()
} receiveValue: { [weak self] servers in
self?.defaultServers = servers.value
}
.store(in: &disposeBag)
}
func pickRandomDefaultServer() -> Mastodon.Entity.Server? {
guard let defaultServers else { return nil }
let weightedServers = defaultServers
.compactMap { [Mastodon.Entity.DefaultServer](repeating: $0, count: $0.weight) }
.reduce([], +)
let randomServer = weightedServers.randomElement()
.map { Mastodon.Entity.Server(domain: $0.domain, instance: Mastodon.Entity.Instance(domain: $0.domain)) }
return randomServer
}
} }

View File

@ -1510,8 +1510,10 @@ public enum L10n {
} }
} }
public enum Welcome { public enum Welcome {
/// Join mastodon.social /// Join %@
public static let joinDefaultServer = L10n.tr("Localizable", "Scene.Welcome.JoinDefaultServer", fallback: "Join mastodon.social") public static func joinDefaultServer(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Welcome.JoinDefaultServer", String(describing: p1), fallback: "Join %@")
}
/// Learn more /// Learn more
public static let learnMore = L10n.tr("Localizable", "Scene.Welcome.LearnMore", fallback: "Learn more") public static let learnMore = L10n.tr("Localizable", "Scene.Welcome.LearnMore", fallback: "Learn more")
/// Log In /// Log In

View File

@ -530,7 +530,7 @@ uploaded to Mastodon.";
"Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon"; "Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon";
"Scene.Welcome.Education.Servers.Description" = "Every Mastodon account is hosted on a server — each with its own values, rules, & admins. No matter which one you pick, you can follow and interact with people on any server."; "Scene.Welcome.Education.Servers.Description" = "Every Mastodon account is hosted on a server — each with its own values, rules, & admins. No matter which one you pick, you can follow and interact with people on any server.";
"Scene.Welcome.Education.Servers.Title" = "What are servers?"; "Scene.Welcome.Education.Servers.Title" = "What are servers?";
"Scene.Welcome.JoinDefaultServer" = "Join mastodon.social"; "Scene.Welcome.JoinDefaultServer" = "Join %@";
"Scene.Welcome.LearnMore" = "Learn more"; "Scene.Welcome.LearnMore" = "Learn more";
"Scene.Welcome.LogIn" = "Log In"; "Scene.Welcome.LogIn" = "Log In";
"Scene.Welcome.PickServer" = "Pick another server"; "Scene.Welcome.PickServer" = "Pick another server";
@ -556,4 +556,4 @@ uploaded to Mastodon.";
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts."; "Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers"; "Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
"Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social"; "Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social";
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower"; "Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";

View File

@ -530,7 +530,7 @@ uploaded to Mastodon.";
"Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon"; "Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon";
"Scene.Welcome.Education.Servers.Description" = "Every Mastodon account is hosted on a server — each with its own values, rules, & admins. No matter which one you pick, you can follow and interact with people on any server."; "Scene.Welcome.Education.Servers.Description" = "Every Mastodon account is hosted on a server — each with its own values, rules, & admins. No matter which one you pick, you can follow and interact with people on any server.";
"Scene.Welcome.Education.Servers.Title" = "What are servers?"; "Scene.Welcome.Education.Servers.Title" = "What are servers?";
"Scene.Welcome.JoinDefaultServer" = "Join mastodon.social"; "Scene.Welcome.JoinDefaultServer" = "Join %@";
"Scene.Welcome.LearnMore" = "Learn more"; "Scene.Welcome.LearnMore" = "Learn more";
"Scene.Welcome.LogIn" = "Log In"; "Scene.Welcome.LogIn" = "Log In";
"Scene.Welcome.PickServer" = "Pick another server"; "Scene.Welcome.PickServer" = "Pick another server";
@ -556,4 +556,4 @@ uploaded to Mastodon.";
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts."; "Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers"; "Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
"Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social"; "Widget.MultipleFollowers.MockUser.AccountName" = "another@follower.social";
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower"; "Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";

View File

@ -4,7 +4,12 @@ import Foundation
extension Mastodon.Entity { extension Mastodon.Entity {
public struct DefaultServer: Codable { public struct DefaultServer: Codable {
let domain: String public let domain: String
let weight: Int public let weight: Int
public init(domain: String, weight: Int) {
self.domain = domain
self.weight = weight
}
} }
} }