Skip to content

Commit a843d94

Browse files
authored
Merge pull request #1081 from mastodon/ios-171-list-of-default-servers
Make list of default servers configurable (IOS-171)
2 parents bd1339e + 06e9e3c commit a843d94

File tree

10 files changed

+135
-31
lines changed

10 files changed

+135
-31
lines changed

Localization/app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@
270270
"welcome": {
271271
"log_in": "Log In",
272272
"learn_more": "Learn more",
273-
"join_default_server": "Join mastodon.social",
273+
"join_default_server": "Join %@",
274274
"pick_server": "Pick another server",
275275
"separator": {
276276
"or": "or"

Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
4747

4848
private(set) lazy var joinDefaultServerButton: UIButton = {
4949
var buttonConfiguration = UIButton.Configuration.filled()
50-
buttonConfiguration.attributedTitle = AttributedString(
51-
L10n.Scene.Welcome.joinDefaultServer,
52-
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
53-
)
5450
buttonConfiguration.baseForegroundColor = .white
5551
buttonConfiguration.background.backgroundColor = Asset.Colors.Brand.blurple.color
5652
buttonConfiguration.background.cornerRadius = 14
@@ -217,6 +213,23 @@ extension WelcomeViewController {
217213
.store(in: &disposeBag)
218214

219215
setupIllustrationLayout()
216+
217+
joinDefaultServerButton.configuration?.showsActivityIndicator = true
218+
joinDefaultServerButton.isEnabled = false
219+
joinDefaultServerButton.configuration?.title = nil
220+
221+
viewModel.downloadDefaultServer { [weak self] in
222+
guard let selectedDefaultServer = self?.viewModel.randomDefaultServer else { return }
223+
224+
DispatchQueue.main.async {
225+
self?.joinDefaultServerButton.configuration?.showsActivityIndicator = false
226+
self?.joinDefaultServerButton.isEnabled = true
227+
self?.joinDefaultServerButton.configuration?.attributedTitle = AttributedString(
228+
L10n.Scene.Welcome.joinDefaultServer(selectedDefaultServer.domain),
229+
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
230+
)
231+
}
232+
}
220233
}
221234

222235
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
@@ -262,12 +275,12 @@ extension WelcomeViewController {
262275
//MARK: - Actions
263276
@objc
264277
private func joinDefaultServer(_ sender: UIButton) {
278+
279+
guard let server = viewModel.randomDefaultServer else { return }
265280
sender.configuration?.title = nil
266281
sender.isEnabled = false
267282
sender.configuration?.showsActivityIndicator = true
268283

269-
let server = Mastodon.Entity.Server.mastodonDotSocial
270-
271284
authenticationViewModel.isAuthenticating.send(true)
272285

273286
context.apiService.instance(domain: server.domain)
@@ -320,19 +333,26 @@ extension WelcomeViewController {
320333
self.authenticationViewModel.isAuthenticating.send(false)
321334

322335
switch completion {
323-
case .failure(let error):
324-
//TODO: show an alert or something
325-
break
326-
case .finished:
327-
break
336+
case .failure(let error ):
337+
guard let randomServer = self.viewModel.pickRandomDefaultServer() else { return }
338+
339+
self.viewModel.randomDefaultServer = randomServer
340+
341+
sender.isEnabled = true
342+
sender.configuration?.showsActivityIndicator = false
343+
sender.configuration?.attributedTitle = AttributedString(
344+
L10n.Scene.Welcome.joinDefaultServer(randomServer.domain),
345+
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
346+
)
347+
case .finished:
348+
sender.isEnabled = true
349+
sender.configuration?.showsActivityIndicator = false
350+
sender.configuration?.attributedTitle = AttributedString(
351+
L10n.Scene.Welcome.joinDefaultServer(server.domain),
352+
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
353+
)
328354
}
329355

330-
sender.isEnabled = true
331-
sender.configuration?.showsActivityIndicator = false
332-
sender.configuration?.attributedTitle = AttributedString(
333-
L10n.Scene.Welcome.joinDefaultServer,
334-
attributes: .init([.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))])
335-
)
336356
} receiveValue: { [weak self] response in
337357
guard let self = self else { return }
338358
if let rules = response.instance.value.rules, !rules.isEmpty {

Mastodon/Scene/Onboarding/Welcome/WelcomeViewModel.swift

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import Foundation
99
import Combine
1010
import MastodonCore
11+
import MastodonSDK
1112

1213
final class WelcomeViewModel {
1314

1415
var disposeBag = Set<AnyCancellable>()
15-
16+
private(set) var defaultServers: [Mastodon.Entity.DefaultServer]?
17+
var randomDefaultServer: Mastodon.Entity.Server?
18+
1619
// input
1720
let context: AppContext
1821

@@ -26,5 +29,40 @@ final class WelcomeViewModel {
2629
.map { !$0.isEmpty }
2730
.assign(to: &$needsShowDismissEntry)
2831
}
29-
32+
33+
func downloadDefaultServer(completion: (() -> Void)? = nil) {
34+
context.apiService.defaultServers()
35+
.timeout(.milliseconds(500) , scheduler: DispatchQueue.main)
36+
.sink { [weak self] result in
37+
38+
switch result {
39+
case .finished:
40+
if let defaultServers = self?.defaultServers, defaultServers.isEmpty == false {
41+
self?.randomDefaultServer = self?.pickRandomDefaultServer()
42+
} else {
43+
self?.randomDefaultServer = Mastodon.Entity.Server.mastodonDotSocial
44+
}
45+
case .failure(_):
46+
self?.randomDefaultServer = Mastodon.Entity.Server.mastodonDotSocial
47+
}
48+
49+
completion?()
50+
} receiveValue: { [weak self] servers in
51+
self?.defaultServers = servers.value
52+
}
53+
.store(in: &disposeBag)
54+
}
55+
56+
func pickRandomDefaultServer() -> Mastodon.Entity.Server? {
57+
guard let defaultServers else { return nil }
58+
59+
let weightedServers = defaultServers
60+
.compactMap { [Mastodon.Entity.DefaultServer](repeating: $0, count: $0.weight) }
61+
.reduce([], +)
62+
63+
let randomServer = weightedServers.randomElement()
64+
.map { Mastodon.Entity.Server(domain: $0.domain, instance: Mastodon.Entity.Instance(domain: $0.domain)) }
65+
66+
return randomServer
67+
}
3068
}

MastodonSDK/Sources/MastodonCore/Service/API/APIService+Onboarding.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ extension APIService {
3333
public func languages() -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Language]>, Error> {
3434
return Mastodon.API.Onboarding.languages(session: session)
3535
}
36+
37+
public func defaultServers() -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.DefaultServer]>, Error> {
38+
return Mastodon.API.Onboarding.defaultServers(session: session)
39+
}
3640
}

MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,8 +1510,10 @@ public enum L10n {
15101510
}
15111511
}
15121512
public enum Welcome {
1513-
/// Join mastodon.social
1514-
public static let joinDefaultServer = L10n.tr("Localizable", "Scene.Welcome.JoinDefaultServer", fallback: "Join mastodon.social")
1513+
/// Join %@
1514+
public static func joinDefaultServer(_ p1: Any) -> String {
1515+
return L10n.tr("Localizable", "Scene.Welcome.JoinDefaultServer", String(describing: p1), fallback: "Join %@")
1516+
}
15151517
/// Learn more
15161518
public static let learnMore = L10n.tr("Localizable", "Scene.Welcome.LearnMore", fallback: "Learn more")
15171519
/// Log In

MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ uploaded to Mastodon.";
530530
"Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon";
531531
"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.";
532532
"Scene.Welcome.Education.Servers.Title" = "What are servers?";
533-
"Scene.Welcome.JoinDefaultServer" = "Join mastodon.social";
533+
"Scene.Welcome.JoinDefaultServer" = "Join %@";
534534
"Scene.Welcome.LearnMore" = "Learn more";
535535
"Scene.Welcome.LogIn" = "Log In";
536536
"Scene.Welcome.PickServer" = "Pick another server";
@@ -556,4 +556,4 @@ uploaded to Mastodon.";
556556
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
557557
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
558558
"Widget.MultipleFollowers.MockUser.AccountName" = "[email protected]";
559-
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";
559+
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";

MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ uploaded to Mastodon.";
530530
"Scene.Welcome.Education.Mastodon.Title" = "Welcome to Mastodon";
531531
"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.";
532532
"Scene.Welcome.Education.Servers.Title" = "What are servers?";
533-
"Scene.Welcome.JoinDefaultServer" = "Join mastodon.social";
533+
"Scene.Welcome.JoinDefaultServer" = "Join %@";
534534
"Scene.Welcome.LearnMore" = "Learn more";
535535
"Scene.Welcome.LogIn" = "Log In";
536536
"Scene.Welcome.PickServer" = "Pick another server";
@@ -556,4 +556,4 @@ uploaded to Mastodon.";
556556
"Widget.MultipleFollowers.ConfigurationDescription" = "Show number of followers for multiple accounts.";
557557
"Widget.MultipleFollowers.ConfigurationDisplayName" = "Multiple followers";
558558
"Widget.MultipleFollowers.MockUser.AccountName" = "[email protected]";
559-
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";
559+
"Widget.MultipleFollowers.MockUser.DisplayName" = "Another follower";

MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Onboarding.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ extension Mastodon.API.Onboarding {
1313
static let serversEndpointURL = Mastodon.API.joinMastodonEndpointURL.appendingPathComponent("servers")
1414
static let categoriesEndpointURL = Mastodon.API.joinMastodonEndpointURL.appendingPathComponent("categories")
1515
static let languagesEndpointURL = Mastodon.API.joinMastodonEndpointURL.appendingPathComponent("languages")
16+
static let defaultServersEndpointURL = Mastodon.API.joinMastodonEndpointURL.appendingPathComponent("default-servers")
1617

1718
/// Fetch server list
1819
///
@@ -96,6 +97,30 @@ extension Mastodon.API.Onboarding {
9697
}
9798
.eraseToAnyPublisher()
9899
}
100+
101+
/// Fetch default servers
102+
///
103+
/// Using this endpoint to fetch default servers
104+
///
105+
/// # Last Update
106+
/// 2023/07/02
107+
/// # Reference
108+
/// undocumented
109+
/// - Parameters:
110+
/// - session: `URLSession`
111+
/// - Returns: `AnyPublisher` contains `Language` nested in the response
112+
public static func defaultServers(
113+
session: URLSession
114+
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.DefaultServer]>, Error> {
115+
let request = Mastodon.API.get(url: defaultServersEndpointURL)
116+
return session.dataTaskPublisher(for: request)
117+
.tryMap { data, response in
118+
let value = try Mastodon.API.decode(type: [Mastodon.Entity.DefaultServer].self, from: data, response: response)
119+
return Mastodon.Response.Content(value: value, response: response)
120+
}
121+
.eraseToAnyPublisher()
122+
}
123+
99124
}
100125

101126
extension Mastodon.API.Onboarding {

MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,39 +134,39 @@ extension Mastodon.API {
134134
static func get(
135135
url: URL,
136136
query: GetQuery? = nil,
137-
authorization: OAuth.Authorization?
137+
authorization: OAuth.Authorization? = nil
138138
) -> URLRequest {
139139
return buildRequest(url: url, method: .GET, query: query, authorization: authorization)
140140
}
141141

142142
static func post(
143143
url: URL,
144144
query: PostQuery?,
145-
authorization: OAuth.Authorization?
145+
authorization: OAuth.Authorization? = nil
146146
) -> URLRequest {
147147
return buildRequest(url: url, method: .POST, query: query, authorization: authorization)
148148
}
149149

150150
static func patch(
151151
url: URL,
152152
query: PatchQuery?,
153-
authorization: OAuth.Authorization?
153+
authorization: OAuth.Authorization? = nil
154154
) -> URLRequest {
155155
return buildRequest(url: url, method: .PATCH, query: query, authorization: authorization)
156156
}
157157

158158
static func put(
159159
url: URL,
160160
query: PutQuery? = nil,
161-
authorization: OAuth.Authorization?
161+
authorization: OAuth.Authorization? = nil
162162
) -> URLRequest {
163163
return buildRequest(url: url, method: .PUT, query: query, authorization: authorization)
164164
}
165165

166166
static func delete(
167167
url: URL,
168168
query: DeleteQuery?,
169-
authorization: OAuth.Authorization?
169+
authorization: OAuth.Authorization? = nil
170170
) -> URLRequest {
171171
return buildRequest(url: url, method: .DELETE, query: query, authorization: authorization)
172172
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
2+
3+
import Foundation
4+
5+
extension Mastodon.Entity {
6+
public struct DefaultServer: Codable {
7+
public let domain: String
8+
public let weight: Int
9+
10+
public init(domain: String, weight: Int) {
11+
self.domain = domain
12+
self.weight = weight
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)