From 121d9f8bf5d5dba60fbf964a32e48ac1f2e33eff Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 02:10:40 +0200 Subject: [PATCH 1/8] Implement new account registration see #20 --- Nio.xcodeproj/project.pbxproj | 12 ++ Nio/AccountStore.swift | 27 +++ Nio/Authentication/LoginFormTextField.swift | 51 ++++++ Nio/Authentication/LoginView.swift | 70 +++----- Nio/Authentication/RegistrationView.swift | 159 ++++++++++++++++++ Nio/Extensions/URL+Homeserver.swift | 18 ++ Nio/Generated/Strings.swift | 13 ++ Nio/RootView.swift | 2 +- .../en.lproj/Localizable.strings | 6 + 9 files changed, 309 insertions(+), 49 deletions(-) create mode 100644 Nio/Authentication/LoginFormTextField.swift create mode 100644 Nio/Authentication/RegistrationView.swift create mode 100644 Nio/Extensions/URL+Homeserver.swift diff --git a/Nio.xcodeproj/project.pbxproj b/Nio.xcodeproj/project.pbxproj index be6c35bd..079e5483 100644 --- a/Nio.xcodeproj/project.pbxproj +++ b/Nio.xcodeproj/project.pbxproj @@ -41,6 +41,9 @@ 393411C723903C94003B49B8 /* EventCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 393411C623903C94003B49B8 /* EventCollection.swift */; }; 393411C923904428003B49B8 /* MXEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 393411C823904428003B49B8 /* MXEvent.swift */; }; 393411D1239087D2003B49B8 /* EventCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 393411D0239087D2003B49B8 /* EventCollectionTests.swift */; }; + 3955DD31245B81A200827F07 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3955DD30245B81A200827F07 /* RegistrationView.swift */; }; + 3955DD33245B824E00827F07 /* LoginFormTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3955DD32245B824E00827F07 /* LoginFormTextField.swift */; }; + 3955DD35245B9D2100827F07 /* URL+Homeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3955DD34245B9D2100827F07 /* URL+Homeserver.swift */; }; 3970DC942385A8BE00EFE31B /* KeyboardObserving in Frameworks */ = {isa = PBXBuildFile; productRef = 3970DC932385A8BE00EFE31B /* KeyboardObserving */; }; 3984654523B7ECBA006C173B /* MXURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984654423B7ECBA006C173B /* MXURL.swift */; }; 3984654823B8D809006C173B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3984654723B8D809006C173B /* SDWebImageSwiftUI */; }; @@ -133,6 +136,9 @@ 393411D0239087D2003B49B8 /* EventCollectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCollectionTests.swift; sourceTree = ""; }; 393411D2239087D2003B49B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3955DD36245C371C00827F07 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 3955DD30245B81A200827F07 /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = ""; }; + 3955DD32245B824E00827F07 /* LoginFormTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFormTextField.swift; sourceTree = ""; }; + 3955DD34245B9D2100827F07 /* URL+Homeserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Homeserver.swift"; sourceTree = ""; }; 3984654423B7ECBA006C173B /* MXURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXURL.swift; sourceTree = ""; }; 3997DCCF245732F000763C07 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; 39B834BF243FC42000AE1EA0 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = ""; }; @@ -210,6 +216,8 @@ isa = PBXGroup; children = ( 39C931F423846966004449E1 /* LoginView.swift */, + 3955DD30245B81A200827F07 /* RegistrationView.swift */, + 3955DD32245B824E00827F07 /* LoginFormTextField.swift */, 3902B8A42395A77800698B87 /* LoadingView.swift */, ); path = Authentication; @@ -404,6 +412,7 @@ CAC46D6223A278F40079C24F /* PreviewProvider+Enumeration.swift */, 39BA0722240B3C9A00FD28C6 /* MXCredentials+Keychain.swift */, 39BA0726240B534600FD28C6 /* Color+allAccent.swift */, + 3955DD34245B9D2100827F07 /* URL+Homeserver.swift */, ); path = Extensions; sourceTree = ""; @@ -709,11 +718,13 @@ 392389892386FD3900B2E1DF /* MXClient+Publisher.swift in Sources */, 3984654523B7ECBA006C173B /* MXURL.swift in Sources */, 392389D2238F2E6F00B2E1DF /* NIORoom.swift in Sources */, + 3955DD33245B824E00827F07 /* LoginFormTextField.swift in Sources */, 4B058B5624573A570059BC75 /* EditEvent.swift in Sources */, 392221AE243A0508004D8794 /* GroupedReactionsView.swift in Sources */, 3923898F2388707E00B2E1DF /* RoomListItemView.swift in Sources */, 39C931DD2384328A004449E1 /* AppDelegate.swift in Sources */, 393411C923904428003B49B8 /* MXEvent.swift in Sources */, + 3955DD35245B9D2100827F07 /* URL+Homeserver.swift in Sources */, 3923898D238859D100B2E1DF /* MX+Identifiable.swift in Sources */, CAC46D5B23A2734C0079C24F /* EnvironmentValues.swift in Sources */, 39C932072384BB13004449E1 /* RecentRoomsView.swift in Sources */, @@ -733,6 +744,7 @@ 392389CC238EBB1500B2E1DF /* ReverseList.swift in Sources */, 392389942388899200B2E1DF /* Formatter.swift in Sources */, 39BA0723240B3C9A00FD28C6 /* MXCredentials+Keychain.swift in Sources */, + 3955DD31245B81A200827F07 /* RegistrationView.swift in Sources */, 392221AC2438149D004D8794 /* RoomMemberEventView.swift in Sources */, 392221B4243D1627004D8794 /* RoomPowerLevelsEventView.swift in Sources */, CAC46D6323A278F40079C24F /* PreviewProvider+Enumeration.swift in Sources */, diff --git a/Nio/AccountStore.swift b/Nio/AccountStore.swift index 840c294f..9fd23318 100644 --- a/Nio/AccountStore.swift +++ b/Nio/AccountStore.swift @@ -45,6 +45,33 @@ class AccountStore: ObservableObject { self.session?.removeListener(self.listenReference) } + // MARK: - Registration + + func register(username: String, password: String, homeserver: URL) { + self.loginState = .authenticating + + self.client = MXRestClient(homeServer: homeserver, unrecognizedCertificateHandler: nil) + self.client?.register(username: username, password: password) { response in + switch response { + case .failure(let error): + self.loginState = .failure(error) + case .success(let credentials): + self.credentials = credentials + credentials.save(to: self.keychain) + + self.sync { result in + switch result { + case .failure(let error): + // Does this make sense? The login itself didn't fail, but syncing did. + self.loginState = .failure(error) + case .success(let state): + self.loginState = state + } + } + } + } + } + // MARK: - Login & Sync @Published var loginState: LoginState = .loggedOut diff --git a/Nio/Authentication/LoginFormTextField.swift b/Nio/Authentication/LoginFormTextField.swift new file mode 100644 index 00000000..77a68aa6 --- /dev/null +++ b/Nio/Authentication/LoginFormTextField.swift @@ -0,0 +1,51 @@ +import SwiftUI + +struct LoginFormTextField: View { + @Environment(\.colorScheme) var colorScheme + + var placeholder: String + @Binding var text: String + var onEditingChanged: ((Bool) -> Void)? + + var isSecure = false + + var buttonIcon: String? + var buttonAction: (() -> Void)? + + var body: some View { + ZStack { + Capsule(style: .continuous) + .foregroundColor(colorScheme == .light ? Color(#colorLiteral(red: 0.9395676295, green: 0.9395676295, blue: 0.9395676295, alpha: 1)) : Color(#colorLiteral(red: 0.2293992357, green: 0.2293992357, blue: 0.2293992357, alpha: 1))) + .frame(height: 50) + if isSecure { + SecureField(placeholder, text: $text) + .padding() + .textContentType(.password) + } else { + HStack { + TextField(placeholder, text: $text, onEditingChanged: onEditingChanged ?? { _ in }) + .padding() + .autocapitalization(.none) + .disableAutocorrection(true) + if buttonIcon != nil && buttonAction != nil { + Button(action: { + self.buttonAction!() + }, label: { + Image(systemName: buttonIcon!) + }) + .padding() + } + } + } + } + .frame(maxWidth: 400) + } +} + +struct LoginFormTextField_Previews: PreviewProvider { + static var previews: some View { + LoginFormTextField(placeholder: "Username", text: .constant("")) + .padding() + .previewLayout(.sizeThatFits) + } +} diff --git a/Nio/Authentication/LoginView.swift b/Nio/Authentication/LoginView.swift index 218f3ae0..f4f15792 100644 --- a/Nio/Authentication/LoginView.swift +++ b/Nio/Authentication/LoginView.swift @@ -3,6 +3,7 @@ import SwiftMatrixSDK struct LoginContainerView: View { @EnvironmentObject var store: AccountStore + @EnvironmentObject var settings: AppSettings @State private var username = "" @State private var password = "" @@ -18,18 +19,16 @@ struct LoginContainerView: View { isLoginEnabled: isLoginEnabled, onLogin: login, guessHomeserverURL: guessHomeserverURL) + .sheet(isPresented: $showingRegisterView) { + RegistrationContainerView() + .accentColor(self.settings.accentColor) + .environmentObject(self.store) + } } private func login() { - var homeserver = self.homeserver.isEmpty ? "https://matrix.org" : self.homeserver - - // If there's no scheme at all, the URLComponents initializer below will think it's a path with no hostname. - if !homeserver.contains("//") { - homeserver = "https://\(homeserver)" - } - var homeserverURLComponents = URLComponents(string: homeserver) - homeserverURLComponents?.scheme = "https" - guard let homeserverURL = homeserverURLComponents?.url else { + let homeserver = self.homeserver.isEmpty ? "https://matrix.org" : self.homeserver + guard let homeserverURL = URL(homeserverString: homeserver) else { // TODO: Handle error print("Invalid homeserver URL '\(homeserver)'") return @@ -86,9 +85,6 @@ struct LoginView: View { Spacer() } .keyboardObserving() - .sheet(isPresented: $showingRegisterView) { - Text(L10n.Login.registerNotYetImplemented) - } } var buttons: some View { @@ -117,6 +113,7 @@ struct LoginTitleView: View { let nio = Text("Nio").foregroundColor(.accentColor) return VStack { + // FIXME: This probably breaks localisation. (Text(L10n.Login.welcomeHeader) + nio + Text("!")) .font(.title) .bold() @@ -129,18 +126,25 @@ struct LoginForm: View { @Binding var username: String @Binding var password: String @Binding var homeserver: String - + let guessHomeserverURL: () -> Void var body: some View { VStack { - FormTextField(title: L10n.Login.Form.username, text: $username, onEditingChanged: { _ in - self.guessHomeserverURL() - }) + LoginFormTextField(placeholder: L10n.Login.Form.username, + text: $username, + onEditingChanged: { _ in self.guessHomeserverURL() }) + .padding(.horizontal) - FormTextField(title: L10n.Login.Form.password, text: $password, isSecure: true) + LoginFormTextField(placeholder: L10n.Login.Form.password, + text: $password, + isSecure: true) + .padding(.horizontal) + + LoginFormTextField(placeholder: L10n.Login.Form.homeserver, + text: $homeserver) + .padding(.horizontal) - FormTextField(title: L10n.Login.Form.homeserver, text: $homeserver) Text(L10n.Login.Form.homeserverOptionalExplanation) .font(.caption) .foregroundColor(.gray) @@ -148,36 +152,6 @@ struct LoginForm: View { } } -private struct FormTextField: View { - @Environment(\.colorScheme) var colorScheme - - var title: String - @Binding var text: String - var onEditingChanged: ((Bool) -> Void)? - - var isSecure = false - - var body: some View { - ZStack { - Capsule(style: .continuous) - .foregroundColor(colorScheme == .light ? Color(#colorLiteral(red: 0.9395676295, green: 0.9395676295, blue: 0.9395676295, alpha: 1)) : Color(#colorLiteral(red: 0.2293992357, green: 0.2293992357, blue: 0.2293992357, alpha: 1))) - .frame(height: 50) - if isSecure { - SecureField(title, text: $text) - .padding() - .textContentType(.password) - } else { - TextField(title, text: $text, onEditingChanged: onEditingChanged ?? { _ in }) - .padding() - .autocapitalization(.none) - .disableAutocorrection(true) - } - } - .padding(.horizontal) - .frame(maxWidth: 400) - } -} - struct LoginView_Previews: PreviewProvider { static var previews: some View { LoginView(username: .constant(""), diff --git a/Nio/Authentication/RegistrationView.swift b/Nio/Authentication/RegistrationView.swift new file mode 100644 index 00000000..16316e66 --- /dev/null +++ b/Nio/Authentication/RegistrationView.swift @@ -0,0 +1,159 @@ +import SwiftUI + +struct RegistrationContainerView: View { + @EnvironmentObject var store: AccountStore + + @State private var username = "" + @State private var password = "" + @State private var passwordConfirmation = "" + @State private var homeserver = "" + + private func register() { + let homeserver = self.homeserver.isEmpty ? "https://matrix.org" : self.homeserver + guard let homeserverURL = URL(homeserverString: homeserver) else { + // TODO: Handle error + print("Invalid homeserver URL '\(homeserver)'") + return + } + store.register(username: username, password: password, homeserver: homeserverURL) + } + + var body: some View { + RegistrationView(username: $username, + password: $password, + passwordConfirmation: $passwordConfirmation, + homeserver: $homeserver, + onRegister: register, + isRegistrationEnabled: isRegistrationEnabled) + } + + private func isRegistrationEnabled() -> Bool { + guard !username.isEmpty && !password.isEmpty else { return false } + guard password == passwordConfirmation else { return false } + let homeserver = self.homeserver.isEmpty ? "https://matrix.org" : self.homeserver + guard URL(string: homeserver) != nil else { return false } + return true + } +} + +struct RegistrationView: View { + @Binding var username: String + @Binding var password: String + @Binding var passwordConfirmation: String + @Binding var homeserver: String + + var onRegister: () -> Void + var isRegistrationEnabled: () -> Bool + + static var randomServerSuggestions = [ + "https://feneas.org", + "https://allmende.io", + "https://tchncs.de", + "https://fairydust.space", + ] + + var header: some View { + VStack { + Image(systemName: "person.3.fill") + .font(.title) + .foregroundColor(.accentColor) + Text(L10n.Registration.header) + .font(.headline) + } + .padding(.bottom) + } + + var mxidPreview: String? { + // TODO: This should ideally also try a well-known discovery like the login does. + switch (username, homeserver) { + case ("", _): + return nil + case (var user, ""): + user = user.replacingOccurrences(of: "@", with: "") + return "@\(user):matrix.org" + case (var user, var server): + user = user.replacingOccurrences(of: "@", with: "") + server = server.replacingOccurrences(of: "https://", with: "") + return "@\(user):\(server)" + } + } + + var form: some View { + VStack { + LoginFormTextField(placeholder: L10n.Login.Form.username, text: $username) + .padding(.horizontal) + .padding(.bottom) + + LoginFormTextField(placeholder: L10n.Login.Form.password, + text: $password, + isSecure: true) + .padding(.horizontal) + + LoginFormTextField(placeholder: L10n.Registration.confirmPassword, + text: $passwordConfirmation, + isSecure: true) + .padding(.horizontal) + .padding(.bottom) + + LoginFormTextField(placeholder: L10n.Login.Form.homeserver, + text: $homeserver, + buttonIcon: "shuffle", + buttonAction: { self.homeserver = Self.randomServerSuggestions.randomElement()! }) + .padding(.horizontal) + } + } + + var body: some View { + VStack { + Spacer() + header + + Spacer() + Text(L10n.Registration.explanation) + .font(.callout) + .padding(.horizontal) + Spacer() + + form + + Text(L10n.Registration.homeserverExplanation) + .font(.caption) + .foregroundColor(.gray) + .padding(.horizontal) + .fixedSize(horizontal: false, vertical: true) + + Button(action: { + self.onRegister() + }, label: { + VStack { + Text(L10n.Registration.register) + .font(.system(size: 18)) + .bold() + if mxidPreview != nil { + Text(mxidPreview!) + .font(.caption) + .bold() + } + } + + }) + .padding([.top, .bottom], 30) + .disabled(!isRegistrationEnabled()) + + Spacer() + } + .keyboardObserving() + } +} + +struct RegistrationView_Previews: PreviewProvider { + static var previews: some View { + RegistrationView(username: .constant(""), + password: .constant(""), + passwordConfirmation: .constant(""), + homeserver: .constant(""), + onRegister: { }, + isRegistrationEnabled: { true }) + .accentColor(.purple) + } +} diff --git a/Nio/Extensions/URL+Homeserver.swift b/Nio/Extensions/URL+Homeserver.swift new file mode 100644 index 00000000..34b17f76 --- /dev/null +++ b/Nio/Extensions/URL+Homeserver.swift @@ -0,0 +1,18 @@ +import Foundation + +extension URL { + init?(homeserverString: String) { + var homeserver = homeserverString + + // If there's no scheme at all, the URLComponents initializer below will think it's a path with no hostname. + if !homeserver.contains("//") { + homeserver = "https://\(homeserver)" + } + + var homeserverURLComponents = URLComponents(string: homeserver) + homeserverURLComponents?.scheme = "https" + + guard let homeserverURL = homeserverURLComponents?.url else { return nil } + self = homeserverURL + } +} diff --git a/Nio/Generated/Strings.swift b/Nio/Generated/Strings.swift index 1d528aa4..6f4bc610 100644 --- a/Nio/Generated/Strings.swift +++ b/Nio/Generated/Strings.swift @@ -194,6 +194,19 @@ internal enum L10n { } } + internal enum Registration { + /// Confirm password + internal static let confirmPassword = L10n.tr("Localizable", "registration.confirm-password") + /// Matrix is a decentralized network, like E-Mail, meaning there's no single server but many that talk to each other. You'll need an account on one of them to talk to other users. + internal static let explanation = L10n.tr("Localizable", "registration.explanation") + /// Register a new Matrix account + internal static let header = L10n.tr("Localizable", "registration.header") + /// You can use this to create an account on a specific Matrix server. Tap the shuffle button to get a random suggestion or leave it empty to create your account on matrix.org. + internal static let homeserverExplanation = L10n.tr("Localizable", "registration.homeserver-explanation") + /// Register + internal static let register = L10n.tr("Localizable", "registration.register") + } + internal enum Room { /// Not yet implemented internal static let attachmentPlaceholder = L10n.tr("Localizable", "room.attachment-placeholder") diff --git a/Nio/RootView.swift b/Nio/RootView.swift index d2247ed3..ba17b68f 100644 --- a/Nio/RootView.swift +++ b/Nio/RootView.swift @@ -10,7 +10,7 @@ struct RootView: View { RecentRoomsContainerView() .environment(\.userId, userId) // Can this ever be nil? And if so, what happens with the default fallback? - .environment(\.homeserver, (store.client?.homeserver.flatMap(URL.init)) ?? HomeserverKey.defaultValue) + .environment(\.homeserver, (store.client?.homeserver.flatMap(URL.init(string:))) ?? HomeserverKey.defaultValue) ) case .loggedOut: return AnyView( diff --git a/Nio/Supporting Files/en.lproj/Localizable.strings b/Nio/Supporting Files/en.lproj/Localizable.strings index 37bdd237..0ad5d301 100644 --- a/Nio/Supporting Files/en.lproj/Localizable.strings +++ b/Nio/Supporting Files/en.lproj/Localizable.strings @@ -9,6 +9,12 @@ "login.register-not-yet-implemented" = "Registering for new accounts is not yet implemented."; "login.failure-back-to-login" = "Back to Login"; +"registration.header" = "Register a new Matrix account"; +"registration.explanation" = "Matrix is a decentralized network, like E-Mail, meaning there's no single server but many that talk to each other. You'll need an account on one of them to talk to other users."; +"registration.confirm-password" = "Confirm password"; +"registration.homeserver-explanation" = "You can use this to create an account on a specific Matrix server. Tap the shuffle button to get a random suggestion or leave it empty to create your account on matrix.org."; +"registration.register" = "Register"; + "loading.1" = "🧑‍🎤 Reticulating splines"; "loading.2" = "🧑‍🏭 Discomfrobulating messages"; "loading.3" = "🧑‍🔧 Logging in"; From da610eafd73df7b7cb0bf23d6358b799494d3fa0 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 02:14:44 +0200 Subject: [PATCH 2/8] Fix login form padding --- Nio/Authentication/LoginView.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Nio/Authentication/LoginView.swift b/Nio/Authentication/LoginView.swift index f4f15792..287a87bb 100644 --- a/Nio/Authentication/LoginView.swift +++ b/Nio/Authentication/LoginView.swift @@ -134,21 +134,19 @@ struct LoginForm: View { LoginFormTextField(placeholder: L10n.Login.Form.username, text: $username, onEditingChanged: { _ in self.guessHomeserverURL() }) - .padding(.horizontal) LoginFormTextField(placeholder: L10n.Login.Form.password, text: $password, isSecure: true) - .padding(.horizontal) LoginFormTextField(placeholder: L10n.Login.Form.homeserver, text: $homeserver) - .padding(.horizontal) Text(L10n.Login.Form.homeserverOptionalExplanation) .font(.caption) .foregroundColor(.gray) } + .padding(.horizontal) } } From e73a553fe4e88c6e1132c0a3f11c67e51a65c4ca Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 02:17:39 +0200 Subject: [PATCH 3/8] Add Dutch and German translations --- Nio/Supporting Files/de.lproj/Localizable.strings | 6 ++++++ Nio/Supporting Files/nl.lproj/Localizable.strings | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Nio/Supporting Files/de.lproj/Localizable.strings b/Nio/Supporting Files/de.lproj/Localizable.strings index d72f2cf4..0c46f504 100644 --- a/Nio/Supporting Files/de.lproj/Localizable.strings +++ b/Nio/Supporting Files/de.lproj/Localizable.strings @@ -9,6 +9,12 @@ "login.register-not-yet-implemented" = "Registrierung für neue Konten ist noch nicht implementiert."; "login.failure-back-to-login" = "Zurück zur Anmeldung"; +"registration.header" = "Erstelle ein neues Matrix-Konto"; +"registration.explanation" = "Matrix ist ein dezentralisiertes Netzwerk, wie E-Mail, d.h. es gibt keinen einzelnen Server, sondern viele, die miteinander kommunizieren. Du brauchst ein Konto auf einem davon, um mit anderen Benutzern zu kommunizieren."; +"registration.confirm-password" = "Passwort bestätigen"; +"registration.homeserver-explanation" = "Du kannst dieses Feld verwenden, um ein Konto auf einem bestimmten Matrix-Server zu erstellen. Tippe auf den Shuffle-Button, um einen Zufallsvorschlag zu erhalten, oder lasse das Feld leer, um ein Konto auf matrix.org zu erstellen."; +"registration.register" = "Registrieren"; + "loading.1" = "🧑‍🎤 Die Matrix wird entschlüsselt"; "loading.2" = "🧑‍🏭 Nachrichten werden auseinander gefriemelt"; "loading.3" = "🧑‍🔧 Anmeldung läuft"; diff --git a/Nio/Supporting Files/nl.lproj/Localizable.strings b/Nio/Supporting Files/nl.lproj/Localizable.strings index fac8948d..ec9cb340 100644 --- a/Nio/Supporting Files/nl.lproj/Localizable.strings +++ b/Nio/Supporting Files/nl.lproj/Localizable.strings @@ -9,6 +9,12 @@ "login.register-not-yet-implemented" = "Registratie voor nieuwe accounts is nog niet geïmplementeerd."; "login.failure-back-to-login" = "Terug naar Login"; +"registration.header" = "Registreer een nieuwe Matrix account"; +"registration.explanation" = "Matrix is een gedecentraliseerd netwerk, zoals E-Mail, wat betekent dat er geen enkele server is, maar veel die met elkaar praten. Je hebt een account nodig op een van hen om met andere gebruikers te kunnen praten."; +"registration.confirm-password" = "Bevestig wachtwoord"; +"registration.homeserver-explanation" = "U kunt dit gebruiken om een account aan te maken op een specifieke Matrix-server. Tik op de shuffle-knop om een willekeurige suggestie te krijgen of laat het leeg staan om uw account aan te maken op matrix.org."; +"registration.register" = "Registreer"; + "loading.1" = "🧑‍🎤 De Matrix wordt gedecodeerd"; "loading.2" = "🧑‍🏭 Berichten worden verzameld"; "loading.3" = "🧑‍🔧 Inloggen"; From 85ace3ec90072d1832fe1e9801b09460e7706cbb Mon Sep 17 00:00:00 2001 From: stefanhofman Date: Fri, 1 May 2020 12:54:10 +0200 Subject: [PATCH 4/8] Update dutch language --- Nio/Supporting Files/nl.lproj/Localizable.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nio/Supporting Files/nl.lproj/Localizable.strings b/Nio/Supporting Files/nl.lproj/Localizable.strings index ec9cb340..eb1ccd8f 100644 --- a/Nio/Supporting Files/nl.lproj/Localizable.strings +++ b/Nio/Supporting Files/nl.lproj/Localizable.strings @@ -10,9 +10,9 @@ "login.failure-back-to-login" = "Terug naar Login"; "registration.header" = "Registreer een nieuwe Matrix account"; -"registration.explanation" = "Matrix is een gedecentraliseerd netwerk, zoals E-Mail, wat betekent dat er geen enkele server is, maar veel die met elkaar praten. Je hebt een account nodig op een van hen om met andere gebruikers te kunnen praten."; +"registration.explanation" = "Matrix is een gedecentraliseerd netwerk, zoals E-Mail, wat betekent dat er niet één enkele server is, maar meerdere die met elkaar praten. Je hebt een account nodig op één van hen om met andere gebruikers te kunnen praten."; "registration.confirm-password" = "Bevestig wachtwoord"; -"registration.homeserver-explanation" = "U kunt dit gebruiken om een account aan te maken op een specifieke Matrix-server. Tik op de shuffle-knop om een willekeurige suggestie te krijgen of laat het leeg staan om uw account aan te maken op matrix.org."; +"registration.homeserver-explanation" = "U kunt dit gebruiken om een account aan te maken op een specifieke Matrix-server. Tik op de shuffle-knop om een willekeurige suggestie te krijgen of laat het leeg om uw account aan te maken op matrix.org."; "registration.register" = "Registreer"; "loading.1" = "🧑‍🎤 De Matrix wordt gedecodeerd"; From 05e7192de77803b25e0ec1de13744b10070b4a7d Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 11:54:26 +0200 Subject: [PATCH 5/8] Remove registration placeholder string --- Nio/Generated/Strings.swift | 2 -- Nio/Supporting Files/de.lproj/Localizable.strings | 1 - Nio/Supporting Files/en.lproj/Localizable.strings | 1 - Nio/Supporting Files/nl.lproj/Localizable.strings | 1 - 4 files changed, 5 deletions(-) diff --git a/Nio/Generated/Strings.swift b/Nio/Generated/Strings.swift index 6f4bc610..b62520e6 100644 --- a/Nio/Generated/Strings.swift +++ b/Nio/Generated/Strings.swift @@ -146,8 +146,6 @@ internal enum L10n { internal static let failureBackToLogin = L10n.tr("Localizable", "login.failure-back-to-login") /// Don't have an account yet? internal static let openRegistrationPrompt = L10n.tr("Localizable", "login.open-registration-prompt") - /// Registering for new accounts is not yet implemented. - internal static let registerNotYetImplemented = L10n.tr("Localizable", "login.register-not-yet-implemented") /// Sign in internal static let signIn = L10n.tr("Localizable", "login.sign-in") /// 👋 Welcome to diff --git a/Nio/Supporting Files/de.lproj/Localizable.strings b/Nio/Supporting Files/de.lproj/Localizable.strings index 0c46f504..d81c48c0 100644 --- a/Nio/Supporting Files/de.lproj/Localizable.strings +++ b/Nio/Supporting Files/de.lproj/Localizable.strings @@ -6,7 +6,6 @@ "login.form.homeserver-optional-explanation" = "Der Homeserver ist optional falls du auf matrix.org registriert bist."; "login.sign-in" = "Anmelden"; "login.open-registration-prompt" = "Du hast noch kein Benutzerkonto?"; -"login.register-not-yet-implemented" = "Registrierung für neue Konten ist noch nicht implementiert."; "login.failure-back-to-login" = "Zurück zur Anmeldung"; "registration.header" = "Erstelle ein neues Matrix-Konto"; diff --git a/Nio/Supporting Files/en.lproj/Localizable.strings b/Nio/Supporting Files/en.lproj/Localizable.strings index 0ad5d301..238c7a80 100644 --- a/Nio/Supporting Files/en.lproj/Localizable.strings +++ b/Nio/Supporting Files/en.lproj/Localizable.strings @@ -6,7 +6,6 @@ "login.form.homeserver-optional-explanation" = "Homeserver is optional if you're using matrix.org."; "login.sign-in" = "Sign in"; "login.open-registration-prompt" = "Don't have an account yet?"; -"login.register-not-yet-implemented" = "Registering for new accounts is not yet implemented."; "login.failure-back-to-login" = "Back to Login"; "registration.header" = "Register a new Matrix account"; diff --git a/Nio/Supporting Files/nl.lproj/Localizable.strings b/Nio/Supporting Files/nl.lproj/Localizable.strings index eb1ccd8f..876e65c2 100644 --- a/Nio/Supporting Files/nl.lproj/Localizable.strings +++ b/Nio/Supporting Files/nl.lproj/Localizable.strings @@ -6,7 +6,6 @@ "login.form.homeserver-optional-explanation" = "Homeserver is optioneel als je matrix.org gebruikt."; "login.sign-in" = "Log in"; "login.open-registration-prompt" = "Nog geen account?"; -"login.register-not-yet-implemented" = "Registratie voor nieuwe accounts is nog niet geïmplementeerd."; "login.failure-back-to-login" = "Terug naar Login"; "registration.header" = "Registreer een nieuwe Matrix account"; From c486ea0ba086ad55238dfd1e887fb876f4075b19 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 13:31:39 +0200 Subject: [PATCH 6/8] Add Chinese registration strings (from DeepL) --- Nio/Supporting Files/zh-Hans.lproj/Localizable.strings | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings b/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings index b93e35b9..501b3365 100644 --- a/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings +++ b/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings @@ -6,9 +6,14 @@ "login.form.homeserver-optional-explanation" = "若是matrix.org,则不必填写主服务器。"; "login.sign-in" = "登录"; "login.open-registration-prompt" = "还没有账户?"; -"login.register-not-yet-implemented" = "新账户注册功能尚未被实现。"; "login.failure-back-to-login" = "返回登录页面"; +"registration.header" = "注册一个新的矩阵账户"; +"registration.explanation" = "矩阵是一个分散式的网络,就像E-Mail一样,意味着没有单一的服务器,而是有很多服务器相互之间的对话。你需要在其中一个服务器上有一个账号才能与其他用户对话。"; +"registration.confirm-password" = "确认密码"; +"registration.homeserver-explanation" = "你可以用它来在特定的矩阵服务器上创建一个账户。点击洗牌按钮获得随机建议,或留空以创建您在matrix.org上的账户。"; +"registration.register" = "注册"; + "loading.1" = "🧑‍🎤 少女祈祷中"; "loading.2" = "🧑‍🏭 正在给长者+1s"; "loading.3" = "🧑‍🔧 登录中"; From 8396f7cfa64fb5ae86d620735aea5f79bb05061b Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Fri, 1 May 2020 14:51:13 +0200 Subject: [PATCH 7/8] Shuffle btn shouldn't give the same result twice --- Nio/Authentication/RegistrationView.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Nio/Authentication/RegistrationView.swift b/Nio/Authentication/RegistrationView.swift index 16316e66..8401bb31 100644 --- a/Nio/Authentication/RegistrationView.swift +++ b/Nio/Authentication/RegistrationView.swift @@ -98,8 +98,13 @@ struct RegistrationView: View { LoginFormTextField(placeholder: L10n.Login.Form.homeserver, text: $homeserver, buttonIcon: "shuffle", - buttonAction: { self.homeserver = Self.randomServerSuggestions.randomElement()! }) - .padding(.horizontal) + buttonAction: { + self.homeserver = Self.randomServerSuggestions + .filter { $0 != self.homeserver } + .randomElement()! + } + ) + .padding(.horizontal) } } From a5a64dfb0afbf3f676a627240022925de6a65bd7 Mon Sep 17 00:00:00 2001 From: CaptainYukinoshitaHachiman Date: Fri, 1 May 2020 22:27:51 +0800 Subject: [PATCH 8/8] Revise Translation of Registration in Simplified Chinese --- Nio/Supporting Files/zh-Hans.lproj/Localizable.strings | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings b/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings index 501b3365..16d369df 100644 --- a/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings +++ b/Nio/Supporting Files/zh-Hans.lproj/Localizable.strings @@ -8,10 +8,10 @@ "login.open-registration-prompt" = "还没有账户?"; "login.failure-back-to-login" = "返回登录页面"; -"registration.header" = "注册一个新的矩阵账户"; -"registration.explanation" = "矩阵是一个分散式的网络,就像E-Mail一样,意味着没有单一的服务器,而是有很多服务器相互之间的对话。你需要在其中一个服务器上有一个账号才能与其他用户对话。"; +"registration.header" = "注册一个新的Matrix账户"; +"registration.explanation" = "Matrix就像电子邮箱一样,是一个分散式的网络。这意味着没有一个唯一的服务器,而是有很多服务器在相互对话。你只须在其中一个服务器上拥有账号就能与其他用户交流。"; "registration.confirm-password" = "确认密码"; -"registration.homeserver-explanation" = "你可以用它来在特定的矩阵服务器上创建一个账户。点击洗牌按钮获得随机建议,或留空以创建您在matrix.org上的账户。"; +"registration.homeserver-explanation" = "你可以用它来在特定的Matrix服务器上创建账户。点击􀊝来获得随机的服务器建议,或留空以在matrix.org上创建账户。"; "registration.register" = "注册"; "loading.1" = "🧑‍🎤 少女祈祷中"; @@ -32,7 +32,7 @@ "room.attachment-placeholder" = "此特性尚未被实现。"; "room.remove.title" = "删除?"; "room.remove.message" = "你确定要删除这条消息吗?"; -"room.remove.action" = "删除";; +"room.remove.action" = "删除"; "composer.new-message" = "新信息..."; "composer.edit-message" = "编辑信息:";