diff --git a/client/android/app/src/main/java/com/liketrek/trek/MainActivity.java b/client/android/app/src/main/java/com/liketrek/trek/MainActivity.java index 9f1ad1c1d..836103180 100644 --- a/client/android/app/src/main/java/com/liketrek/trek/MainActivity.java +++ b/client/android/app/src/main/java/com/liketrek/trek/MainActivity.java @@ -1,11 +1,20 @@ package com.liketrek.trek; import android.content.Intent; +import android.graphics.drawable.GradientDrawable; import android.os.Bundle; +import android.util.TypedValue; +import android.view.Gravity; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.Menu; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; import com.getcapacitor.BridgeActivity; import com.liketrek.trek.screen.TrekKeyCommands; import com.liketrek.trek.tiles.TrekTileStore; @@ -37,7 +46,11 @@ public class MainActivity extends BridgeActivity { // scroller natively. It scales the whole view, top bar and bottom dock included. bridge.getWebView().setOverScrollMode(View.OVER_SCROLL_NEVER); // Set before any WebView callback can run: they are posted to this thread. - if (onServer) bridge.setWebViewClient(new TrekWebViewClient(bridge, this::showAddressScreen, TrekServerStore.getSignInHosts(this))); + if (onServer) { + bridge.setWebViewClient( + new TrekWebViewClient(bridge, this::showAddressScreen, this::showSignInBar, TrekServerStore.getSignInHosts(this)) + ); + } TrekSharedFiles.receive(this, getIntent()); // Android 15's widget picker draws the widgets live from a sample trip. TrekWidgetUpdater.publishPreviews(this); @@ -69,6 +82,88 @@ public class MainActivity extends BridgeActivity { private boolean leavingServer; + private View signInBar; + /** Whether the page is a proxy's sign-in page, and whether the keyboard is up over it. */ + private boolean onSignInPage; + private boolean keyboardUp; + + /** + * Over a proxy's sign-in page (Pangolin, authentik, ...): the server's UI is + * not there, so nothing else would lead back to the address screen, and a + * passkey cannot work in a web view for a domain the app is not tied to. + * The bar says so and offers to change the server. + */ + private void showSignInBar(boolean show) { + runOnUiThread(() -> { + onSignInPage = show; + if (show && signInBar == null) signInBar = buildSignInBar(); + updateSignInBar(); + }); + } + + /** Out of the way while the keyboard is up: the form needs the room, and the bar is there again once it closes. */ + private void updateSignInBar() { + if (signInBar != null) signInBar.setVisibility(onSignInPage && !keyboardUp ? View.VISIBLE : View.GONE); + } + + private View buildSignInBar() { + float dp = getResources().getDisplayMetrics().density; + LinearLayout bar = new LinearLayout(this); + bar.setOrientation(LinearLayout.HORIZONTAL); + bar.setGravity(Gravity.CENTER_VERTICAL); + bar.setPadding(Math.round(14 * dp), Math.round(10 * dp), Math.round(8 * dp), Math.round(10 * dp)); + GradientDrawable background = new GradientDrawable(); + background.setColor(0xF2111827); + background.setCornerRadius(16 * dp); + bar.setBackground(background); + bar.setElevation(6 * dp); + + TextView hint = new TextView(this); + hint.setText(TrekServerStore.signInHintLabel(this)); + hint.setTextColor(0xFFE5E7EB); + hint.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12.5f); + bar.addView(hint, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); + + Button change = new Button(this, null, android.R.attr.borderlessButtonStyle); + change.setText(TrekServerStore.changeServerLabel(this)); + change.setAllCaps(false); + change.setTextColor(0xFFFFFFFF); + change.setOnClickListener((v) -> { + TrekServerStore.setUrl(this, null); + TrekServerStore.markRestart(this); + recreate(); + }); + bar.addView(change, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + int margin = Math.round(12 * dp); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM + ); + params.setMargins(margin, 0, margin, margin); + FrameLayout root = findViewById(android.R.id.content); + root.addView(bar, params); + // Above the gesture bar, and above the keyboard while it is up. + bar.setOnApplyWindowInsetsListener((v, insets) -> { + boolean modern = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R; + int bottom = modern ? insets.getInsets(WindowInsets.Type.systemBars()).bottom : insets.getStableInsetBottom(); + boolean imeVisible = modern ? insets.isVisible(WindowInsets.Type.ime()) : insets.getSystemWindowInsetBottom() > bottom * 3; + if (imeVisible != keyboardUp) { + keyboardUp = imeVisible; + updateSignInBar(); + } + FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) v.getLayoutParams(); + if (p.bottomMargin != margin + bottom) { + p.bottomMargin = margin + bottom; + v.setLayoutParams(p); + } + return insets; + }); + bar.requestApplyInsets(); + return bar; + } + /** * The server did not load: back to the address screen, which offers retry * and change. A fresh task rather than recreate(), because this usually diff --git a/client/android/app/src/main/java/com/liketrek/trek/TrekServerStore.java b/client/android/app/src/main/java/com/liketrek/trek/TrekServerStore.java index 3bfa22911..1aab11914 100644 --- a/client/android/app/src/main/java/com/liketrek/trek/TrekServerStore.java +++ b/client/android/app/src/main/java/com/liketrek/trek/TrekServerStore.java @@ -24,6 +24,8 @@ final class TrekServerStore { private static final String KEY_URL = "server_url"; private static final String KEY_PREVIOUS_URL = "previous_server_url"; private static final String KEY_SERVER_CHECK = "server_check"; + private static final String KEY_LABEL_SIGN_IN_HINT = "label_sign_in_hint"; + private static final String KEY_LABEL_CHANGE_SERVER = "label_change_server"; /** * The server API this build needs (MIN_NATIVE_API in serverAddress.ts). A @@ -76,6 +78,29 @@ final class TrekServerStore { editor.commit(); } + /** + * The words of the bar over a proxy's sign-in page, in the user's language, + * as the address screen passed them (setServer's `labels`): the server's UI + * is not there to say them. Null keeps the English fallback. + */ + static void setLabels(Context context, String signInHint, String changeServer) { + SharedPreferences.Editor editor = prefs(context).edit(); + if (signInHint != null) editor.putString(KEY_LABEL_SIGN_IN_HINT, signInHint); + if (changeServer != null) editor.putString(KEY_LABEL_CHANGE_SERVER, changeServer); + editor.commit(); + } + + static String signInHintLabel(Context context) { + return prefs(context).getString( + KEY_LABEL_SIGN_IN_HINT, + "Passkeys and security keys do not work on this page in the app. Sign in with a password, or let the app through your proxy." + ); + } + + static String changeServerLabel(Context context) { + return prefs(context).getString(KEY_LABEL_CHANGE_SERVER, "Change server"); + } + /** The server an update asked to check again, until a server is set. */ static String getPreviousUrl(Context context) { return prefs(context).getString(KEY_PREVIOUS_URL, null); diff --git a/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java b/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java index dedd6a10d..6cb5bede3 100644 --- a/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java +++ b/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java @@ -326,6 +326,10 @@ public class TrekShellPlugin extends Plugin { return; } TrekServerStore.setServer(getContext(), url, signInHostsOf(call)); + JSObject labels = call.getObject("labels"); + if (labels != null) { + TrekServerStore.setLabels(getContext(), labels.getString("signInHint"), labels.getString("changeServer")); + } // A TREK Cloud purchase: the code that signs the buyer in, opened once after the first page. String welcome = call.getString("welcome"); TrekServerStore.setWelcome(getContext(), welcome != null && welcome.matches("[A-Za-z0-9_-]{43}") ? welcome : null); diff --git a/client/android/app/src/main/java/com/liketrek/trek/TrekWebViewClient.java b/client/android/app/src/main/java/com/liketrek/trek/TrekWebViewClient.java index 96a029a07..bd50636f4 100644 --- a/client/android/app/src/main/java/com/liketrek/trek/TrekWebViewClient.java +++ b/client/android/app/src/main/java/com/liketrek/trek/TrekWebViewClient.java @@ -12,6 +12,7 @@ import com.liketrek.trek.clientcert.TrekClientIdentity; import java.util.HashSet; import java.util.Locale; import java.util.Set; +import java.util.function.Consumer; /** * Used while the WebView shows the user's server. When that server cannot be @@ -24,13 +25,16 @@ import java.util.Set; class TrekWebViewClient extends BridgeWebViewClient { private final Runnable onUnreachable; + /** Told whether the page now shown is a proxy's sign-in page, for the bar with the way out. */ + private final Consumer onSignInPage; private final Set signInHosts; /** A page of the server has shown since launch; from then on no sign-in host is learned (SignInRedirect). */ private boolean serverShown; - TrekWebViewClient(Bridge bridge, Runnable onUnreachable, Set signInHosts) { + TrekWebViewClient(Bridge bridge, Runnable onUnreachable, Consumer onSignInPage, Set signInHosts) { super(bridge); this.onUnreachable = onUnreachable; + this.onSignInPage = onSignInPage; this.signInHosts = new HashSet<>(signInHosts); } @@ -61,6 +65,13 @@ class TrekWebViewClient extends BridgeWebViewClient { return super.shouldOverrideUrlLoading(view, request); } + /** Every page the main frame shows, pushState included: is it a proxy's sign-in page? */ + @Override + public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { + super.doUpdateVisitedHistory(view, url, isReload); + onSignInPage.accept(url != null && isSignInHost(Uri.parse(url))); + } + /** After the server's first page, a pending welcome code opens the welcome page once (TrekServerStore.takeWelcomeUrl). */ @Override public void onPageFinished(WebView view, String url) { diff --git a/client/ios/App/App/TrekBridgeViewController.swift b/client/ios/App/App/TrekBridgeViewController.swift index 3f6434c74..7ef4ab3fe 100644 --- a/client/ios/App/App/TrekBridgeViewController.swift +++ b/client/ios/App/App/TrekBridgeViewController.swift @@ -45,6 +45,7 @@ class TrekBridgeViewController: CAPBridgeViewController { webView.navigationDelegate = proxy } guardBridgeMessages() + watchSignInPages() bridge?.registerPluginInstance(TrekShellPlugin()) // WebKit's own swipe back slides away a screenshot of the whole page, // closes no sheet and takes every drag near the edge from the UI. The UI @@ -56,6 +57,117 @@ class TrekBridgeViewController: CAPBridgeViewController { webView?.allowsLinkPreview = false } + // MARK: - A proxy's sign-in page + + /// Over a proxy's sign-in page (Pangolin, authentik, ...) the server's UI is + /// not there, so nothing else leads back to the address screen, and a passkey + /// cannot work in a web view for a domain the app is not tied to. A bar says + /// so and offers to change the server; the keyboard gets room on that page, + /// which TREK's own script makes everywhere else (native/keepFieldInView.ts). + private var signInBar: UIView? + private var urlObservation: NSKeyValueObservation? + private var onSignInPage = false + private var keyboardUp = false + + private func watchSignInPages() { + urlObservation = webView?.observe(\.url, options: [.initial, .new]) { [weak self] webView, _ in + let signIn = TrekServerStore.isSignInHost(webView.url) + DispatchQueue.main.async { self?.signInPageChanged(signIn) } + } + let center = NotificationCenter.default + center.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) + center.addObserver(self, selector: #selector(keyboardDidShow(_:)), name: UIResponder.keyboardDidShowNotification, object: nil) + center.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + private func signInPageChanged(_ signIn: Bool) { + onSignInPage = signIn + if signIn && signInBar == nil { signInBar = makeSignInBar() } + updateSignInBar() + } + + /// Out of the way while the keyboard is up: the form needs the room. + private func updateSignInBar() { + signInBar?.isHidden = !(onSignInPage && !keyboardUp) + } + + @objc private func keyboardWillShow() { + keyboardUp = true + updateSignInBar() + } + + /// The keyboard plugin sizes the body to the visible area and resets the + /// scroll inset on every frame change, which left a foreign page with no way + /// to scroll a button under the keyboard into view. After its last change, + /// the page gets its height back and as much room below as the keyboard takes. + @objc private func keyboardDidShow(_ note: Notification) { + guard onSignInPage, let frame = note.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } + let room = max(0, frame.height - view.safeAreaInsets.bottom) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in + guard let self, self.onSignInPage, self.keyboardUp, let webView = self.webView else { return } + webView.evaluateJavaScript("document.body.style.height = ''", completionHandler: nil) + webView.scrollView.contentInset.bottom = room + webView.scrollView.verticalScrollIndicatorInsets.bottom = room + } + } + + @objc private func keyboardWillHide() { + keyboardUp = false + updateSignInBar() + guard onSignInPage, let webView else { return } + webView.scrollView.contentInset.bottom = 0 + webView.scrollView.verticalScrollIndicatorInsets.bottom = 0 + } + + private func makeSignInBar() -> UIView { + let labels = TrekServerStore.labels + let bar = UIView() + bar.backgroundColor = UIColor(red: 17 / 255, green: 24 / 255, blue: 39 / 255, alpha: 0.95) + bar.layer.cornerRadius = 16 + bar.layer.cornerCurve = .continuous + bar.translatesAutoresizingMaskIntoConstraints = false + + let hint = UILabel() + hint.text = labels["signInHint"] + ?? "Passkeys and security keys do not work on this page in the app. Sign in with a password, or let the app through your proxy." + hint.textColor = UIColor(white: 0.9, alpha: 1) + hint.font = .preferredFont(forTextStyle: .footnote) + hint.adjustsFontForContentSizeCategory = true + hint.numberOfLines = 0 + hint.translatesAutoresizingMaskIntoConstraints = false + + let change = UIButton(type: .system) + change.setTitle(labels["changeServer"] ?? "Change server", for: .normal) + change.setTitleColor(.white, for: .normal) + change.titleLabel?.font = .systemFont(ofSize: 15, weight: .semibold) + change.addTarget(self, action: #selector(changeServerFromSignIn), for: .touchUpInside) + change.translatesAutoresizingMaskIntoConstraints = false + change.setContentHuggingPriority(.required, for: .horizontal) + change.setContentCompressionResistancePriority(.required, for: .horizontal) + + bar.addSubview(hint) + bar.addSubview(change) + view.addSubview(bar) + NSLayoutConstraint.activate([ + bar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 12), + bar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -12), + bar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8), + hint.leadingAnchor.constraint(equalTo: bar.leadingAnchor, constant: 14), + hint.topAnchor.constraint(equalTo: bar.topAnchor, constant: 10), + hint.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -10), + change.leadingAnchor.constraint(equalTo: hint.trailingAnchor, constant: 8), + change.trailingAnchor.constraint(equalTo: bar.trailingAnchor, constant: -12), + change.centerYAnchor.constraint(equalTo: bar.centerYAnchor), + ]) + return bar + } + + /// What resetServer does from the UI: forget the server, back to the address screen. + @objc private func changeServerFromSignIn() { + TrekServerStore.url = nil + view.window?.rootViewController = TrekBridgeViewController() + } + /// Kept alive here: the content controller holds it, but only for as long as it is registered. private var messageGuard: TrekBridgeMessageGuard? @@ -162,6 +274,16 @@ enum TrekServerStore { private static let checkedKey = "TrekServerCheck" + private static let labelsKey = "TrekShellLabels" + + /// The words of the bar over a proxy's sign-in page, in the user's language, + /// as the address screen passed them (setServer's `labels`): the server's UI + /// is not there to say them. Empty keeps the English fallback. + static var labels: [String: String] { + get { UserDefaults.standard.dictionary(forKey: labelsKey) as? [String: String] ?? [:] } + set { UserDefaults.standard.set(newValue, forKey: labelsKey) } + } + /// The server API this build needs (MIN_NATIVE_API in serverAddress.ts). /// A server remembered under a lower value is checked once more: the first /// launch after an update that raised it puts the address screen back up, diff --git a/client/ios/App/App/TrekShellPlugin.swift b/client/ios/App/App/TrekShellPlugin.swift index 931e8b2be..435261af0 100644 --- a/client/ios/App/App/TrekShellPlugin.swift +++ b/client/ios/App/App/TrekShellPlugin.swift @@ -360,6 +360,8 @@ public class TrekShellPlugin: CAPPlugin, CAPBridgedPlugin, ASWebAuthenticationPr return } TrekServerStore.url = raw + let labels = (call.getObject("labels") ?? [:]).compactMapValues { $0 as? String } + if !labels.isEmpty { TrekServerStore.labels = labels } // After the URL: setting it clears the hosts of the server before. let hosts = (call.getArray("signInHosts", String.self) ?? []).filter { $0.range(of: "^[A-Za-z0-9.-]+(:[0-9]+)?$", options: .regularExpression) != nil } TrekServerStore.signInHosts = Set(hosts.map { $0.lowercased() }) diff --git a/client/native-shell/main.ts b/client/native-shell/main.ts index 72eec1d12..1d8bf45b4 100644 --- a/client/native-shell/main.ts +++ b/client/native-shell/main.ts @@ -1206,6 +1206,7 @@ async function connected(url: string, signInHosts: string[] = [], welcome?: stri await new Promise((r) => setTimeout(r, 700)); await TrekShell.setServer({ url, + labels: { signInHint: t('native.signInBar.hint'), changeServer: t('native.changeServer') }, ...(signInHosts.length ? { signInHosts } : {}), ...(welcome ? { welcome } : {}), }); diff --git a/client/src/native/trekShell.ts b/client/src/native/trekShell.ts index c5939aeec..b341a767b 100644 --- a/client/src/native/trekShell.ts +++ b/client/src/native/trekShell.ts @@ -26,8 +26,15 @@ export interface TrekShellPlugin { * `welcome` is the one-time code of a TREK Cloud purchase: the first load * opens the server's welcome page with it, which signs the buyer in. A build * without this option ignores it and opens the server as usual. + * `labels` are the words of the bar the app shows over a proxy's sign-in page, + * in the user's language: the server's UI is not there to say them. */ - setServer(options: { url: string; signInHosts?: string[]; welcome?: string }): Promise; + setServer(options: { + url: string; + signInHosts?: string[]; + welcome?: string; + labels?: { signInHint: string; changeServer: string }; + }): Promise; /** Forgets the server and returns to the address screen. */ resetServer(): Promise; /** Loads the remembered server again, after it could not be reached. */ diff --git a/shared/src/i18n/ar/native.ts b/shared/src/i18n/ar/native.ts index f65d55009..dec3f6425 100644 --- a/shared/src/i18n/ar/native.ts +++ b/shared/src/i18n/ar/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'تعذّر على TREK الوصول إلى {server}. تحقق من اتصالك ثم حاول مرة أخرى.', 'native.offline.retry': 'إعادة المحاولة', 'native.changeServer': 'تغيير الخادم', + 'native.signInBar.hint': + 'لا تعمل مفاتيح المرور ومفاتيح الأمان على هذه الصفحة داخل التطبيق. سجّل الدخول بكلمة مرور، أو اسمح للتطبيق بتجاوز الوكيل.', 'native.login.failed': 'تعذّر تسجيل الدخول. يرجى المحاولة مرة أخرى.', 'native.handoff.title': 'المتابعة في التطبيق', 'native.handoff.hint': 'لقد سجّلت الدخول باسم {name}. افتح تطبيق TREK لإكمال تسجيل الدخول هناك.', diff --git a/shared/src/i18n/az/native.ts b/shared/src/i18n/az/native.ts index 5006ba644..bacae704b 100644 --- a/shared/src/i18n/az/native.ts +++ b/shared/src/i18n/az/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK {server} ünvanına çata bilmədi. Bağlantınızı yoxlayın və yenidən cəhd edin.', 'native.offline.retry': 'Yenidən cəhd et', 'native.changeServer': 'Serveri dəyiş', + 'native.signInBar.hint': + 'Passkey və təhlükəsizlik açarları tətbiqdə bu səhifədə işləmir. Parolla daxil olun və ya tətbiqi proksinizdən keçirin.', 'native.login.failed': 'Daxil olmaq alınmadı. Zəhmət olmasa, yenidən cəhd edin.', 'native.handoff.title': 'Tətbiqdə davam edin', 'native.handoff.hint': 'Siz {name} kimi daxil olmusunuz. Girişi orada tamamlamaq üçün TREK tətbiqini açın.', diff --git a/shared/src/i18n/br/native.ts b/shared/src/i18n/br/native.ts index b75ee0dda..5fa92d9b8 100644 --- a/shared/src/i18n/br/native.ts +++ b/shared/src/i18n/br/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'O TREK não conseguiu acessar {server}. Verifique sua conexão e tente novamente.', 'native.offline.retry': 'Tentar novamente', 'native.changeServer': 'Trocar de servidor', + 'native.signInBar.hint': + 'Passkeys e chaves de segurança não funcionam nesta página no app. Entre com uma senha ou libere o app no seu proxy.', 'native.login.failed': 'Não foi possível entrar. Tente novamente.', 'native.handoff.title': 'Continuar no app', 'native.handoff.hint': 'Você entrou como {name}. Abra o app TREK para concluir o login por lá.', diff --git a/shared/src/i18n/ca/native.ts b/shared/src/i18n/ca/native.ts index 7cd8fa6ff..fefea110a 100644 --- a/shared/src/i18n/ca/native.ts +++ b/shared/src/i18n/ca/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK no ha pogut connectar amb {server}. Comprova la connexió i torna-ho a provar.', 'native.offline.retry': 'Torna-ho a provar', 'native.changeServer': 'Canvia de servidor', + 'native.signInBar.hint': + 'Les claus d’accés i les claus de seguretat no funcionen en aquesta pàgina dins l’app. Inicia la sessió amb una contrasenya o deixa passar l’app pel teu proxy.', 'native.login.failed': 'No s’ha pogut iniciar la sessió. Torna-ho a provar.', 'native.handoff.title': 'Continua a l’app', 'native.handoff.hint': 'Has iniciat la sessió com a {name}. Obre l’app de TREK per acabar d’iniciar-hi la sessió.', diff --git a/shared/src/i18n/cs/native.ts b/shared/src/i18n/cs/native.ts index ff7a3700b..ced69ec2b 100644 --- a/shared/src/i18n/cs/native.ts +++ b/shared/src/i18n/cs/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK se nepodařilo spojit s {server}. Zkontrolujte připojení a zkuste to znovu.', 'native.offline.retry': 'Zkusit znovu', 'native.changeServer': 'Změnit server', + 'native.signInBar.hint': + 'Přístupové klíče a bezpečnostní klíče na této stránce v aplikaci nefungují. Přihlaste se heslem, nebo aplikaci propusťte přes svůj proxy.', 'native.login.failed': 'Přihlášení se nezdařilo. Zkuste to prosím znovu.', 'native.handoff.title': 'Pokračovat v aplikaci', 'native.handoff.hint': 'Jste přihlášeni jako {name}. Otevřete aplikaci TREK a dokončete přihlášení v ní.', diff --git a/shared/src/i18n/de/native.ts b/shared/src/i18n/de/native.ts index a1efd1855..69d677c54 100644 --- a/shared/src/i18n/de/native.ts +++ b/shared/src/i18n/de/native.ts @@ -20,6 +20,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK konnte {server} nicht erreichen. Prüfe deine Verbindung und versuche es erneut.', 'native.offline.retry': 'Erneut versuchen', 'native.changeServer': 'Server wechseln', + 'native.signInBar.hint': + 'Passkeys und Sicherheitsschlüssel funktionieren auf dieser Seite in der App nicht. Melde dich mit Passwort an oder lass die App an deinem Proxy vorbei.', 'native.login.failed': 'Die Anmeldung hat nicht geklappt. Bitte versuche es erneut.', 'native.handoff.title': 'Weiter in der App', 'native.handoff.hint': 'Du bist als {name} angemeldet. Öffne die TREK-App, um die Anmeldung dort abzuschließen.', diff --git a/shared/src/i18n/en/native.ts b/shared/src/i18n/en/native.ts index ce6d176c9..fccf8fc37 100644 --- a/shared/src/i18n/en/native.ts +++ b/shared/src/i18n/en/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK could not reach {server}. Check your connection and try again.', 'native.offline.retry': 'Try again', 'native.changeServer': 'Change server', + 'native.signInBar.hint': + 'Passkeys and security keys do not work on this page in the app. Sign in with a password, or let the app through your proxy.', 'native.login.failed': 'Signing in did not work. Please try again.', 'native.handoff.title': 'Continue in the app', 'native.handoff.hint': 'You are signed in as {name}. Open the TREK app to finish signing in there.', diff --git a/shared/src/i18n/es/native.ts b/shared/src/i18n/es/native.ts index 083855abd..bf2187bda 100644 --- a/shared/src/i18n/es/native.ts +++ b/shared/src/i18n/es/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK no pudo conectar con {server}. Revisa tu conexión e inténtalo de nuevo.', 'native.offline.retry': 'Reintentar', 'native.changeServer': 'Cambiar de servidor', + 'native.signInBar.hint': + 'Las llaves de acceso y las llaves de seguridad no funcionan en esta página dentro de la app. Inicia sesión con una contraseña o deja pasar la app por tu proxy.', 'native.login.failed': 'No se pudo iniciar sesión. Inténtalo de nuevo.', 'native.handoff.title': 'Continuar en la app', 'native.handoff.hint': 'Has iniciado sesión como {name}. Abre la app de TREK para terminar de iniciar sesión allí.', diff --git a/shared/src/i18n/et/native.ts b/shared/src/i18n/et/native.ts index 0a0576d55..e064c8f2a 100644 --- a/shared/src/i18n/et/native.ts +++ b/shared/src/i18n/et/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK ei jõudnud serverini {server}. Kontrolli oma ühendust ja proovi uuesti.', 'native.offline.retry': 'Proovi uuesti', 'native.changeServer': 'Vaheta serverit', + 'native.signInBar.hint': + 'Pääsuvõtmed ja turvavõtmed ei tööta sellel lehel rakenduses. Logi sisse parooliga või lase rakendus oma puhverserverist läbi.', 'native.login.failed': 'Sisselogimine ei õnnestunud. Palun proovi uuesti.', 'native.handoff.title': 'Jätka rakenduses', 'native.handoff.hint': 'Oled sisse logitud kui {name}. Ava TREKi rakendus, et sisselogimine seal lõpetada.', diff --git a/shared/src/i18n/fr/native.ts b/shared/src/i18n/fr/native.ts index 64e717822..3037057b9 100644 --- a/shared/src/i18n/fr/native.ts +++ b/shared/src/i18n/fr/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK n’a pas pu joindre {server}. Vérifiez votre connexion et réessayez.', 'native.offline.retry': 'Réessayer', 'native.changeServer': 'Changer de serveur', + 'native.signInBar.hint': + 'Les clés d’accès et les clés de sécurité ne fonctionnent pas sur cette page dans l’application. Connectez-vous avec un mot de passe ou laissez passer l’application par votre proxy.', 'native.login.failed': 'La connexion n’a pas abouti. Veuillez réessayer.', 'native.handoff.title': 'Continuer dans l’application', 'native.handoff.hint': diff --git a/shared/src/i18n/gr/native.ts b/shared/src/i18n/gr/native.ts index 6d3ecc35b..30e4923b5 100644 --- a/shared/src/i18n/gr/native.ts +++ b/shared/src/i18n/gr/native.ts @@ -20,6 +20,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'Το TREK δεν μπόρεσε να συνδεθεί στο {server}. Ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά.', 'native.offline.retry': 'Δοκιμάστε ξανά', 'native.changeServer': 'Αλλαγή διακομιστή', + 'native.signInBar.hint': + 'Τα passkeys και τα κλειδιά ασφαλείας δεν λειτουργούν σε αυτή τη σελίδα μέσα στην εφαρμογή. Συνδεθείτε με κωδικό ή αφήστε την εφαρμογή να περνά από τον διακομιστή μεσολάβησης.', 'native.login.failed': 'Η σύνδεση δεν ολοκληρώθηκε. Δοκιμάστε ξανά.', 'native.handoff.title': 'Συνέχεια στην εφαρμογή', 'native.handoff.hint': 'Έχετε συνδεθεί ως {name}. Ανοίξτε την εφαρμογή TREK για να ολοκληρώσετε εκεί τη σύνδεση.', diff --git a/shared/src/i18n/he/native.ts b/shared/src/i18n/he/native.ts index 8563522dd..9d64a8d16 100644 --- a/shared/src/i18n/he/native.ts +++ b/shared/src/i18n/he/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK לא הצליח להגיע אל {server}. בדקו את החיבור ונסו שוב.', 'native.offline.retry': 'נסו שוב', 'native.changeServer': 'החלפת שרת', + 'native.signInBar.hint': + 'מפתחות גישה ומפתחות אבטחה לא עובדים בדף הזה באפליקציה. התחבר עם סיסמה, או אפשר לאפליקציה לעבור דרך ה־proxy שלך.', 'native.login.failed': 'ההתחברות נכשלה. אנא נסו שוב.', 'native.handoff.title': 'המשך באפליקציה', 'native.handoff.hint': 'אתם מחוברים בתור {name}. פתחו את אפליקציית TREK כדי להשלים שם את ההתחברות.', diff --git a/shared/src/i18n/hu/native.ts b/shared/src/i18n/hu/native.ts index 5743b1d32..ac972e9af 100644 --- a/shared/src/i18n/hu/native.ts +++ b/shared/src/i18n/hu/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'A TREK nem érte el ezt: {server}. Ellenőrizd a kapcsolatot, és próbáld újra.', 'native.offline.retry': 'Újra', 'native.changeServer': 'Szerver módosítása', + 'native.signInBar.hint': + 'A jelkulcsok és a biztonsági kulcsok ezen az oldalon nem működnek az alkalmazásban. Jelentkezz be jelszóval, vagy engedd át az alkalmazást a proxyn.', 'native.login.failed': 'A bejelentkezés nem sikerült. Próbáld újra.', 'native.handoff.title': 'Folytatás az alkalmazásban', 'native.handoff.hint': diff --git a/shared/src/i18n/id/native.ts b/shared/src/i18n/id/native.ts index d29a1b2f0..b58ddc852 100644 --- a/shared/src/i18n/id/native.ts +++ b/shared/src/i18n/id/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK tidak dapat menjangkau {server}. Periksa koneksimu dan coba lagi.', 'native.offline.retry': 'Coba lagi', 'native.changeServer': 'Ganti server', + 'native.signInBar.hint': + 'Passkey dan kunci keamanan tidak berfungsi di halaman ini dalam aplikasi. Masuk dengan kata sandi, atau izinkan aplikasi melewati proxy Anda.', 'native.login.failed': 'Gagal masuk. Silakan coba lagi.', 'native.handoff.title': 'Lanjutkan di aplikasi', 'native.handoff.hint': 'Kamu masuk sebagai {name}. Buka aplikasi TREK untuk menyelesaikan proses masuk di sana.', diff --git a/shared/src/i18n/it/native.ts b/shared/src/i18n/it/native.ts index 4a2709c25..b8d0f313c 100644 --- a/shared/src/i18n/it/native.ts +++ b/shared/src/i18n/it/native.ts @@ -20,6 +20,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK non è riuscito a raggiungere {server}. Controlla la connessione e riprova.', 'native.offline.retry': 'Riprova', 'native.changeServer': 'Cambia server', + 'native.signInBar.hint': + 'Le passkey e le chiavi di sicurezza non funzionano su questa pagina nell’app. Accedi con una password o lascia passare l’app dal tuo proxy.', 'native.login.failed': 'L’accesso non è riuscito. Riprova.', 'native.handoff.title': 'Continua nell’app', 'native.handoff.hint': 'Hai effettuato l’accesso come {name}. Apri l’app TREK per completare lì l’accesso.', diff --git a/shared/src/i18n/ja/native.ts b/shared/src/i18n/ja/native.ts index 5e68226fa..ea86ceec2 100644 --- a/shared/src/i18n/ja/native.ts +++ b/shared/src/i18n/ja/native.ts @@ -17,6 +17,7 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK は {server} に接続できませんでした。接続を確認して、もう一度お試しください。', 'native.offline.retry': '再試行', 'native.changeServer': 'サーバーを変更', + 'native.signInBar.hint': 'このページではアプリ内でパスキーとセキュリティキーを使えません。パスワードでサインインするか、プロキシでアプリを通過させてください。', 'native.login.failed': 'ログインできませんでした。もう一度お試しください。', 'native.handoff.title': 'アプリで続行', 'native.handoff.hint': '{name} としてログインしています。TREK アプリを開いて、そちらでログインを完了してください。', diff --git a/shared/src/i18n/ko/native.ts b/shared/src/i18n/ko/native.ts index c21776ead..6a1dce84f 100644 --- a/shared/src/i18n/ko/native.ts +++ b/shared/src/i18n/ko/native.ts @@ -17,6 +17,7 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK이 {server}에 연결하지 못했습니다. 연결 상태를 확인한 후 다시 시도하세요.', 'native.offline.retry': '다시 시도', 'native.changeServer': '서버 변경', + 'native.signInBar.hint': '이 페이지에서는 앱 안에서 패스키와 보안 키를 사용할 수 없습니다. 비밀번호로 로그인하거나 프록시에서 앱을 통과시키세요.', 'native.login.failed': '로그인하지 못했습니다. 다시 시도하세요.', 'native.handoff.title': '앱에서 계속', 'native.handoff.hint': '{name}(으)로 로그인되어 있습니다. TREK 앱을 열어 로그인을 완료하세요.', diff --git a/shared/src/i18n/nl/native.ts b/shared/src/i18n/nl/native.ts index f43fa375d..44f227670 100644 --- a/shared/src/i18n/nl/native.ts +++ b/shared/src/i18n/nl/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK kon {server} niet bereiken. Controleer je verbinding en probeer het opnieuw.', 'native.offline.retry': 'Opnieuw proberen', 'native.changeServer': 'Server wijzigen', + 'native.signInBar.hint': + 'Passkeys en beveiligingssleutels werken op deze pagina niet in de app. Meld je aan met een wachtwoord of laat de app door je proxy.', 'native.login.failed': 'Inloggen is niet gelukt. Probeer het opnieuw.', 'native.handoff.title': 'Verder in de app', 'native.handoff.hint': 'Je bent ingelogd als {name}. Open de TREK-app om daar het inloggen af te ronden.', diff --git a/shared/src/i18n/pl/native.ts b/shared/src/i18n/pl/native.ts index 209289017..2ed82e69d 100644 --- a/shared/src/i18n/pl/native.ts +++ b/shared/src/i18n/pl/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK nie mógł połączyć się z {server}. Sprawdź połączenie i spróbuj ponownie.', 'native.offline.retry': 'Spróbuj ponownie', 'native.changeServer': 'Zmień serwer', + 'native.signInBar.hint': + 'Klucze dostępu i klucze bezpieczeństwa nie działają na tej stronie w aplikacji. Zaloguj się hasłem albo przepuść aplikację przez swój serwer proxy.', 'native.login.failed': 'Logowanie się nie powiodło. Spróbuj ponownie.', 'native.handoff.title': 'Kontynuuj w aplikacji', 'native.handoff.hint': 'Zalogowano jako {name}. Otwórz aplikację TREK, aby tam dokończyć logowanie.', diff --git a/shared/src/i18n/ru/native.ts b/shared/src/i18n/ru/native.ts index fea20bf5d..6e96d2516 100644 --- a/shared/src/i18n/ru/native.ts +++ b/shared/src/i18n/ru/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK не удалось подключиться к {server}. Проверьте подключение и повторите попытку.', 'native.offline.retry': 'Повторить', 'native.changeServer': 'Сменить сервер', + 'native.signInBar.hint': + 'Ключи доступа и ключи безопасности не работают на этой странице в приложении. Войдите с паролем или пропустите приложение через свой прокси.', 'native.login.failed': 'Не удалось войти. Попробуйте ещё раз.', 'native.handoff.title': 'Продолжить в приложении', 'native.handoff.hint': 'Вы вошли как {name}. Откройте приложение TREK, чтобы завершить вход в нём.', diff --git a/shared/src/i18n/sk/native.ts b/shared/src/i18n/sk/native.ts index aa4718017..b3c115aef 100644 --- a/shared/src/i18n/sk/native.ts +++ b/shared/src/i18n/sk/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK sa nepodarilo spojiť so serverom {server}. Skontrolujte pripojenie a skúste to znova.', 'native.offline.retry': 'Skúsiť znova', 'native.changeServer': 'Zmeniť server', + 'native.signInBar.hint': + 'Prístupové kľúče a bezpečnostné kľúče na tejto stránke v aplikácii nefungujú. Prihláste sa heslom alebo aplikáciu prepustite cez svoj proxy.', 'native.login.failed': 'Prihlásenie sa nepodarilo. Skúste to, prosím, znova.', 'native.handoff.title': 'Pokračovať v aplikácii', 'native.handoff.hint': 'Ste prihlásení ako {name}. Otvorte aplikáciu TREK a dokončite prihlásenie v nej.', diff --git a/shared/src/i18n/sv/native.ts b/shared/src/i18n/sv/native.ts index 640f61795..e557ad327 100644 --- a/shared/src/i18n/sv/native.ts +++ b/shared/src/i18n/sv/native.ts @@ -20,6 +20,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK kunde inte nå {server}. Kontrollera din anslutning och försök igen.', 'native.offline.retry': 'Försök igen', 'native.changeServer': 'Byt server', + 'native.signInBar.hint': + 'Nyckelpass och säkerhetsnycklar fungerar inte på den här sidan i appen. Logga in med lösenord eller släpp igenom appen i din proxy.', 'native.login.failed': 'Inloggningen misslyckades. Försök igen.', 'native.handoff.title': 'Fortsätt i appen', 'native.handoff.hint': 'Du är inloggad som {name}. Öppna TREK-appen för att slutföra inloggningen där.', diff --git a/shared/src/i18n/th/native.ts b/shared/src/i18n/th/native.ts index fe4378e22..61b60c93d 100644 --- a/shared/src/i18n/th/native.ts +++ b/shared/src/i18n/th/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK เข้าถึง {server} ไม่ได้ โปรดตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง', 'native.offline.retry': 'ลองอีกครั้ง', 'native.changeServer': 'เปลี่ยนเซิร์ฟเวอร์', + 'native.signInBar.hint': + 'พาสคีย์และคีย์ความปลอดภัยใช้ไม่ได้บนหน้านี้ในแอป ลงชื่อเข้าใช้ด้วยรหัสผ่าน หรืออนุญาตให้แอปผ่านพร็อกซีของคุณ', 'native.login.failed': 'เข้าสู่ระบบไม่สำเร็จ โปรดลองอีกครั้ง', 'native.handoff.title': 'ดำเนินการต่อในแอป', 'native.handoff.hint': 'คุณเข้าสู่ระบบในชื่อ {name} เปิดแอป TREK เพื่อเข้าสู่ระบบให้เสร็จสิ้นที่นั่น', diff --git a/shared/src/i18n/tr/native.ts b/shared/src/i18n/tr/native.ts index 13b9c5c1e..e3c2ee2a0 100644 --- a/shared/src/i18n/tr/native.ts +++ b/shared/src/i18n/tr/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK, {server} adresine ulaşamadı. Bağlantını kontrol edip tekrar dene.', 'native.offline.retry': 'Tekrar dene', 'native.changeServer': 'Sunucuyu değiştir', + 'native.signInBar.hint': + 'Geçiş anahtarları ve güvenlik anahtarları uygulamada bu sayfada çalışmaz. Parolayla giriş yapın veya uygulamayı proxy’nizden geçirin.', 'native.login.failed': 'Giriş yapılamadı. Lütfen tekrar dene.', 'native.handoff.title': 'Uygulamada devam et', 'native.handoff.hint': '{name} olarak giriş yaptın. Girişi tamamlamak için TREK uygulamasını aç.', diff --git a/shared/src/i18n/uk/native.ts b/shared/src/i18n/uk/native.ts index c62e1dd5c..2bb37cc57 100644 --- a/shared/src/i18n/uk/native.ts +++ b/shared/src/i18n/uk/native.ts @@ -19,6 +19,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK не вдалося підключитися до {server}. Перевірте з’єднання та спробуйте ще раз.', 'native.offline.retry': 'Спробувати ще раз', 'native.changeServer': 'Змінити сервер', + 'native.signInBar.hint': + 'Ключі доступу та ключі безпеки не працюють на цій сторінці в застосунку. Увійдіть із паролем або пропустіть застосунок через свій проксі.', 'native.login.failed': 'Не вдалося увійти. Спробуйте ще раз.', 'native.handoff.title': 'Продовжити в застосунку', 'native.handoff.hint': 'Ви увійшли як {name}. Відкрийте застосунок TREK, щоб завершити вхід у ньому.', diff --git a/shared/src/i18n/vi/native.ts b/shared/src/i18n/vi/native.ts index 6f82db662..017596a3a 100644 --- a/shared/src/i18n/vi/native.ts +++ b/shared/src/i18n/vi/native.ts @@ -20,6 +20,8 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK không thể kết nối tới {server}. Hãy kiểm tra kết nối và thử lại.', 'native.offline.retry': 'Thử lại', 'native.changeServer': 'Đổi máy chủ', + 'native.signInBar.hint': + 'Khóa truy cập và khóa bảo mật không hoạt động trên trang này trong ứng dụng. Hãy đăng nhập bằng mật khẩu hoặc cho ứng dụng đi qua proxy của bạn.', 'native.login.failed': 'Đăng nhập không thành công. Vui lòng thử lại.', 'native.handoff.title': 'Tiếp tục trong ứng dụng', 'native.handoff.hint': 'Bạn đã đăng nhập với tên {name}. Mở ứng dụng TREK để hoàn tất đăng nhập ở đó.', diff --git a/shared/src/i18n/zh-TW/native.ts b/shared/src/i18n/zh-TW/native.ts index 53143f6ce..6c082e1a2 100644 --- a/shared/src/i18n/zh-TW/native.ts +++ b/shared/src/i18n/zh-TW/native.ts @@ -17,6 +17,7 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK 無法連線到 {server}。請檢查網路連線後再試一次。', 'native.offline.retry': '再試一次', 'native.changeServer': '更換伺服器', + 'native.signInBar.hint': '在 App 中,此頁面無法使用通行密鑰和安全金鑰。請使用密碼登入,或讓 App 略過你的代理伺服器。', 'native.login.failed': '登入未成功,請再試一次。', 'native.handoff.title': '在 App 中繼續', 'native.handoff.hint': '你已以 {name} 身分登入。請開啟 TREK App,在那裡完成登入。', diff --git a/shared/src/i18n/zh/native.ts b/shared/src/i18n/zh/native.ts index c12419514..bf4c43a49 100644 --- a/shared/src/i18n/zh/native.ts +++ b/shared/src/i18n/zh/native.ts @@ -17,6 +17,7 @@ const native: TranslationStrings = { 'native.offline.hint': 'TREK 无法连接到 {server}。请检查网络连接后重试。', 'native.offline.retry': '重试', 'native.changeServer': '更换服务器', + 'native.signInBar.hint': '在应用中,此页面无法使用通行密钥和安全密钥。请使用密码登录,或让应用绕过你的代理。', 'native.login.failed': '登录未成功,请重试。', 'native.handoff.title': '在应用中继续', 'native.handoff.hint': '你已以 {name} 身份登录。请打开 TREK 应用,在那里完成登录。',